Skip to content
MusicStation – Home

iPad remote

Platform: the remote app of this release, MusicStationRemote, is built for the iPad. The remote UI and the remote library behind it are an iOS package that is meant to run on the iPhone as well, so a layout for the iPhone follows in a later release; until then the app is installed on an iPad.

The MusicStation remote is the iPad app that drives the transport and the clips of a running MusicStation over the local network. It is a normal client of the same control protocol the interface uses: the bridge (msh-bridge) runs next to the engine on the Mac, connects to it on the loopback address, and is the only part of MusicStation that is visible on the LAN. The remote never talks to the engine directly and never touches the audio path.

What the remote needs:

  • MusicStation and the bridge running on the Mac, both in the same network as the iPad.
  • The pairing token of the bridge - a shared secret, shown by the bridge itself (see Pairing token).
  • The bridge's address. The remote finds bridges by itself over Bonjour (_msh-remote._tcp, port 47900) and shows them by the host name they advertise (see Bridges on the network); the WebSocket it opens is ws://<host>:47900/ws.

The remote window is built from three parts: the header with the bridge it talks to, the connection state, Disconnect and the language menu; the connect screen with the host list of the discovered bridges, the address field, the pairing token, the language picker and Connect; and, once the connection is paired, the session grid of the running MusicStation together with the transport controls described in the Transport chapter. The remote's own texts are English and German and follow the language of the device (see Language).

Bridges on the network

The list at the top of the connect screen, headed Bridges on the network: every MusicStation bridge that advertises itself in your network over Bonjour (_msh-remote._tcp, port 47900). Each row shows the host name the bridge advertises, its address as host:port, the protocol version it speaks and, when it requires one, the badge Pairing required. The remote searches while the connect screen is open and stops the search when the app goes to the background; it starts again as soon as the app is in the foreground.

  • Tap a row to select that bridge; the selected row wears the accent colour. Tapping the selected row again clears the selection, so the address you typed below is used again (see Bridge address).
  • While the list is empty the remote says why: Searching for bridges on the network … while the search runs, No bridge found yet. when nothing answered, and a message that the search failed when the browse itself reported an error - the usual cause is that the app has no permission for the local network. Check that the Mac and the iPad are in the same network and that the bridge runs on the Mac.
  • A bridge that is found is not paired yet: the pairing token is not part of the advertisement, so you have to enter it (see Pairing token).

The list is a display and a selection, nothing else: the connection is opened by Connect (see Connect).

Bridge address

The text field under the host list (Bridge address): the bridge to talk to when the discovered rows are not what you want. It accepts the name of the Mac as it is known in the network, an IP address, or a complete WebSocket address:

  • studio-mac - a host name; the remote adds the default port 47900 and the path /ws.
  • studio-mac:47900 or 192.168.1.20:47900 - host and port.
  • ws://192.168.1.20:47900/ws - a complete address; a port and a path you give are kept, a missing one is completed with the defaults.
  • [fe80::1] - an IPv6 address in brackets, again completed with port 47900 and /ws.

The hint in the field shows the two short forms (studio-mac, 192.168.1.20). The field never auto-capitalises and never autocorrects, because a host name and an address are case-sensitive; on the iPad the keyboard starts in URL mode. A bridge selected in the host list wins over what you type here, so the field is ignored while a row is selected; clear the selection to use the typed address. If the selected bridge can no longer be resolved, the Connect control stays disabled until you pick another bridge or clear the selection. The address is resolved when you press Connect.

Pairing token

The field for the bridge's pairing token (German: Kopplungsschlüssel), the shared secret that authorises a remote to command this MusicStation. A token is exactly 32 lower-case hexadecimal characters, for example 0123456789abcdef0123456789abcdef; the bridge generates one on its first start and keeps it in a file that only your user account can read (on macOS ~/Library/Application Support/musicstation/bridge-token, file mode 0600).

Where to read it:

  • The bridge prints the token once when it starts: bridge token: <token>.
  • msh-bridge --print-token prints the stored token again and exits (it neither creates nor changes it).
  • msh-bridge --rotate-token writes a fresh random token, keeps the file private, prints it and exits. Connections that are already paired stay connected; every later pairing needs the new token.

Copy the token into the field of the remote and pair with it (see Connect). The token is compared per pairing attempt and never leaves your network: it is not part of the Bonjour advertisement, so a remote that finds a bridge still has to be given the token by you. The field is only needed for pairing, and every new connection pairs again, so the token has to be the one the bridge currently accepts. A rejected token is answered with auth_failed (and the connection is closed with 4003); the remote library reports that failure so the app can ask you for the current token.

