code cleanup in preparation of separating out the core loop/functionality
[jackctlmmc.git] / main.c
blob50444394e4561827c12d4d40b76de8f41ba9b623
1 #include <jack/jack.h>
2 #include <alsa/asoundlib.h>
3 #include <lash/lash.h>
5 #include <signal.h>
6 #include <unistd.h>
7 #include <stdio.h>
8 #include <stdlib.h>
10 snd_seq_t * g_seq_ptr = NULL;
11 lash_client_t * g_lashc = NULL;
12 jack_client_t * g_jack_client = NULL;
14 // frame rate (frames per second) of the MMC controlling device
15 uint32_t g_device_frame_rate = 30; // command line equivalent: -f 30
17 // jitter tolerance in milliseconds
18 uint32_t g_jitter_tolerance = 50; //command line: -t 50
20 int g_verbose = 0; // pass -v on the command line to turn this on
22 /* a flag which will be set by our signal handler when it's time to exit */
23 int g_quit = 0;
25 /* the signal handler */
26 void signalled(int signal)
28 g_quit = 1;
31 // foward declares
32 void process_lash_event(lash_event_t * event_ptr);
33 void process_lash_config(lash_config_t * config_ptr);
34 int init_alsa_sequencer();
35 void init_lash();
36 void process_command_line(int argc, char *argv[]);
37 void cleanup();
39 int main(int argc, char *argv[])
41 int ret = 0;
42 lash_event_t * lash_event_ptr = NULL;
43 lash_config_t * lash_config_ptr = NULL;
44 snd_seq_event_t * seq_event_ptr = NULL;
46 int npfd;
47 struct pollfd * pfd;
49 // read the jitter tolerance, verbose setting, and device frame rate from command line
50 process_command_line(argc, argv);
52 printf("%s starting with a jitter tolerance of %d milliseconds and a device frame rate of %d per second.\n\n",
53 argv[0], g_jitter_tolerance, g_device_frame_rate);
55 /* setup our signal handler signalled() above, so we can exit cleanly (see end of main()) */
56 signal(SIGINT, signalled);
58 /* ALSA sequencer initialization */
59 ret = init_alsa_sequencer();
60 if (ret < 0)
62 printf("Error with alsa sequencer initialization, %s\n", snd_strerror(ret));
63 cleanup();
64 exit(0);
66 else
68 snd_seq_set_client_name(g_seq_ptr, "jackctlmmc");
71 /* naturally we need to become a jack client :) */
72 g_jack_client = jack_client_new("jackctlmmc");
73 if (!g_jack_client)
75 printf("couldn't connect to jack server. Either it's not running or the client name is already taken\n");
76 cleanup();
77 exit(0);
80 init_lash(argc, argv);
82 /* tell jack that we are ready to do our thing */
83 jack_activate(g_jack_client);
85 npfd = snd_seq_poll_descriptors_count(g_seq_ptr, POLLIN);
86 pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
87 snd_seq_poll_descriptors(g_seq_ptr, pfd, npfd, POLLIN);
89 /* wait until this app receives a SIGINT (i.e. press ctrl-c in the terminal) see signalled() above */
90 while(!g_quit)
92 if (poll(pfd, npfd, 250) > 0 && snd_seq_event_input(g_seq_ptr, &seq_event_ptr) >= 0)
94 if (seq_event_ptr->type == SND_SEQ_EVENT_SYSEX &&
95 ((uint8_t *)seq_event_ptr->data.ext.ptr)[0] == 0xF0 &&
96 ((uint8_t *)seq_event_ptr->data.ext.ptr)[1] == 0x7F &&
97 ((uint8_t *)seq_event_ptr->data.ext.ptr)[2] == 0x7F && /* All devices */
98 ((uint8_t *)seq_event_ptr->data.ext.ptr)[3] == 0x06)
100 switch (((char *)seq_event_ptr->data.ext.ptr)[4])
102 case 1: /* stop */
103 if (g_verbose)
104 printf("MMC Stop -> JACK transport stop\n");
105 jack_transport_stop(g_jack_client);
106 break;
107 case 2: /* play */
108 case 3: /* deferred play */
109 if (g_verbose)
110 printf("MMC Play -> JACK transport start\n");
111 jack_transport_start(g_jack_client);
112 break;
113 case 5: /* rewind */
114 if (g_verbose)
115 printf("MMC Play -> JACK transport locate to 0\n");
116 jack_transport_locate(g_jack_client, 0);
117 break;
118 case 0x44:
119 if (((uint8_t*)seq_event_ptr->data.ext.ptr)[5] == 0x06 &&
120 ((uint8_t*)seq_event_ptr->data.ext.ptr)[6] == 0x01)
122 // some devices call hour 0 "60". Mask off the upper bits
123 uint8_t hour = ((uint8_t*)seq_event_ptr->data.ext.ptr)[7] & 0x1f;
124 uint8_t minute = ((uint8_t*)seq_event_ptr->data.ext.ptr)[8];
125 uint8_t second = ((uint8_t*)seq_event_ptr->data.ext.ptr)[9];
126 uint8_t frame = ((uint8_t*)seq_event_ptr->data.ext.ptr)[10];
127 uint8_t subframe = ((uint8_t*)seq_event_ptr->data.ext.ptr)[11]; // percentage of a frame: 0 - 99
128 jack_position_t jack_pos;
130 if (g_verbose)
131 printf("MMC locate to hour: %d, minute: %d, second: %d, frame: %d, subframe: %d\n",
132 hour,
133 minute,
134 second,
135 frame,
136 subframe);
138 // get Jack's current framerate and position
139 jack_transport_query(g_jack_client, &jack_pos);
140 const uint32_t jack_frame_rate = jack_pos.frame_rate;
141 const uint32_t jack_time_ms = (jack_pos.frame * 1000) / jack_frame_rate;
142 const uint32_t device_time_ms = ((subframe * 10) /g_device_frame_rate + // subframe == 1/100th of a frame
143 (frame * 1000) / g_device_frame_rate +
144 (second * 1000) +
145 (minute * 60 * 1000) +
146 (hour * 60 * 60 * 1000));
147 const uint32_t jitter = (device_time_ms > jack_time_ms ? (device_time_ms - jack_time_ms) : (jack_time_ms - device_time_ms));
149 if (g_verbose)
150 printf("difference between JACK time and MMC destination time in ms: %d\n", jitter);
152 // check if the JACK clock is far enough away from the MMC time to care
153 if (jitter > g_jitter_tolerance)
154 { // it is, change it.
155 jack_pos.valid = 0; // only the frame number will be valid since that's all we're changing
156 jack_pos.frame = device_time_ms * jack_frame_rate / 1000;
157 jack_transport_reposition(g_jack_client, &jack_pos);
161 break;
162 default:
163 if (g_verbose)
164 printf("unsupported MMC command: 0x%x\n", ((char *)seq_event_ptr->data.ext.ptr)[4]);
169 /* Process LASH events */
170 while ((lash_event_ptr = lash_get_event(g_lashc)) != NULL)
172 process_lash_event(lash_event_ptr);
173 lash_event_destroy(lash_event_ptr);
176 /* Process LASH configs */
177 while ((lash_config_ptr = lash_get_config(g_lashc)) != NULL)
179 process_lash_config(lash_config_ptr);
180 lash_config_destroy(lash_config_ptr);
184 /* so we shall quit, eh? ok, cleanup time. otherwise jack would probably produce an xrun
185 * on shutdown */
186 jack_deactivate(g_jack_client);
187 cleanup();
189 if (g_verbose)
190 printf("exited normally\n");
191 return 0;
194 int init_alsa_sequencer()
196 snd_seq_port_info_t * seq_port_info = NULL;
197 int ret = 0;
199 /* ALSA sequencer initialisation */
200 ret = snd_seq_open( &g_seq_ptr, "default", SND_SEQ_OPEN_INPUT, 0);
201 if (ret < 0)
203 return ret;
206 // setup alsa sequencer port
207 snd_seq_port_info_alloca(&seq_port_info);
208 snd_seq_port_info_set_capability(seq_port_info, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE);
209 snd_seq_port_info_set_type( seq_port_info, SND_SEQ_PORT_TYPE_APPLICATION);
210 snd_seq_port_info_set_midi_channels(seq_port_info, 16);
211 snd_seq_port_info_set_port_specified(seq_port_info, 1);
212 snd_seq_port_info_set_name(seq_port_info, "midi in");
213 snd_seq_port_info_set_port(seq_port_info, 0);
215 return snd_seq_create_port(g_seq_ptr, seq_port_info);
218 void init_lash(int argc, char* argv[])
220 lash_event_t * lash_event_ptr = NULL;
222 /* LASH setup */
223 g_lashc = lash_init( lash_extract_args(&argc, &argv), "jackctlmmc", 0, LASH_PROTOCOL_VERSION);
225 if (g_lashc == NULL)
227 printf("Failed to connect to LASH. Session management will not occur.\n");
229 else
231 lash_event_ptr = lash_event_new_with_type(LASH_Client_Name);
232 lash_event_set_string(lash_event_ptr, "jackctlmmc");
233 lash_send_event(g_lashc, lash_event_ptr);
236 // register JACK and ALSA clients with lash
237 lash_alsa_client_id(g_lashc, snd_seq_client_id(g_seq_ptr));
238 lash_jack_client_name(g_lashc, "jackctlmmc");
241 void process_lash_event(lash_event_t * event_ptr)
243 enum LASH_Event_Type type;
244 const char * str;
246 type = lash_event_get_type(event_ptr);
247 str = lash_event_get_string(event_ptr);
249 switch (type)
251 case LASH_Quit:
252 printf("LASH_Quit received.\n");
253 g_lashc = NULL;
254 g_quit = 1;
255 break;
256 case LASH_Save_File:
257 case LASH_Restore_File:
258 case LASH_Save_Data_Set:
259 default:
260 printf("LASH Event. Type = %u, string = \"%s\"\n",
261 (unsigned int)type,
262 (str == NULL)?"":str);
266 void process_lash_config(lash_config_t * config_ptr)
268 const char * key;
269 const void * data;
270 size_t data_size;
272 key = lash_config_get_key(config_ptr);
273 data = lash_config_get_value(config_ptr);
274 data_size = lash_config_get_value_size(config_ptr);
276 printf("LASH Config. Key = \"%s\"\n", key);
279 void process_command_line(int argc, char *argv[])
281 int parameter;
282 while ((parameter = getopt(argc, argv, "f:t:v")) != -1)
284 switch (parameter)
286 case 'v':
287 g_verbose = 1;
288 break;
289 case 'f':
290 g_device_frame_rate = atoi(optarg);
291 break;
292 case 't':
293 g_jitter_tolerance = atoi(optarg);
294 break;
295 case '?':
296 fprintf(stderr, "usage: %s -f <device_frames_per_second> -t <jitter_tolerance_in_ms>\n", argv[0]);
297 exit(1);
298 break;
303 void cleanup()
305 printf("cleaning up\n");
306 int ret = 0;
307 if (g_seq_ptr)
309 ret = snd_seq_close(g_seq_ptr);
310 if (ret < 0)
312 printf("Cannot close sequncer, %s\n", snd_strerror(ret));
316 if (g_jack_client)
318 jack_client_close(g_jack_client);