added config.h.in so users don't have to rerun autoconf
[jackctlmmc.git] / common.c
blobebb0a8fa68a0de62ddce5fbdeff74c05b6abb090
1 /*
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
20 #include "common.h"
22 // common globals
23 int g_quit = 0; // a flag which will be set by our signal handler when it's time to exit
24 snd_seq_t* g_seq_ptr = NULL;
25 jack_client_t* g_jack_client = NULL;
26 int g_isListening = 0;
28 #if LASH_SUPPORT
29 lash_client_t* g_lashc = NULL;
31 void init_lash(int argc, char* argv[])
33 lash_event_t * lash_event_ptr = NULL;
35 /* LASH setup */
36 g_lashc = lash_init( lash_extract_args(&argc, &argv), "jackctlmmc", 0, LASH_PROTOCOL_VERSION);
38 if (g_lashc == NULL)
40 printMMCMessage("Failed to connect to LASH. Session management will not occur.\n");
42 else
44 lash_event_ptr = lash_event_new_with_type(LASH_Client_Name);
45 lash_event_set_string(lash_event_ptr, "jackctlmmc");
46 lash_send_event(g_lashc, lash_event_ptr);
49 // register JACK and ALSA clients with lash
50 lash_alsa_client_id(g_lashc, snd_seq_client_id(g_seq_ptr));
51 lash_jack_client_name(g_lashc, "jackctlmmc");
54 void process_lash_event(lash_event_t * event_ptr)
56 enum LASH_Event_Type type;
57 const char * str;
59 type = lash_event_get_type(event_ptr);
60 str = lash_event_get_string(event_ptr);
62 switch (type)
64 case LASH_Quit:
65 printMMCMessage("LASH_Quit received.\n");
66 g_lashc = NULL;
67 g_quit = 1;
68 break;
69 case LASH_Save_File:
70 case LASH_Restore_File:
71 case LASH_Save_Data_Set:
72 default:
73 printMMCMessage("LASH Event. Type = %u, string = \"%s\"\n",
74 (unsigned int)type,
75 (str == NULL)?"":str);
79 void process_lash_config(lash_config_t * config_ptr)
81 const char * key;
82 const void * data;
83 size_t data_size;
85 key = lash_config_get_key(config_ptr);
86 data = lash_config_get_value(config_ptr);
87 data_size = lash_config_get_value_size(config_ptr);
89 printMMCMessage("LASH Config. Key = \"%s\"\n", key);
91 #endif // LASH_SUPPORT
93 #if JACK_MIDI_SUPPORT
95 jack_port_t* g_jackMidiIn = NULL;
97 int jack_process_cb(jack_nframes_t numFrames, void* cbContext)
99 if (g_isListening)
101 uint8_t* inBuffer = 0;
102 int numMidiEvents = 0;
103 jack_midi_event_t currEvent;
104 int midiIndex = 0;
105 MidiSettings* settings = (MidiSettings*) cbContext;
107 inBuffer = jack_port_get_buffer(g_jackMidiIn, numFrames);
108 numMidiEvents = jack_midi_get_event_count(inBuffer);
110 for (midiIndex = 0; midiIndex < numMidiEvents; ++midiIndex)
112 jack_midi_event_get(&currEvent, inBuffer, midiIndex);
113 // printMMCMessage("0: %x 1: %x 2: %x 3: %x\n", currEvent.buffer[0], currEvent.buffer[1], currEvent.buffer[2], currEvent.buffer[3]);
114 handle_midi(currEvent.buffer, settings);
118 return 0;
121 int init_jack_midi(MidiSettings* settings)
123 int errorCode = 0;
125 errorCode = jack_set_process_callback(g_jack_client, &jack_process_cb, settings);
126 g_jackMidiIn = jack_port_register(g_jack_client, "in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 1024);
127 return (errorCode == 0 && g_jackMidiIn != 0);
129 #endif // JACK_MIDI_SUPPORT
131 int init_alsa_sequencer(const char* appName)
133 snd_seq_port_info_t * seq_port_info = NULL;
134 int ret = 0;
136 /* ALSA sequencer initialisation */
137 ret = snd_seq_open( &g_seq_ptr, "default", SND_SEQ_OPEN_INPUT, 0);
138 if (ret < 0)
139 return ret;
141 // setup alsa sequencer port
142 snd_seq_port_info_alloca(&seq_port_info);
143 snd_seq_port_info_set_capability(seq_port_info, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE);
144 snd_seq_port_info_set_type( seq_port_info, SND_SEQ_PORT_TYPE_APPLICATION);
145 snd_seq_port_info_set_midi_channels(seq_port_info, 16);
146 snd_seq_port_info_set_port_specified(seq_port_info, 1);
147 snd_seq_port_info_set_name(seq_port_info, "midi in");
148 snd_seq_port_info_set_port(seq_port_info, 0);
150 ret = snd_seq_create_port(g_seq_ptr, seq_port_info);
152 if (ret < 0)
153 printMMCMessage("Error with alsa sequencer initialization, %s\n", snd_strerror(ret));
154 else // all is well, register the sequencer with whatever name was passed in
155 snd_seq_set_client_name(g_seq_ptr, appName);
157 return ret;
160 int init_jack(const char* appName)
162 jack_status_t status;
163 g_jack_client = jack_client_open(appName, JackNoStartServer, &status);
165 if (!g_jack_client) // something went wrong
166 return -1; // todo: check status to see exactly what happened
168 return 0;
171 int activate_jack()
173 /* tell jack that we are ready to do our thing */
174 return jack_activate(g_jack_client);
177 void handle_midi(uint8_t* midiBuff, MidiSettings* settings)
179 if (midiBuff[0] == 0xF0 && midiBuff[1] == 0x7F && midiBuff[3] == 0x06)
181 const uint8_t messageDeviceID = midiBuff[2];
182 if (messageDeviceID == settings->deviceID)
184 const char mmcCommand = midiBuff[4];
185 switch (mmcCommand)
187 case 1: /* stop */
188 if (settings->verbose)
189 printMMCMessage("MMC Stop -> JACK transport stop\n");
190 jack_transport_stop(g_jack_client);
191 break;
192 case 2: /* play */
193 case 3: /* deferred play */
194 if (settings->verbose)
195 printMMCMessage("MMC Play -> JACK transport start\n");
196 jack_transport_start(g_jack_client);
197 break;
198 case 4:
199 if (settings->verbose)
200 printMMCMessage("MMC Fast Forward -> Ignored\n");
201 break;
202 case 5: /* rewind */
203 if (settings->verbose)
204 printMMCMessage("MMC Play -> JACK transport locate to 0\n");
205 jack_transport_locate(g_jack_client, 0);
206 break;
207 case 6:
208 if (settings->verbose)
209 printMMCMessage("MMC Record Strobe (Punch In) -> Ignored\n");
210 break;
211 case 7:
212 if (settings->verbose)
213 printMMCMessage("MMC Record Exit (Punch out) -> Ignored\n");
214 break;
215 case 8:
216 if (settings->verbose)
217 printMMCMessage("MMC Record Ready (Record Pause) -> Ignored\n");
218 break;
219 case 9: /* pause */
220 if (settings->verbose)
221 printMMCMessage("MMC Pause -> JACK transport stop\n");
222 jack_transport_stop(g_jack_client);
223 break;
224 case 0x44: /* goto */
225 if (midiBuff[5] == 0x06 && midiBuff[6] == 0x01)
227 // some devices call hour 0 "60". Mask off the upper bits
228 uint8_t hour = (midiBuff[7] & 0x1f);
229 uint8_t minute = midiBuff[8];
230 uint8_t second = midiBuff[9];
231 uint8_t frame = midiBuff[10];
232 uint8_t subframe = midiBuff[11]; // percentage of a frame: 0 - 99
233 jack_position_t jack_pos;
235 // get Jack's current framerate and position
236 jack_transport_query(g_jack_client, &jack_pos);
237 uint32_t jack_frame_rate = jack_pos.frame_rate;
238 uint32_t jack_time_ms = (jack_pos.frame * 1000) / jack_frame_rate;
239 uint32_t device_time_ms = ((subframe * 10) / settings->frameRate + // subframe == 1/100th of a frame
240 (frame * 1000) / settings->frameRate +
241 (second * 1000) +
242 (minute * 60 * 1000) +
243 (hour * 60 * 60 * 1000));
245 // difference in milliseconds from JACK's reported transport to the MIDI goto's time
246 uint32_t jitter = (device_time_ms > jack_time_ms ? (device_time_ms - jack_time_ms) : (jack_time_ms - device_time_ms));
248 if (settings->verbose)
249 printMMCMessage("MMC locate to hour: %d, minute: %d, second: %d, frame: %d, subframe: %d\n",
250 hour,
251 minute,
252 second,
253 frame,
254 subframe);
256 // check if the JACK clock is far enough away from the MMC time to care
257 if (jitter > settings->jitterTolerance)
258 { // it is, change it.
259 jack_pos.valid = 0; // only the frame number will be valid since that's all we're changing
261 // need a placeholder so that we can keep precision while not letting the integer overflow
262 uint64_t placeholder = (uint64_t)device_time_ms * jack_frame_rate / 1000;
263 jack_pos.frame = placeholder;
265 jack_transport_reposition(g_jack_client, &jack_pos);
267 else if (settings->verbose)
268 printMMCMessage("New position is %d ms away and within jitter range, ignoring.\n", jitter);
270 break;
271 case 0x48: /* step */
273 int step;
274 jack_position_t jack_pos;
276 step = midiBuff[6];
277 if (step > 0x40)
279 step &= 0x3F;
280 step = -step;
283 if (settings->verbose)
284 printMMCMessage("MMC Step %d\n", step);
286 // get Jack's current framerate and position
287 jack_transport_query(g_jack_client, &jack_pos);
289 jack_pos.valid = 0; // only the frame number will be valid since that's all we're changing
291 jack_pos.frame += (int64_t)step * (int64_t)jack_pos.frame_rate / (int64_t)10;
293 jack_transport_reposition(g_jack_client, &jack_pos);
295 break;
296 default:
297 if (settings->verbose)
298 printMMCMessage("unsupported MMC command: 0x%x\n", mmcCommand);
301 else if (settings->verbose)
303 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",
304 messageDeviceID,
305 settings->deviceID);
311 void listen_loop (MidiSettings* settings)
313 snd_seq_event_t * seq_event_ptr = NULL;
315 int npfd = snd_seq_poll_descriptors_count(g_seq_ptr, POLLIN);
316 struct pollfd * pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
317 snd_seq_poll_descriptors(g_seq_ptr, pfd, npfd, POLLIN);
319 while(!g_quit)
321 if (poll(pfd, npfd, 250) > 0 && snd_seq_event_input(g_seq_ptr, &seq_event_ptr) >= 0 && seq_event_ptr->type == SND_SEQ_EVENT_SYSEX)
322 handle_midi((uint8_t *)seq_event_ptr->data.ext.ptr, settings);
324 #if LASH_SUPPORT
325 /* Process LASH events */
327 lash_event_t * lash_event_ptr = NULL;
328 lash_config_t * lash_config_ptr = NULL;
329 while ((lash_event_ptr = lash_get_event(g_lashc)) != NULL)
331 process_lash_event(lash_event_ptr);
332 lash_event_destroy(lash_event_ptr);
335 /* Process LASH configs */
336 while ((lash_config_ptr = lash_get_config(g_lashc)) != NULL)
338 process_lash_config(lash_config_ptr);
339 lash_config_destroy(lash_config_ptr);
342 #endif
346 void cleanup_globals()
348 int ret = 0;
350 if (g_seq_ptr)
352 ret = snd_seq_close(g_seq_ptr);
353 if (ret < 0)
355 printMMCMessage("Cannot close sequncer, %s\n", snd_strerror(ret));
359 if (g_jack_client)
361 jack_deactivate(g_jack_client);
362 jack_client_close(g_jack_client);