Language

The language of the remote app: System language (follow the iPad), English or Deutsch. The picker sits on the connect screen and in the header of the app, so the language can be changed before and after pairing; both places are the same control. A chosen language is stored on the device and used from the next start on as well, and System language removes that override again.

The setting only changes the texts of the remote app: the desktop application, its own Language setting and the content of the session are not affected. Messages from the engine and the bridge are translated with the same setting, because the engine sends stable error codes and never prose.

Connect

Connects the remote to the selected bridge and pairs it. Pick a host from the discovered list (or the one you typed) and press Connect: the remote opens ws://<host>:47900/ws and sends the pairing command with the token from the field above. When the bridge answers, the remote is paired and the session grid of the running MusicStation appears.

What happens on the way:

  • Directly after the connection is opened, the bridge reports that this connection is not paired yet; the remote shows Connecting ….
  • On a successful pairing the bridge acknowledges the token and sends its cached transport, and the remote shows Connected. From then on the transport and the session grid can be used.
  • A dropped connection is retried by itself, with an increasing delay (starting at about 250 ms, doubling up to 30 s, each delay varied by up to 20 percent). A connection that lasted at least 5 s resets the delay to its shortest value.
  • Disconnected means there is currently no connection; the remote keeps trying on its own.
  • Incompatible version means the bridge speaks a different protocol version. Retrying cannot help, so the remote stops there - update MusicStation and the app to the same release.
  • Connection failed means the bridge ended the connection with an error you have to act on. The most common case is a rejected token (auth_failed, close code 4003), which is not retried: check the token the bridge prints and pair again with it. The message of the failure is translated into the language of the remote.

Commands sent before the pairing succeeded are answered with not_paired and do not change anything; the connection stays open, so pairing can still succeed.

Dismissing a message

The message of the last failed attempt. It appears directly under the Connect control of the connect screen and, once paired, as a banner above the session grid. Its text is the translated message of the error the bridge or the engine reported - a rejected pairing token, a command that was dropped because too many ran at once, a connection that could not be established.

Tap the message to close it (the hint beside it says Dismiss message or Dismiss error). Closing is only closing: nothing is retried and no state is changed by it. If a pairing failed, the connection stays unpaired until you press Connect again; if a command was rejected, the grid has already taken that gesture back and the next snapshot shows the engine's state. The next failure shows a new message.

Disconnect

Closes the connection to the bridge and stops the automatic reconnect. Press Disconnect when you hand the iPad away or want to switch to another MusicStation; the host list stays available, so you can pair with a bridge again at any time (see Pairing token).

The disconnect is a normal close of the connection (close code 1001). Nothing on the Mac changes because of it: playback continues, and other remotes or an OSC controller stay unaffected. The remote forgets the transport state it was showing, so the next connect starts from the engine's current snapshot.

Remote transport

Once connected, the remote controls the same transport the desktop does; the controls are documented once in the Transport chapter, and the remote shows them in its own layout:

  • Play and Stop - identical to the desktop buttons, including the latched highlighting (Play, Stop).
  • Tempo - the same range of 20 to 999 BPM and the same default of 120.00 BPM; a value outside the range is clamped before it is sent (Tempo).
  • Position - the same bars.beats.ticks readout (4/4, 480 ticks per beat, project start 1.1.1), moved between the engine's snapshots with a 20 Hz timer (Position). It is a readout: jumping to a bar is the desktop shortcut Enter and has no remote equivalent yet.
  • Test tone - the same 440 Hz tone at -20 dBFS on the master output, switchable from the remote (Test tone).

A command changes the display at once and is sent in the background; the engine stays the authority. If the engine rejects a command - for example because another client changed the value - the remote rolls its display back to the engine's answer and shows the translated error message instead.

Session grid

