2 * Control JACK transport using MMC (MIDI)
4 * Copyright (c) 2006,2007,2008,2010 Nedko Arnaudov <nedko@arnaudov.name>
5 * Copyright (c) 2008 Alex Montgomery <apmontgomery@gmail.com>
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free Software
9 * Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 int g_quit
= 0; // a flag which will be set by our signal handler when it's time to exit
24 int g_isListening
= 0;
26 // globals used only here in common.c
27 snd_seq_t
* g_seq_ptr
= NULL
;
28 jack_client_t
* g_jack_client
= NULL
;
29 struct pollfd
* g_pollDescriptor
= NULL
;
30 int g_numDescriptors
= 0;
33 lash_client_t
* g_lashc
= NULL
;
35 void init_lash(int argc
, char* argv
[])
38 g_lashc
= lash_init( lash_extract_args(&argc
, &argv
), "jackctlmmc", 0, LASH_PROTOCOL_VERSION
);
42 printMMCMessage("Failed to connect to LASH. Session management will not occur.\n");
45 // register JACK and ALSA clients with lash
46 lash_alsa_client_id(g_lashc
, snd_seq_client_id(g_seq_ptr
));
47 lash_jack_client_name(g_lashc
, "jackctlmmc");
50 void process_lash_event(lash_event_t
* event_ptr
)
52 enum LASH_Event_Type type
;
55 type
= lash_event_get_type(event_ptr
);
56 str
= lash_event_get_string(event_ptr
);
61 printMMCMessage("LASH_Quit received.\n");
66 case LASH_Restore_File
:
67 case LASH_Save_Data_Set
:
69 printMMCMessage("LASH Event. Type = %u, string = \"%s\"\n",
71 (str
== NULL
)?"":str
);
75 void process_lash_config(lash_config_t
* config_ptr
)
81 key
= lash_config_get_key(config_ptr
);
82 data
= lash_config_get_value(config_ptr
);
83 data_size
= lash_config_get_value_size(config_ptr
);
85 printMMCMessage("LASH Config. Key = \"%s\"\n", key
);
87 #endif // LASH_SUPPORT
91 jack_port_t
* g_jackMidiIn
= NULL
;
93 int jack_process_cb(jack_nframes_t numFrames
, void* cbContext
)
97 uint8_t* inBuffer
= 0;
98 int numMidiEvents
= 0;
99 jack_midi_event_t currEvent
;
101 MidiSettings
* settings
= (MidiSettings
*) cbContext
;
103 inBuffer
= jack_port_get_buffer(g_jackMidiIn
, numFrames
);
104 numMidiEvents
= jack_midi_get_event_count(inBuffer
);
106 for (midiIndex
= 0; midiIndex
< numMidiEvents
; ++midiIndex
)
108 jack_midi_event_get(&currEvent
, inBuffer
, midiIndex
);
109 handle_midi(currEvent
.buffer
, settings
);
116 int init_jack_midi(MidiSettings
* settings
)
120 errorCode
= jack_set_process_callback(g_jack_client
, &jack_process_cb
, settings
);
121 g_jackMidiIn
= jack_port_register(g_jack_client
, "in", JACK_DEFAULT_MIDI_TYPE
, JackPortIsInput
| JackPortIsTerminal
, 1024);
122 return (errorCode
== 0 && g_jackMidiIn
!= 0);
124 #endif // JACK_MIDI_SUPPORT
126 int init_alsa_sequencer(const char* appName
)
128 snd_seq_port_info_t
* seq_port_info
= NULL
;
131 /* ALSA sequencer initialisation */
132 ret
= snd_seq_open( &g_seq_ptr
, "default", SND_SEQ_OPEN_INPUT
, 0);
136 // setup alsa sequencer port
137 snd_seq_port_info_alloca(&seq_port_info
);
138 snd_seq_port_info_set_capability(seq_port_info
, SND_SEQ_PORT_CAP_WRITE
| SND_SEQ_PORT_CAP_SUBS_WRITE
);
139 snd_seq_port_info_set_type( seq_port_info
, SND_SEQ_PORT_TYPE_APPLICATION
);
140 snd_seq_port_info_set_midi_channels(seq_port_info
, 16);
141 snd_seq_port_info_set_port_specified(seq_port_info
, 1);
142 snd_seq_port_info_set_name(seq_port_info
, "midi in");
143 snd_seq_port_info_set_port(seq_port_info
, 0);
145 ret
= snd_seq_create_port(g_seq_ptr
, seq_port_info
);
148 printMMCMessage("Error with alsa sequencer initialization, %s\n", snd_strerror(ret
));
149 else // all is well, register the sequencer with whatever name was passed in
151 snd_seq_set_client_name(g_seq_ptr
, appName
);
153 g_numDescriptors
= snd_seq_poll_descriptors_count(g_seq_ptr
, POLLIN
);
154 g_pollDescriptor
= (struct pollfd
*)malloc(g_numDescriptors
* sizeof(struct pollfd
));
155 snd_seq_poll_descriptors(g_seq_ptr
, g_pollDescriptor
, g_numDescriptors
, POLLIN
);
161 int init_jack(const char* appName
)
163 jack_status_t status
;
164 g_jack_client
= jack_client_open(appName
, JackNoStartServer
, &status
);
166 if (!g_jack_client
) // something went wrong
167 return -1; // todo: check status to see exactly what happened
174 /* tell jack that we are ready to do our thing */
175 return jack_activate(g_jack_client
);
178 void handle_midi(uint8_t* midiBuff
, MidiSettings
* settings
)
180 if (midiBuff
[0] == 0xF0 && midiBuff
[1] == 0x7F && midiBuff
[3] == 0x06)
182 const uint8_t messageDeviceID
= midiBuff
[2];
183 if (messageDeviceID
== settings
->deviceID
)
185 const char mmcCommand
= midiBuff
[4];
189 if (settings
->verbose
)
190 printMMCMessage("MMC Stop -> JACK transport stop\n");
191 jack_transport_stop(g_jack_client
);
194 case 3: /* deferred play */
195 if (settings
->verbose
)
196 printMMCMessage("MMC Play -> JACK transport start\n");
197 jack_transport_start(g_jack_client
);
200 if (settings
->verbose
)
201 printMMCMessage("MMC Fast Forward -> Ignored\n");
204 if (settings
->verbose
)
205 printMMCMessage("MMC Play -> JACK transport locate to 0\n");
206 jack_transport_locate(g_jack_client
, 0);
209 if (settings
->verbose
)
210 printMMCMessage("MMC Record Strobe (Punch In) -> Ignored\n");
213 if (settings
->verbose
)
214 printMMCMessage("MMC Record Exit (Punch out) -> Ignored\n");
217 if (settings
->verbose
)
218 printMMCMessage("MMC Record Ready (Record Pause) -> Ignored\n");
221 if (settings
->verbose
)
222 printMMCMessage("MMC Pause -> JACK transport stop\n");
223 jack_transport_stop(g_jack_client
);
225 case 0x44: /* goto */
226 if (midiBuff
[5] == 0x06 && midiBuff
[6] == 0x01)
228 // some devices call hour 0 "60". Mask off the upper bits
229 uint8_t hour
= (midiBuff
[7] & 0x1f);
230 uint8_t minute
= midiBuff
[8];
231 uint8_t second
= midiBuff
[9];
232 uint8_t frame
= midiBuff
[10];
233 uint8_t subframe
= midiBuff
[11]; // percentage of a frame: 0 - 99
234 jack_position_t jack_pos
;
236 // get Jack's current framerate and position
237 jack_transport_query(g_jack_client
, &jack_pos
);
238 uint32_t jack_frame_rate
= jack_pos
.frame_rate
;
239 uint32_t jack_time_ms
= (jack_pos
.frame
* 1000) / jack_frame_rate
;
240 uint32_t device_time_ms
= ((subframe
* 10) / settings
->frameRate
+ // subframe == 1/100th of a frame
241 (frame
* 1000) / settings
->frameRate
+
243 (minute
* 60 * 1000) +
244 (hour
* 60 * 60 * 1000));
246 // difference in milliseconds from JACK's reported transport to the MIDI goto's time
247 uint32_t jitter
= (device_time_ms
> jack_time_ms
? (device_time_ms
- jack_time_ms
) : (jack_time_ms
- device_time_ms
));
249 if (settings
->verbose
)
250 printMMCMessage("MMC locate to hour: %d, minute: %d, second: %d, frame: %d, subframe: %d\n",
257 // check if the JACK clock is far enough away from the MMC time to care
258 if (jitter
> settings
->jitterTolerance
)
259 { // it is, change it.
260 jack_pos
.valid
= 0; // only the frame number will be valid since that's all we're changing
262 // need a placeholder so that we can keep precision while not letting the integer overflow
263 uint64_t placeholder
= (uint64_t)device_time_ms
* jack_frame_rate
/ 1000;
264 jack_pos
.frame
= placeholder
;
266 jack_transport_reposition(g_jack_client
, &jack_pos
);
268 else if (settings
->verbose
)
269 printMMCMessage("New position is %d ms away and within jitter range, ignoring.\n", jitter
);
272 case 0x48: /* step */
275 jack_position_t jack_pos
;
284 if (settings
->verbose
)
285 printMMCMessage("MMC Step %d\n", step
);
287 // get Jack's current framerate and position
288 jack_transport_query(g_jack_client
, &jack_pos
);
290 jack_pos
.valid
= 0; // only the frame number will be valid since that's all we're changing
292 jack_pos
.frame
+= (int64_t)step
* (int64_t)jack_pos
.frame_rate
/ (int64_t)10;
294 jack_transport_reposition(g_jack_client
, &jack_pos
);
298 if (settings
->verbose
)
299 printMMCMessage("unsupported MMC command: 0x%x\n", mmcCommand
);
302 else if (settings
->verbose
)
304 printMMCMessage("MMC command received for device ID %x while listening for commands from device ID %x. Have you specified the correct device ID to listen to?\n",
312 void poll_midi (MidiSettings
* settings
)
314 snd_seq_event_t
* seq_event_ptr
= NULL
;
316 if (poll(g_pollDescriptor
, g_numDescriptors
, 250) > 0 && snd_seq_event_input(g_seq_ptr
, &seq_event_ptr
) >= 0 && seq_event_ptr
->type
== SND_SEQ_EVENT_SYSEX
)
317 handle_midi((uint8_t *)seq_event_ptr
->data
.ext
.ptr
, settings
);
320 /* Process LASH events */
322 lash_event_t
* lash_event_ptr
= NULL
;
323 lash_config_t
* lash_config_ptr
= NULL
;
324 while ((lash_event_ptr
= lash_get_event(g_lashc
)) != NULL
)
326 process_lash_event(lash_event_ptr
);
327 lash_event_destroy(lash_event_ptr
);
330 /* Process LASH configs */
331 while ((lash_config_ptr
= lash_get_config(g_lashc
)) != NULL
)
333 process_lash_config(lash_config_ptr
);
334 lash_config_destroy(lash_config_ptr
);
340 void cleanup_globals()
346 ret
= snd_seq_close(g_seq_ptr
);
349 printMMCMessage("Cannot close sequncer, %s\n", snd_strerror(ret
));
353 if (g_pollDescriptor
)
354 free(g_pollDescriptor
);
358 #if JACK_MIDI_SUPPORT
359 jack_port_unregister(g_jack_client
, g_jackMidiIn
);
361 jack_deactivate(g_jack_client
);
362 jack_client_close(g_jack_client
);