MMC step support; detect some other MMC commands
[jackctlmmc.git] / common.c
blob1c99594bcda1812c765bfd40ee8c7afa5c3e8ea9
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 snd_seq_t* g_seq_ptr = NULL;
24 lash_client_t* g_lashc = NULL;
25 jack_client_t* g_jack_client = NULL;
26 int g_quit = 0; // a flag which will be set by our signal handler when it's time to exit
28 void process_lash_event(lash_event_t * event_ptr)
30 enum LASH_Event_Type type;
31 const char * str;
33 type = lash_event_get_type(event_ptr);
34 str = lash_event_get_string(event_ptr);
36 switch (type)
38 case LASH_Quit:
39 printf("LASH_Quit received.\n");
40 g_lashc = NULL;
41 g_quit = 1;
42 break;
43 case LASH_Save_File:
44 case LASH_Restore_File:
45 case LASH_Save_Data_Set:
46 default:
47 printf("LASH Event. Type = %u, string = \"%s\"\n",
48 (unsigned int)type,
49 (str == NULL)?"":str);
53 void process_lash_config(lash_config_t * config_ptr)
55 const char * key;
56 const void * data;
57 size_t data_size;
59 key = lash_config_get_key(config_ptr);
60 data = lash_config_get_value(config_ptr);
61 data_size = lash_config_get_value_size(config_ptr);
63 printf("LASH Config. Key = \"%s\"\n", key);
66 int init_alsa_sequencer(const char* appName)
68 snd_seq_port_info_t * seq_port_info = NULL;
69 int ret = 0;
71 /* ALSA sequencer initialisation */
72 ret = snd_seq_open( &g_seq_ptr, "default", SND_SEQ_OPEN_INPUT, 0);
73 if (ret < 0)
74 return ret;
76 // setup alsa sequencer port
77 snd_seq_port_info_alloca(&seq_port_info);
78 snd_seq_port_info_set_capability(seq_port_info, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE);
79 snd_seq_port_info_set_type( seq_port_info, SND_SEQ_PORT_TYPE_APPLICATION);
80 snd_seq_port_info_set_midi_channels(seq_port_info, 16);
81 snd_seq_port_info_set_port_specified(seq_port_info, 1);
82 snd_seq_port_info_set_name(seq_port_info, "midi in");
83 snd_seq_port_info_set_port(seq_port_info, 0);
85 ret = snd_seq_create_port(g_seq_ptr, seq_port_info);
87 if (ret < 0)
88 printf("Error with alsa sequencer initialization, %s\n", snd_strerror(ret));
89 else // all is well, register the sequencer with whatever name was passed in
90 snd_seq_set_client_name(g_seq_ptr, appName);
92 return ret;
95 void init_lash(int argc, char* argv[])
97 lash_event_t * lash_event_ptr = NULL;
99 /* LASH setup */
100 g_lashc = lash_init( lash_extract_args(&argc, &argv), "jackctlmmc", 0, LASH_PROTOCOL_VERSION);
102 if (g_lashc == NULL)
104 printf("Failed to connect to LASH. Session management will not occur.\n");
106 else
108 lash_event_ptr = lash_event_new_with_type(LASH_Client_Name);
109 lash_event_set_string(lash_event_ptr, "jackctlmmc");
110 lash_send_event(g_lashc, lash_event_ptr);
113 // register JACK and ALSA clients with lash
114 lash_alsa_client_id(g_lashc, snd_seq_client_id(g_seq_ptr));
115 lash_jack_client_name(g_lashc, "jackctlmmc");
118 int init_jack(const char* appName)
120 jack_status_t status;
121 g_jack_client = jack_client_open(appName, JackNoStartServer, &status);
123 if (!g_jack_client) // something went wrong
124 return -1; // todo: check status to see exactly what happened
126 return 0;
129 void activate_jack()
131 /* tell jack that we are ready to do our thing */
132 jack_activate(g_jack_client);
135 void listen_loop (uint32_t frameRate, uint32_t jitterTolerance, int verbose, uint8_t deviceID)
137 lash_event_t * lash_event_ptr = NULL;
138 lash_config_t * lash_config_ptr = NULL;
139 snd_seq_event_t * seq_event_ptr = NULL;
141 int npfd = snd_seq_poll_descriptors_count(g_seq_ptr, POLLIN);
142 struct pollfd * pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
143 snd_seq_poll_descriptors(g_seq_ptr, pfd, npfd, POLLIN);
145 while(!g_quit)
147 if (poll(pfd, npfd, 250) > 0 && snd_seq_event_input(g_seq_ptr, &seq_event_ptr) >= 0)
149 if (seq_event_ptr->type == SND_SEQ_EVENT_SYSEX &&
150 ((uint8_t *)seq_event_ptr->data.ext.ptr)[0] == 0xF0 &&
151 ((uint8_t *)seq_event_ptr->data.ext.ptr)[1] == 0x7F &&
152 ((uint8_t *)seq_event_ptr->data.ext.ptr)[3] == 0x06)
154 const uint8_t messageDeviceID = ((uint8_t *)seq_event_ptr->data.ext.ptr)[2];
155 if (messageDeviceID == deviceID)
157 const char mmcCommand = ((char *)seq_event_ptr->data.ext.ptr)[4];
158 switch (mmcCommand)
160 case 1: /* stop */
161 if (verbose)
162 printf("MMC Stop -> JACK transport stop\n");
163 jack_transport_stop(g_jack_client);
164 break;
165 case 2: /* play */
166 case 3: /* deferred play */
167 if (verbose)
168 printf("MMC Play -> JACK transport start\n");
169 jack_transport_start(g_jack_client);
170 break;
171 case 4:
172 if (verbose)
173 printf("MMC Fast Forward -> Ignored\n");
174 break;
175 case 5: /* rewind */
176 if (verbose)
177 printf("MMC Play -> JACK transport locate to 0\n");
178 jack_transport_locate(g_jack_client, 0);
179 break;
180 case 6:
181 if (verbose)
182 printf("MMC Record Strobe (Punch In) -> Ignored\n");
183 break;
184 case 7:
185 if (verbose)
186 printf("MMC Record Exit (Punch out) -> Ignored\n");
187 break;
188 case 8:
189 if (verbose)
190 printf("MMC Record Ready (Record Pause) -> Ignored\n");
191 break;
192 case 9: /* pause */
193 if (verbose)
194 printf("MMC Pause -> JACK transport stop\n");
195 jack_transport_stop(g_jack_client);
196 break;
197 case 0x44: /* goto */
198 if (((uint8_t*)seq_event_ptr->data.ext.ptr)[5] == 0x06 &&
199 ((uint8_t*)seq_event_ptr->data.ext.ptr)[6] == 0x01)
201 // some devices call hour 0 "60". Mask off the upper bits
202 uint8_t hour = ((uint8_t*)seq_event_ptr->data.ext.ptr)[7] & 0x1f;
203 uint8_t minute = ((uint8_t*)seq_event_ptr->data.ext.ptr)[8];
204 uint8_t second = ((uint8_t*)seq_event_ptr->data.ext.ptr)[9];
205 uint8_t frame = ((uint8_t*)seq_event_ptr->data.ext.ptr)[10];
206 uint8_t subframe = ((uint8_t*)seq_event_ptr->data.ext.ptr)[11]; // percentage of a frame: 0 - 99
207 jack_position_t jack_pos;
209 // get Jack's current framerate and position
210 jack_transport_query(g_jack_client, &jack_pos);
211 uint32_t jack_frame_rate = jack_pos.frame_rate;
212 uint32_t jack_time_ms = (jack_pos.frame * 1000) / jack_frame_rate;
213 uint32_t device_time_ms = ((subframe * 10) / frameRate + // subframe == 1/100th of a frame
214 (frame * 1000) / frameRate +
215 (second * 1000) +
216 (minute * 60 * 1000) +
217 (hour * 60 * 60 * 1000));
219 // difference in milliseconds from JACK's reported transport to the MIDI goto's time
220 uint32_t jitter = (device_time_ms > jack_time_ms ? (device_time_ms - jack_time_ms) : (jack_time_ms - device_time_ms));
222 if (verbose)
223 printf("MMC locate to hour: %d, minute: %d, second: %d, frame: %d, subframe: %d\n",
224 hour,
225 minute,
226 second,
227 frame,
228 subframe);
230 // check if the JACK clock is far enough away from the MMC time to care
231 if (jitter > jitterTolerance)
232 { // it is, change it.
233 jack_pos.valid = 0; // only the frame number will be valid since that's all we're changing
235 // need a placeholder so that we can keep precision while not letting the integer overflow
236 uint64_t placeholder = (uint64_t)device_time_ms * jack_frame_rate / 1000;
237 jack_pos.frame = placeholder;
239 jack_transport_reposition(g_jack_client, &jack_pos);
241 if (verbose)
242 printf("Difference between JACK time and MMC destination time in ms: %d. New position is outside of jitter range -> repositioning JACK.\n", jitter);
244 else if (verbose)
246 if (verbose)
247 printf("Difference between JACK time and MMC destination time in ms: %d. New position is within jitter range -> ignoring.\n", jitter);
251 break;
252 case 0x48: /* step */
254 int step;
255 jack_position_t jack_pos;
257 step = ((uint8_t*)seq_event_ptr->data.ext.ptr)[6];
258 if (step > 0x40)
260 step &= 0x3F;
261 step = -step;
264 if (verbose)
265 printf("MMC Step %d\n", step);
267 // get Jack's current framerate and position
268 jack_transport_query(g_jack_client, &jack_pos);
270 jack_pos.valid = 0; // only the frame number will be valid since that's all we're changing
272 jack_pos.frame += (int64_t)step * (int64_t)jack_pos.frame_rate / (int64_t)10;
274 jack_transport_reposition(g_jack_client, &jack_pos);
276 break;
277 default:
278 if (verbose)
279 printf("unsupported MMC command: 0x%x\n", mmcCommand);
282 else if (verbose)
284 printf("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",
285 messageDeviceID,
286 deviceID);
292 /* Process LASH events */
293 while ((lash_event_ptr = lash_get_event(g_lashc)) != NULL)
295 process_lash_event(lash_event_ptr);
296 lash_event_destroy(lash_event_ptr);
299 /* Process LASH configs */
300 while ((lash_config_ptr = lash_get_config(g_lashc)) != NULL)
302 process_lash_config(lash_config_ptr);
303 lash_config_destroy(lash_config_ptr);
308 void cleanup_globals()
310 int ret = 0;
312 if (g_seq_ptr)
314 ret = snd_seq_close(g_seq_ptr);
315 if (ret < 0)
317 printf("Cannot close sequncer, %s\n", snd_strerror(ret));
321 if (g_jack_client)
323 jack_deactivate(g_jack_client);
324 jack_client_close(g_jack_client);