Once the bridge is paired the app shows the session grid of the running MusicStation - the same grid the desktop shows, mirrored live. The remote receives the whole session state as a snapshot (at most 30 times a second) and draws exactly that: the grid is the engine's state, not a copy the remote keeps for itself. In this version the grid is fixed to 16 tracks by 16 scenes; whatever size the snapshot reports is what is drawn.

  • The pinned track row at the top and the pinned scene column on the left stay in view while the matrix scrolls under them. Track and scene names never travel between engine and remote, so the headings are the numbers (Track 1, Scene 1).
  • Every cell is a clip slot of one track and scene and at least 56 points large - well beyond the 44 points a touch target should have.
  • Tap a slot that is stopped to launch its clip: the remote sends clip.launch, the slot is armed and starts at the session's quantisation boundary (see Quantisation).
  • Tap a slot that plays or waits to stop it: the remote sends clip.stop. A waiting slot is a launch that has not happened yet, so a tap cancels it; a playing clip fades out over 5 ms at its next boundary. This is where the remote differs from the desktop, where a click on the slot starts the clip again (see Clip slot).
  • Tap the button of a scene in the scene column (the play symbol with the scene number) to launch the whole row: every slot of that scene that holds a clip is armed at the same boundary (see Scene launch).
  • The quantisation picker in the header of the grid carries the same seven values as the desktop and changes the launch/stop quantisation of the session, so it applies to every client (see Quantisation).
  • An empty slot has nothing to launch and a tap does nothing; the same holds for a cell outside the grid the snapshot reported.

The status mark of a cell shows what its slot is doing: a filled dot while the clip plays, and a blinking dot while the slot waits for its quantisation boundary (the dot is visible for 250 ms and hidden for 250 ms, so the blink is the remote's way of saying that the launch is still on its way). An empty or stopped slot carries no mark at all. The mark is never a control of its own: it is part of the cell, and the whole cell is the touch target.

Every gesture is a command, so the grid reacts at once and the engine's answer decides: a slot that was armed is marked as waiting immediately, a slot that is playing and is armed again keeps playing until its boundary, and a rejected command takes the change back and shows the translated message (see Dismissing a message).

What the remote grid deliberately does not do:

  • No file loading. A clip is loaded with a path on the machine that runs the engine, so a remote cannot name a file; dragging a file onto the grid is a desktop gesture (see Files: drag and drop).
  • No loop switch. The loop flag of a clip is set on the desktop; the remote neither shows nor changes it (see Loop).

Limits of the remote

  • The remote commands the transport and the clips in this version: it starts and stops clips, starts scenes and changes the launch quantisation. Everything further - recording, the arrangement, the mixer, plugins, loading files and the loop flag of a clip - is not part of the remote surface yet; those chapters of this manual describe the desktop application.
  • The bridge throttles what it sends: the transport state at most 30 times per second per client and the moving playhead at most 20 times per second. A remote that cannot keep up loses whole snapshots, never a part of one, so it always shows a consistent state.
  • The bridge limits how many commands one client may send. A command over that budget is dropped and answered with rate_limited; a client that keeps exceeding it is disconnected and has to wait at least 30 s before reconnecting. The remote shows the message and pauses, then continues on its own.
  • Up to 5 failed pairing attempts per client address within one minute are tolerated; after that the bridge closes the connection with the rate-limit close code. Wait a minute, check the token and try again.
  • The remote has one setting of its own, its Language; the language and the appearance of the desktop application are not affected by it (see Settings).
  • The remote app runs on an iPad in this version (see the note at the top of this chapter), and it has no About and "What's new" screens of its own yet: the release-note history of the running version is read in the desktop application (see About and What's New).

Workflow: pair the iPad with the Mac

  1. Start MusicStation and the bridge on the Mac. Note the line bridge token: <token> the bridge prints when it starts (see Pairing token).
  2. Open the remote app on the iPad. It searches the network and lists the bridges it finds by their host names (see Bridges on the network). If your Mac does not appear, type its address into the Bridge address field.
  3. Pick your Mac from the list and copy the token into the Pairing token field (see Pairing token).
  4. Press Connect (see Connect). The status goes to Connecting … and then to Connected; the session grid of the running MusicStation appears.
  5. Press Play to start playback on the Mac and Stop to stop it; set the Tempo and switch the Test tone just as on the desktop (see Transport).
  6. Press Disconnect when you are done (see Disconnect) - or simply leave the app; the bridge drops the connection when the app closes.

Workflow: launch a scene from the iPad

  1. Pair the app with the Mac as described above. The session grid of the running MusicStation appears (see Session grid).
  2. Check the quantisation in the header of the grid and pick the value the song expects - a fresh session reports Off, most songs are played with 1 bar (see Quantisation).
  3. Tap the play symbol of the scene you want to start, for example Scene 1. The silent slots of that row start to blink: they are armed and wait for their boundary.
  4. At the boundary the clips start together; the blinking dots turn into filled dots while the clips play. A clip of the same track that was playing before stops at the same boundary (see Session grid).
  5. Tap a playing slot to stop it at the next boundary, or tap Stop in the transport to stop the transport and all sounding clips together.
  6. A clip that is not loaded yet has to be put into its slot on the Mac first - the remote cannot load files (see Files: drag and drop).