Make LASH support actually work. Submitted by Nedko Arnaudov.
[jack-smf-utils.git] / src / jack-smf-player.c
blobabf485212d0e3a0254a1b75ad6116bbedc3a4e33
1 /*-
2 * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa <trasz@FreeBSD.org>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE
15 * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18 * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
20 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
21 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * This is jack-smf-player, Standard MIDI File player for JACK MIDI.
31 * For questions and comments, contact Edward Tomasz Napierala <trasz@FreeBSD.org>.
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <unistd.h>
39 #include <assert.h>
40 #include <string.h>
41 #include <sysexits.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <jack/jack.h>
45 #include <jack/midiport.h>
46 #include <glib.h>
48 #include "config.h"
49 #include "smf.h"
51 #ifdef HAVE_LASH
52 #include <lash/lash.h>
53 #endif
55 #define PROGRAM_NAME "jack-smf-player"
56 #define PROGRAM_VERSION PACKAGE_VERSION
58 #define MIDI_CONTROLLER 0xB0
59 #define MIDI_ALL_SOUND_OFF 120
61 #define MAX_NUMBER_OF_TRACKS 128
63 jack_port_t *output_ports[MAX_NUMBER_OF_TRACKS];
64 int drop_messages = 0;
65 jack_client_t *jack_client = NULL;
66 double rate_limit = 0;
67 int just_one_output = 0;
68 int start_stopped = 0;
69 int use_transport = 1;
70 int be_quiet = 0;
71 volatile int playback_started = -1, song_position = 0, ctrl_c_pressed = 0;
72 smf_t *smf = NULL;
74 #ifdef HAVE_LASH
75 lash_client_t *lash_client;
76 #endif
78 /* Will emit a warning if time between jack callbacks is longer than this. */
79 #define MAX_TIME_BETWEEN_CALLBACKS 0.1
81 /* Will emit a warning if execution of jack callback takes longer than this. */
82 #define MAX_PROCESSING_TIME 0.01
84 double
85 get_time(void)
87 double seconds;
88 int ret;
89 struct timeval tv;
91 ret = gettimeofday(&tv, NULL);
93 if (ret) {
94 perror("gettimeofday");
95 exit(EX_OSERR);
98 seconds = tv.tv_sec + tv.tv_usec / 1000000.0;
100 return seconds;
103 double
104 get_delta_time(void)
106 static double previously = -1.0;
107 double now;
108 double delta;
110 now = get_time();
112 if (previously == -1.0) {
113 previously = now;
115 return 0;
118 delta = now - previously;
119 previously = now;
121 assert(delta >= 0.0);
123 return delta;
126 static gboolean
127 warning_async(gpointer s)
129 const char *str = (const char *)s;
131 g_warning(str);
133 return FALSE;
136 static void
137 warn_from_jack_thread_context(const char *str)
139 g_idle_add(warning_async, (gpointer)str);
142 static double
143 nframes_to_ms(jack_nframes_t nframes)
145 jack_nframes_t sr;
147 sr = jack_get_sample_rate(jack_client);
149 assert(sr > 0);
151 return (nframes * 1000.0) / (double)sr;
154 static double
155 nframes_to_seconds(jack_nframes_t nframes)
157 return nframes_to_ms(nframes) / 1000.0;
160 static jack_nframes_t
161 ms_to_nframes(double ms)
163 jack_nframes_t sr;
165 sr = jack_get_sample_rate(jack_client);
167 assert(sr > 0);
169 return ((double)sr * ms) / 1000.0;
172 static jack_nframes_t
173 seconds_to_nframes(double seconds)
175 return ms_to_nframes(seconds * 1000.0);
178 static void
179 send_all_sound_off(void *port_buffers[MAX_NUMBER_OF_TRACKS], jack_nframes_t nframes)
181 int i, channel;
182 unsigned char *buffer;
184 for (i = 0; i <= smf->number_of_tracks; i++) {
185 for (channel = 0; channel < 16; channel++) {
186 #ifdef JACK_MIDI_NEEDS_NFRAMES
187 buffer = jack_midi_event_reserve(port_buffers[i], 0, 3, nframes);
188 #else
189 buffer = jack_midi_event_reserve(port_buffers[i], 0, 3);
190 #endif
191 if (buffer == NULL) {
192 warn_from_jack_thread_context("jack_midi_event_reserve failed, cannot send All Sound Off.");
193 break;
196 buffer[0] = MIDI_CONTROLLER | channel;
197 buffer[1] = MIDI_ALL_SOUND_OFF;
198 buffer[2] = 0;
201 if (just_one_output)
202 break;
206 static void
207 process_midi_output(jack_nframes_t nframes)
209 int i, t, bytes_remaining, track_number;
210 unsigned char *buffer, tmp_status;
211 void *port_buffers[MAX_NUMBER_OF_TRACKS];
212 jack_nframes_t last_frame_time;
213 jack_transport_state_t transport_state;
214 static jack_transport_state_t previous_transport_state = JackTransportStopped;
216 for (i = 0; i <= smf->number_of_tracks; i++) {
217 port_buffers[i] = jack_port_get_buffer(output_ports[i], nframes);
219 if (port_buffers[i] == NULL) {
220 warn_from_jack_thread_context("jack_port_get_buffer failed, cannot send anything.");
221 return;
224 #ifdef JACK_MIDI_NEEDS_NFRAMES
225 jack_midi_clear_buffer(port_buffers[i], nframes);
226 #else
227 jack_midi_clear_buffer(port_buffers[i]);
228 #endif
230 if (just_one_output)
231 break;
234 if (ctrl_c_pressed) {
235 send_all_sound_off(port_buffers, nframes);
237 /* The idea here is to exit at the second time process_midi_output gets called.
238 Otherwise, All Sound Off won't be delivered. */
239 ctrl_c_pressed++;
240 if (ctrl_c_pressed >= 3)
241 exit(0);
243 return;
246 if (use_transport) {
247 transport_state = jack_transport_query(jack_client, NULL);
248 if (transport_state == JackTransportStopped) {
249 if (previous_transport_state == JackTransportRolling)
250 send_all_sound_off(port_buffers, nframes);
252 previous_transport_state = transport_state;
254 return;
257 previous_transport_state = transport_state;
260 last_frame_time = jack_last_frame_time(jack_client);
262 /* End of song already? */
263 if (playback_started < 0)
264 return;
266 /* We may push at most one byte per 0.32ms to stay below 31.25 Kbaud limit. */
267 bytes_remaining = nframes_to_ms(nframes) * rate_limit;
269 for (;;) {
270 smf_event_t *event = smf_peek_next_event(smf);
272 if (event == NULL) {
273 if (!be_quiet)
274 g_debug("End of song.");
275 playback_started = -1;
277 if (!use_transport)
278 ctrl_c_pressed = 1;
280 break;
283 /* Skip over metadata events. */
284 if (smf_event_is_metadata(event)) {
285 char *decoded = smf_event_decode(event);
286 if (decoded && !be_quiet)
287 g_debug("Metadata: %s", decoded);
289 smf_get_next_event(smf);
290 continue;
293 bytes_remaining -= event->midi_buffer_length;
295 if (rate_limit > 0.0 && bytes_remaining <= 0) {
296 warn_from_jack_thread_context("Rate limiting in effect.");
297 break;
300 t = seconds_to_nframes(event->time_seconds) + playback_started - song_position + nframes - last_frame_time;
302 /* If computed time is too much into the future, we'll need
303 to send it later. */
304 if (t >= (int)nframes)
305 break;
307 /* If computed time is < 0, we missed a cycle because of xrun. */
308 if (t < 0)
309 t = 0;
311 assert(event->track->track_number >= 0 && event->track->track_number <= MAX_NUMBER_OF_TRACKS);
313 /* We will send this event; remove it from the queue. */
314 smf_get_next_event(smf);
316 /* First, send it via midi_out. */
317 track_number = 0;
319 #ifdef JACK_MIDI_NEEDS_NFRAMES
320 buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length, nframes);
321 #else
322 buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length);
323 #endif
325 if (buffer == NULL) {
326 warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST.");
327 break;
330 memcpy(buffer, event->midi_buffer, event->midi_buffer_length);
332 /* Ignore per-track outputs? */
333 if (just_one_output)
334 continue;
336 /* Send it via proper output port. */
337 track_number = event->track->track_number;
339 #ifdef JACK_MIDI_NEEDS_NFRAMES
340 buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length, nframes);
341 #else
342 buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length);
343 #endif
345 if (buffer == NULL) {
346 warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST.");
347 break;
350 /* Before sending, reset channel to 0. XXX: Not very pretty. */
351 assert(event->midi_buffer_length >= 1);
353 tmp_status = event->midi_buffer[0];
355 if (event->midi_buffer[0] >= 0x80 && event->midi_buffer[0] <= 0xEF)
356 event->midi_buffer[0] &= 0xF0;
358 memcpy(buffer, event->midi_buffer, event->midi_buffer_length);
360 event->midi_buffer[0] = tmp_status;
364 static int
365 process_callback(jack_nframes_t nframes, void *notused)
367 #ifdef MEASURE_TIME
368 if (get_delta_time() > MAX_TIME_BETWEEN_CALLBACKS) {
369 warn_from_jack_thread_context("Had to wait too long for JACK callback; scheduling problem?");
371 #endif
373 /* Check for impossible condition that actually happened to me, caused by some problem between jackd and OSS4. */
374 if (nframes <= 0) {
375 warn_from_jack_thread_context("Process callback called with nframes = 0; bug in JACK?");
376 return 0;
379 process_midi_output(nframes);
381 #ifdef MEASURE_TIME
382 if (get_delta_time() > MAX_PROCESSING_TIME) {
383 warn_from_jack_thread_context("Processing took too long; scheduling problem?");
385 #endif
387 return 0;
390 static int
391 sync_callback(jack_transport_state_t state, jack_position_t *position, void *notused)
393 assert(jack_client);
395 /* XXX: We should probably adapt to external tempo changes. */
397 if (state == JackTransportStarting) {
398 song_position = position->frame;
399 smf_seek_to_seconds(smf, nframes_to_seconds(position->frame));
401 if (!be_quiet)
402 g_debug("Seeking to %f seconds.", nframes_to_seconds(position->frame));
404 playback_started = jack_frame_time(jack_client);
406 } else if (state == JackTransportStopped) {
407 playback_started = -1;
410 return TRUE;
413 void timebase_callback(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *notused)
415 double min; /* Minutes since frame 0. */
416 long abs_tick; /* Ticks since frame 0. */
417 long abs_beat; /* Beats since frame 0. */
418 smf_tempo_t *tempo;
419 static smf_tempo_t *previous_tempo = NULL;
421 smf_event_t *event = smf_peek_next_event(smf);
422 if (event == NULL)
423 return;
425 tempo = smf_get_tempo_by_pulses(smf, event->time_pulses);
427 assert(tempo);
429 if (new_pos || previous_tempo != tempo) {
430 pos->valid = JackPositionBBT;
431 pos->beats_per_bar = tempo->numerator;
432 pos->beat_type = 1.0 / (double)tempo->denominator;
433 pos->ticks_per_beat = event->track->smf->ppqn; /* XXX: Is this right? */
434 pos->beats_per_minute = 60000000.0 / (double)tempo->microseconds_per_quarter_note;
436 min = pos->frame / ((double) pos->frame_rate * 60.0);
437 abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
438 abs_beat = abs_tick / pos->ticks_per_beat;
440 pos->bar = abs_beat / pos->beats_per_bar;
441 pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1;
442 pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat);
443 pos->bar_start_tick = pos->bar * pos->beats_per_bar * pos->ticks_per_beat;
444 pos->bar++; /* adjust start to bar 1 */
446 previous_tempo = tempo;
448 } else {
449 /* Compute BBT info based on previous period. */
450 pos->tick += nframes * pos->ticks_per_beat * pos->beats_per_minute / (pos->frame_rate * 60);
452 while (pos->tick >= pos->ticks_per_beat) {
453 pos->tick -= pos->ticks_per_beat;
454 if (++pos->beat > pos->beats_per_bar) {
455 pos->beat = 1;
456 ++pos->bar;
457 pos->bar_start_tick += pos->beats_per_bar * pos->ticks_per_beat;
463 /* Connects to the specified input port, disconnecting already connected ports. */
465 connect_to_input_port(const char *port)
467 int ret;
469 ret = jack_port_disconnect(jack_client, output_ports[0]);
471 if (ret) {
472 g_warning("Cannot disconnect MIDI port.");
474 return -3;
477 ret = jack_connect(jack_client, jack_port_name(output_ports[0]), port);
479 if (ret) {
480 g_warning("Cannot connect to %s.", port);
482 return -4;
485 g_warning("Connected to %s.", port);
487 return 0;
490 static void
491 init_jack(void)
493 int i, err;
495 #ifdef HAVE_LASH
496 lash_event_t *event;
497 #endif
499 jack_client = jack_client_open(PROGRAM_NAME, JackNullOption, NULL);
501 if (jack_client == NULL) {
502 g_critical("Could not connect to the JACK server; run jackd first?");
503 exit(EX_UNAVAILABLE);
506 #ifdef HAVE_LASH
507 event = lash_event_new_with_type(LASH_Client_Name);
508 assert (event); /* Documentation does not say anything about return value. */
509 lash_event_set_string(event, jack_get_client_name(jack_client));
510 lash_send_event(lash_client, event);
512 lash_jack_client_name(lash_client, jack_get_client_name(jack_client));
513 #endif
515 err = jack_set_process_callback(jack_client, process_callback, 0);
516 if (err) {
517 g_critical("Could not register JACK process callback.");
518 exit(EX_UNAVAILABLE);
521 if (use_transport) {
522 err = jack_set_sync_callback(jack_client, sync_callback, 0);
523 if (err) {
524 g_critical("Could not register JACK sync callback.");
525 exit(EX_UNAVAILABLE);
527 #if 0
528 err = jack_set_timebase_callback(jack_client, 1, timebase_callback, 0);
529 if (err) {
530 g_critical("Could not register JACK timebase callback.");
531 exit(EX_UNAVAILABLE);
533 #endif
536 assert(smf->number_of_tracks >= 1);
538 /* We are allocating number_of_tracks + 1 output ports. */
539 for (i = 0; i <= smf->number_of_tracks; i++) {
540 char port_name[32];
542 if (i == 0)
543 snprintf(port_name, sizeof(port_name), "midi_out");
544 else
545 snprintf(port_name, sizeof(port_name), "track_%d_midi_out", i);
547 output_ports[i] = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
549 if (output_ports[i] == NULL) {
550 g_critical("Could not register JACK output port '%s'.", port_name);
551 exit(EX_UNAVAILABLE);
554 if (just_one_output)
555 break;
558 if (jack_activate(jack_client)) {
559 g_critical("Cannot activate JACK client.");
560 exit(EX_UNAVAILABLE);
564 #ifdef HAVE_LASH
566 static gboolean
567 lash_callback(gpointer notused)
569 lash_event_t *event;
571 while ((event = lash_get_event(lash_client))) {
572 switch (lash_event_get_type(event)) {
573 case LASH_Restore_Data_Set:
574 case LASH_Save_Data_Set:
575 break;
577 case LASH_Quit:
578 g_warning("Exiting due to LASH request.");
579 ctrl_c_pressed = 1;
580 break;
582 default:
583 g_warning("Receieved unknown LASH event of type %d.", lash_event_get_type(event));
584 lash_event_destroy(event);
588 return TRUE;
591 static void
592 init_lash(lash_args_t *args)
594 /* XXX: Am I doing the right thing wrt protocol version? */
595 lash_client = lash_init(args, PROGRAM_NAME, LASH_Config_Data_Set, LASH_PROTOCOL(2, 0));
597 if (!lash_server_connected(lash_client)) {
598 g_critical("Cannot initialize LASH. Continuing anyway.");
599 /* exit(EX_UNAVAILABLE); */
601 return;
604 /* Schedule a function to process LASH events, ten times per second. */
605 g_timeout_add(100, lash_callback, NULL);
608 #endif /* HAVE_LASH */
611 * This is neccessary for exiting due to jackd being killed, when exit(0)
612 * in process_callback won't get called for obvious reasons.
614 gboolean
615 emergency_exit_timeout(gpointer notused)
617 if (ctrl_c_pressed == 0)
618 return TRUE;
620 exit(0);
623 void
624 ctrl_c_handler(int signum)
626 ctrl_c_pressed = 1;
629 static void
630 log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer notused)
632 fprintf(stderr, "%s: %s\n", log_domain, message);
635 static void
636 show_version(void)
638 fprintf(stdout, "%s %s, libsmf %s\n", PROGRAM_NAME, PROGRAM_VERSION, smf_get_version());
640 exit(EX_OK);
643 static void
644 usage(void)
646 fprintf(stderr, "usage: jack-smf-player [-dnqstV] [ -a <input port>] [-r <rate>] file_name\n");
648 exit(EX_USAGE);
651 int
652 main(int argc, char *argv[])
654 int ch;
655 char *file_name, *autoconnect_port_name = NULL;
657 #ifdef HAVE_LASH
658 lash_args_t *lash_args;
659 #endif
661 g_thread_init(NULL);
663 #ifdef HAVE_LASH
664 lash_args = lash_extract_args(&argc, &argv);
665 #endif
667 g_log_set_default_handler(log_handler, NULL);
669 while ((ch = getopt(argc, argv, "a:dnqr:stV")) != -1) {
670 switch (ch) {
671 case 'a':
672 autoconnect_port_name = strdup(optarg);
673 break;
675 case 'd':
676 drop_messages = 1;
677 break;
679 case 'n':
680 start_stopped = 1;
681 break;
683 case 'q':
684 be_quiet = 1;
685 break;
687 case 'r':
688 rate_limit = strtod(optarg, NULL);
689 if (rate_limit <= 0.0) {
690 g_critical("Invalid rate limit specified.\n");
692 exit(EX_USAGE);
695 break;
697 case 's':
698 just_one_output = 1;
699 break;
701 case 't':
702 use_transport = 0;
703 break;
705 case 'V':
706 show_version();
707 break;
709 case '?':
710 default:
711 usage();
715 argc -= optind;
716 argv += optind;
718 if (argv[0] == NULL) {
719 g_critical("No file name given.");
720 usage();
723 file_name = argv[0];
725 smf = smf_load(file_name);
727 if (smf == NULL) {
728 g_critical("Loading SMF file failed.");
730 exit(-1);
733 if (!be_quiet)
734 g_message("%s.", smf_decode(smf));
736 if (smf->number_of_tracks > MAX_NUMBER_OF_TRACKS) {
737 g_warning("Number of tracks (%d) exceeds maximum for per-track output; implying '-s' option.", smf->number_of_tracks);
738 just_one_output = 1;
741 #ifdef HAVE_LASH
742 init_lash(lash_args);
743 #endif
745 g_timeout_add(1000, emergency_exit_timeout, (gpointer)0);
746 signal(SIGINT, ctrl_c_handler);
748 init_jack();
750 if (autoconnect_port_name) {
751 if (connect_to_input_port(autoconnect_port_name)) {
752 g_critical("Couldn't connect to '%s', exiting.", autoconnect_port_name);
753 exit(EX_UNAVAILABLE);
757 if (use_transport && !start_stopped) {
758 jack_transport_locate(jack_client, 0);
759 jack_transport_start(jack_client);
762 if (!use_transport)
763 playback_started = jack_frame_time(jack_client);
765 g_main_loop_run(g_main_loop_new(NULL, TRUE));
767 /* Not reached. */
769 return 0;