2 * Control JACK transport using MMC (MIDI)
4 * Copyright (c) 2006,2007,2008 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 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
;
33 type
= lash_event_get_type(event_ptr
);
34 str
= lash_event_get_string(event_ptr
);
39 printf("LASH_Quit received.\n");
44 case LASH_Restore_File
:
45 case LASH_Save_Data_Set
:
47 printf("LASH Event. Type = %u, string = \"%s\"\n",
49 (str
== NULL
)?"":str
);
53 void process_lash_config(lash_config_t
* config_ptr
)
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
;
71 /* ALSA sequencer initialisation */
72 ret
= snd_seq_open( &g_seq_ptr
, "default", SND_SEQ_OPEN_INPUT
, 0);
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
);
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
);
95 void init_lash(int argc
, char* argv
[])
97 lash_event_t
* lash_event_ptr
= NULL
;
100 g_lashc
= lash_init( lash_extract_args(&argc
, &argv
), "jackctlmmc", 0, LASH_PROTOCOL_VERSION
);
104 printf("Failed to connect to LASH. Session management will not occur.\n");
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
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
)
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
);
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
)[2] == 0x7F && /* All devices */
153 ((uint8_t *)seq_event_ptr
->data
.ext
.ptr
)[3] == 0x06)
155 switch (((char *)seq_event_ptr
->data
.ext
.ptr
)[4])
159 printf("MMC Stop -> JACK transport stop\n");
160 jack_transport_stop(g_jack_client
);
163 case 3: /* deferred play */
165 printf("MMC Play -> JACK transport start\n");
166 jack_transport_start(g_jack_client
);
170 printf("MMC Play -> JACK transport locate to 0\n");
171 jack_transport_locate(g_jack_client
, 0);
173 case 0x44: /* goto */
174 if (((uint8_t*)seq_event_ptr
->data
.ext
.ptr
)[5] == 0x06 &&
175 ((uint8_t*)seq_event_ptr
->data
.ext
.ptr
)[6] == 0x01)
177 // some devices call hour 0 "60". Mask off the upper bits
178 uint8_t hour
= ((uint8_t*)seq_event_ptr
->data
.ext
.ptr
)[7] & 0x1f;
179 uint8_t minute
= ((uint8_t*)seq_event_ptr
->data
.ext
.ptr
)[8];
180 uint8_t second
= ((uint8_t*)seq_event_ptr
->data
.ext
.ptr
)[9];
181 uint8_t frame
= ((uint8_t*)seq_event_ptr
->data
.ext
.ptr
)[10];
182 uint8_t subframe
= ((uint8_t*)seq_event_ptr
->data
.ext
.ptr
)[11]; // percentage of a frame: 0 - 99
183 jack_position_t jack_pos
;
186 printf("MMC locate to hour: %d, minute: %d, second: %d, frame: %d, subframe: %d\n",
193 // get Jack's current framerate and position
194 jack_transport_query(g_jack_client
, &jack_pos
);
195 uint32_t jack_frame_rate
= jack_pos
.frame_rate
;
196 uint32_t jack_time_ms
= (jack_pos
.frame
* 1000) / jack_frame_rate
;
197 uint32_t device_time_ms
= ((subframe
* 10) / frameRate
+ // subframe == 1/100th of a frame
198 (frame
* 1000) / frameRate
+
200 (minute
* 60 * 1000) +
201 (hour
* 60 * 60 * 1000));
203 // difference in milliseconds from JACK's reported transport to the MIDI goto's time
204 uint32_t jitter
= (device_time_ms
> jack_time_ms
? (device_time_ms
- jack_time_ms
) : (jack_time_ms
- device_time_ms
));
207 printf("difference between JACK time and MMC destination time in ms: %d\n", jitter
);
209 // check if the JACK clock is far enough away from the MMC time to care
210 if (jitter
> jitterTolerance
)
211 { // it is, change it.
212 jack_pos
.valid
= 0; // only the frame number will be valid since that's all we're changing
214 // need a placeholder so that we can keep precision while not letting the integer overflow
215 uint64_t placeholder
= (uint64_t)device_time_ms
* jack_frame_rate
/ 1000;
216 jack_pos
.frame
= placeholder
;
218 jack_transport_reposition(g_jack_client
, &jack_pos
);
225 printf("unsupported MMC command: 0x%x\n", ((char *)seq_event_ptr
->data
.ext
.ptr
)[4]);
230 /* Process LASH events */
231 while ((lash_event_ptr
= lash_get_event(g_lashc
)) != NULL
)
233 process_lash_event(lash_event_ptr
);
234 lash_event_destroy(lash_event_ptr
);
237 /* Process LASH configs */
238 while ((lash_config_ptr
= lash_get_config(g_lashc
)) != NULL
)
240 process_lash_config(lash_config_ptr
);
241 lash_config_destroy(lash_config_ptr
);
246 void cleanup_globals()
252 ret
= snd_seq_close(g_seq_ptr
);
255 printf("Cannot close sequncer, %s\n", snd_strerror(ret
));
261 jack_client_close(g_jack_client
);
262 jack_deactivate(g_jack_client
);