/** * Seeks the SMF to the given position. For example, after seeking to 10 pulses, * smf_get_next_event will return first event that happens after the first ten pulses. * \return 0 if everything went ok, nonzero otherwise. */ int smf_seek_to_pulses(smf_t *smf, int pulses) { smf_event_t *event; assert(pulses >= 0); smf_rewind(smf); #if 0 g_debug("Seeking to %d pulses.", pulses); #endif for (;;) { event = smf_peek_next_event(smf); if (event == NULL) { g_critical("Trying to seek past the end of song."); return (-1); } if (event->time_pulses < pulses) smf_skip_next_event(smf); else break; } smf->last_seek_position = event->time_seconds; return (0); }
/** * Seeks the SMF to the given event. After calling this routine, smf_get_next_event * will return the event that was the second argument of this call. */ int smf_seek_to_event(smf_t *smf, const smf_event_t *target) { smf_event_t *event; smf_rewind(smf); #if 0 g_debug("Seeking to event %d, track %d.", target->event_number, target->track->track_number); #endif for (;;) { event = smf_peek_next_event(smf); /* There can't be NULL here, unless "target" is not in this smf. */ assert(event); if (event != target) smf_skip_next_event(smf); else break; } smf->last_seek_position = event->time_seconds; return (0); }
smf_event_t * get_smf_event (double until_time) { if (Denemo.project == NULL || Denemo.project->movement == NULL || Denemo.project->movement->smf == NULL) return NULL; smf_t *smf = Denemo.project->movement->smf; if (until_time > Denemo.project->movement->end_time) { until_time = Denemo.project->movement->end_time; } for (;;) { smf_event_t *event = smf_peek_next_event (smf); if (event == NULL || event->time_seconds >= until_time) { return NULL; } if (smf_event_is_metadata (event)) { // consume metadata event and continue with the next one event = smf_get_next_event (smf); continue; } // consume the event event = smf_get_next_event (smf); return event; } }
void timebase_callback(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *notused) { double min; /* Minutes since frame 0. */ long abs_tick; /* Ticks since frame 0. */ long abs_beat; /* Beats since frame 0. */ smf_tempo_t *tempo; static smf_tempo_t *previous_tempo = NULL; smf_event_t *event = smf_peek_next_event(smf); if (event == NULL) return; tempo = smf_get_tempo_by_pulses(smf, event->time_pulses); assert(tempo); if (new_pos || previous_tempo != tempo) { pos->valid = JackPositionBBT; pos->beats_per_bar = tempo->numerator; pos->beat_type = 1.0 / (double)tempo->denominator; pos->ticks_per_beat = event->track->smf->ppqn; /* XXX: Is this right? */ pos->beats_per_minute = 60000000.0 / (double)tempo->microseconds_per_quarter_note; min = pos->frame / ((double) pos->frame_rate * 60.0); abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat; abs_beat = abs_tick / pos->ticks_per_beat; pos->bar = abs_beat / pos->beats_per_bar; pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1; pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat); pos->bar_start_tick = pos->bar * pos->beats_per_bar * pos->ticks_per_beat; pos->bar++; /* adjust start to bar 1 */ previous_tempo = tempo; } else { /* Compute BBT info based on previous period. */ pos->tick += nframes * pos->ticks_per_beat * pos->beats_per_minute / (pos->frame_rate * 60); while (pos->tick >= pos->ticks_per_beat) { pos->tick -= pos->ticks_per_beat; if (++pos->beat > pos->beats_per_bar) { pos->beat = 1; ++pos->bar; pos->bar_start_tick += pos->beats_per_bar * pos->ticks_per_beat; } } } }
//finds the first note which comes ON after the passed time DenemoObject * get_obj_for_start_time (smf_t * smf, gdouble time) { if (time < 0.0) time = 0.0; static smf_event_t *event; smf_event_t *initial = smf_peek_next_event (smf); gdouble total = smf_get_length_seconds (smf); time = (time > total ? total : time); if(smf_seek_to_seconds (smf, time)) g_debug("smf_seek_to_seconds failed"); do { event = smf_get_next_event (smf); } while (event && (((event->midi_buffer[0] & 0xF0) == MIDI_NOTE_OFF) || !event->user_pointer)); if (initial && smf_seek_to_event (smf, initial)) g_debug("smf_seek_to_event failed"); //if (event) g_debug("sought for endObj %f found %f\n", time, event->time_seconds); if (event) return (DenemoObject *) (event->user_pointer); return get_object_for_time (time, TRUE); }
/** * Seeks the SMF to the given position. For example, after seeking to 1.0 seconds, * smf_get_next_event will return first event that happens after the first second of song. * \return 0 if everything went ok, nonzero otherwise. */ int smf_seek_to_seconds(smf_t *smf, double seconds) { smf_event_t *event; assert(seconds >= 0.0); if (seconds == smf->last_seek_position) { #if 0 g_debug("Avoiding seek to %f seconds.", seconds); #endif return (0); } smf_rewind(smf); #if 0 g_debug("Seeking to %f seconds.", seconds); #endif for (;;) { event = smf_peek_next_event(smf); if (event == NULL) { g_critical("Trying to seek past the end of song."); return (-1); } if (event->time_seconds < seconds) smf_skip_next_event(smf); else break; } smf->last_seek_position = seconds; return (0); }
//finds the first note which comes OFF after the passed time DenemoObject * get_obj_for_end_time (smf_t * smf, gdouble time) { if (time < 0.0) time = 0.0; static smf_event_t *event = NULL; smf_event_t *initial = smf_peek_next_event (smf); gdouble total = smf_get_length_seconds (smf); time = (time > total ? total : time); if(smf_seek_to_seconds (smf, time)) g_debug("smf_seek_to_seconds failed"); do { event = smf_get_next_event (smf); } while (event && (((event->midi_buffer[0] & 0xF0) == MIDI_NOTE_ON) || !event->user_pointer)); if (initial && smf_seek_to_event (smf, initial)) g_debug("smf_seek_to_event failed");//if (event) g_debug("sought for startObj %f found %f\n", time, event->time_seconds); if (event) return (DenemoObject *) (event->user_pointer); //midi is generated by LilyPond, no user_pointer, get timings from events.txt return get_object_for_time (time, FALSE); }
static void process_midi_output(jack_nframes_t nframes) { int i, t, bytes_remaining, track_number; unsigned char *buffer, tmp_status; void *port_buffers[MAX_NUMBER_OF_TRACKS]; jack_nframes_t last_frame_time; jack_transport_state_t transport_state; static jack_transport_state_t previous_transport_state = JackTransportStopped; static int previous_playback_started = -1; static int previous_playback_paused = 0; for (i = 0; i <= smf->number_of_tracks; i++) { port_buffers[i] = jack_port_get_buffer(output_ports[i], nframes); if (port_buffers[i] == NULL) { warn_from_jack_thread_context("jack_port_get_buffer failed, cannot send anything."); return; } #ifdef JACK_MIDI_NEEDS_NFRAMES jack_midi_clear_buffer(port_buffers[i], nframes); #else jack_midi_clear_buffer(port_buffers[i]); #endif if (just_one_output) break; } if (ctrl_c_pressed) { send_all_sound_off(port_buffers, nframes); /* The idea here is to exit at the second time process_midi_output gets called. Otherwise, All Sound Off won't be delivered. */ ctrl_c_pressed++; if (ctrl_c_pressed >= 3) exit(0); return; } // g_debug("PROCESS CALLBACK!!!"); if (playback_paused) { if (!previous_playback_paused) { send_all_sound_off(port_buffers, nframes); } previous_playback_paused = playback_paused; return; } previous_playback_paused = playback_paused; if (use_transport) { // if (!ready_to_roll) return; transport_state = jack_transport_query(jack_client, NULL); if (transport_state == JackTransportStopped) { if (previous_transport_state == JackTransportRolling) { send_all_sound_off(port_buffers, nframes); } playback_started = -1; } if (transport_state == JackTransportStarting) { playback_started = -1; } if (transport_state == JackTransportRolling) { if (previous_transport_state != JackTransportRolling) { jack_position_t position; jack_transport_query(jack_client, &position); song_position = position.frame; playback_started = jack_last_frame_time(jack_client); } } previous_transport_state = transport_state; } else { if (playback_started == -1) { if (previous_playback_started >= 0) { send_all_sound_off(port_buffers, nframes); send_sond_end(); } } previous_playback_started = playback_started; } /* End of song already? */ if (playback_started < 0) return; last_frame_time = jack_last_frame_time(jack_client); /* We may push at most one byte per 0.32ms to stay below 31.25 Kbaud limit. */ bytes_remaining = nframes_to_ms(nframes) * rate_limit; double loop_offset = loop_song(smf); for (;;) { smf_event_t *event = smf_peek_next_event(smf); int end_of_song = 0; int is_meta_event = 0; if (event == NULL) { end_of_song = 1; } else if (smf_event_is_metadata(event)) { is_meta_event = 1; char *decoded = smf_event_decode(event); if (decoded) { if (!be_quiet) g_debug("Metadata: %s", decoded); end_of_song = process_meta_event(decoded); free(decoded); } } if (end_of_song) { if (!be_quiet) g_debug("End of song."); playback_started = -1; if (!use_transport) ctrl_c_pressed = 1; break; } if (is_meta_event) { smf_event_t *ret = smf_get_next_event(smf); assert(ret != 0); continue; } bytes_remaining -= event->midi_buffer_length; if (rate_limit > 0.0 && bytes_remaining <= 0) { warn_from_jack_thread_context("Rate limiting in effect."); break; } // t = seconds_to_nframes(event->time_seconds) + playback_started - song_position + nframes - last_frame_time; t = seconds_to_nframes(event->time_seconds + loop_offset) + playback_started - song_position - last_frame_time; /* If computed time is too much into the future, we'll need to send it later. */ if (t >= (int)nframes) break; /* If computed time is < 0, we missed a cycle because of xrun. */ if (t < 0) t = 0; assert(event->track->track_number >= 0 && event->track->track_number <= MAX_NUMBER_OF_TRACKS); /* We will send this event; remove it from the queue. */ smf_event_t *ret = smf_get_next_event(smf); assert(ret != 0); /* First, send it via midi_out. */ track_number = 0; #ifdef JACK_MIDI_NEEDS_NFRAMES buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length, nframes); #else buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length); #endif if (buffer == NULL) { warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST."); break; } memcpy(buffer, event->midi_buffer, event->midi_buffer_length); /* Ignore per-track outputs? */ if (just_one_output) continue; /* Send it via proper output port. */ track_number = event->track->track_number; #ifdef JACK_MIDI_NEEDS_NFRAMES buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length, nframes); #else buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length); #endif if (buffer == NULL) { warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST."); break; } /* Before sending, reset channel to 0. XXX: Not very pretty. */ assert(event->midi_buffer_length >= 1); tmp_status = event->midi_buffer[0]; if (event->midi_buffer[0] >= 0x80 && event->midi_buffer[0] <= 0xEF) event->midi_buffer[0] &= 0xF0; memcpy(buffer, event->midi_buffer, event->midi_buffer_length); event->midi_buffer[0] = tmp_status; } }