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; } }
static int show_event(smf_event_t *event) { int off = 0, i; char *decoded, *type; if (smf_event_is_metadata(event)) type = "Metadata"; else type = "Event"; decoded = smf_event_decode(event); if (decoded == NULL) { decoded = malloc(BUFFER_SIZE); if (decoded == NULL) { g_critical("show_event: malloc failed."); return (-1); } off += snprintf(decoded + off, BUFFER_SIZE - off, "Unknown event:"); for (i = 0; i < event->midi_buffer_length && i < 5; i++) off += snprintf(decoded + off, BUFFER_SIZE - off, " 0x%x", event->midi_buffer[i]); } g_message("%d: %s: %s, %f seconds, %d pulses, %d delta pulses", event->event_number, type, decoded, event->time_seconds, event->time_pulses, event->delta_time_pulses); free(decoded); return (0); }
CAMLprim value ocaml_smf_event_is_metadata(value event) { CAMLparam1(event); int ret; smf_event_t *e = get_event(event); ret = smf_event_is_metadata(e); CAMLreturn(Val_bool(ret)); }
/** * \return Nonzero if event is Tempo Change or Time Signature metaevent. */ int smf_event_is_tempo_change_or_time_signature(const smf_event_t *event) { if (!smf_event_is_metadata(event)) return (0); assert(event->midi_buffer_length >= 2); if (event->midi_buffer[1] == 0x51 || event->midi_buffer[1] == 0x58) return (1); return (0); }
/** * \return Nonzero if event is System Realtime. */ int smf_event_is_system_realtime(const smf_event_t *event) { assert(event->midi_buffer); assert(event->midi_buffer_length > 0); if (smf_event_is_metadata(event)) return (0); if (event->midi_buffer[0] >= 0xF8) return (1); return (0); }
/** * \return 1, if passed a metaevent containing text, that is, Text, Copyright, * Sequence/Track Name, Instrument, Lyric, Marker, Cue Point, Program Name, * or Device Name; 0 otherwise. */ int smf_event_is_textual(const smf_event_t *event) { if (!smf_event_is_metadata(event)) return (0); if (event->midi_buffer_length < 4) return (0); if (event->midi_buffer[3] < 1 || event->midi_buffer[3] > 9) return (0); return (1); }
static int ui_load_state(ui_t *ui, char *filename) { int i; smf_t *smf; smf_event_t *event; smf_track_t *track; jack_nframes_t frame; if (!filename) { ui->filename = ui_get_filename(ui, ".", "Load file: "); } if (ui->filename) { smf = smf_load(ui->filename); if (!smf) { free(ui->filename); ui->filename = NULL; return 0; } ui_send_clear(ui, ui->pattern); for (i = 0; i < 8; i++) { track = smf_get_track_by_number(smf, i + 1); if (!track) { continue; } while ((event = smf_track_get_next_event(track))) { if (smf_event_is_metadata(event) || event->midi_buffer[0] == NOTEOFF) { continue; } frame = ui_seconds_to_nframes(ui, event->time_seconds); ui_send_add(ui, event->midi_buffer[1], event->midi_buffer[2], frame ? frame / (88200 / ui->steps) + 1 : 0, MAX_LEN, i); } } smf_delete(smf); } return 1; }
/** * \return Textual representation of the event given, or NULL, if event is unknown. * Returned string looks like this: * * Note On, channel 1, note F#3, velocity 0 * * You should free the returned string afterwards, using free(3). */ char * smf_event_decode(const smf_event_t *event) { int off = 0, channel; char *buf, note[5]; if (smf_event_is_metadata(event)) return (smf_event_decode_metadata(event)); if (smf_event_is_system_realtime(event)) return (smf_event_decode_system_realtime(event)); if (smf_event_is_system_common(event)) return (smf_event_decode_system_common(event)); if (!smf_event_length_is_valid(event)) { g_critical("smf_event_decode: incorrect MIDI message length."); return (NULL); } buf = (char*)malloc(BUFFER_SIZE); if (buf == NULL) { g_critical("smf_event_decode: malloc failed."); return (NULL); } /* + 1, because user-visible channels used to be in range <1-16>. */ channel = (event->midi_buffer[0] & 0x0F) + 1; switch (event->midi_buffer[0] & 0xF0) { case 0x80: note_from_int(note, event->midi_buffer[1]); off += snprintf(buf + off, BUFFER_SIZE - off, "Note Off, channel %d, note %s, velocity %d", channel, note, event->midi_buffer[2]); break; case 0x90: note_from_int(note, event->midi_buffer[1]); off += snprintf(buf + off, BUFFER_SIZE - off, "Note On, channel %d, note %s, velocity %d", channel, note, event->midi_buffer[2]); break; case 0xA0: note_from_int(note, event->midi_buffer[1]); off += snprintf(buf + off, BUFFER_SIZE - off, "Aftertouch, channel %d, note %s, pressure %d", channel, note, event->midi_buffer[2]); break; case 0xB0: off += snprintf(buf + off, BUFFER_SIZE - off, "Controller, channel %d, controller %d, value %d", channel, event->midi_buffer[1], event->midi_buffer[2]); break; case 0xC0: off += snprintf(buf + off, BUFFER_SIZE - off, "Program Change, channel %d, controller %d", channel, event->midi_buffer[1]); break; case 0xD0: off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Pressure, channel %d, pressure %d", channel, event->midi_buffer[1]); break; case 0xE0: off += snprintf(buf + off, BUFFER_SIZE - off, "Pitch Wheel, channel %d, value %d", channel, ((int)event->midi_buffer[2] << 7) | (int)event->midi_buffer[2]); break; default: free(buf); return (NULL); } assert(off <= BUFFER_SIZE); return (buf); }
static char * smf_event_decode_metadata(const smf_event_t *event) { int off = 0, mspqn, flats, isminor; char *buf; static const char *const major_keys[] = {"Fb", "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#"}; static const char *const minor_keys[] = {"Dbm", "Abm", "Ebm", "Bbm", "Fm", "Cm", "Gm", "Dm", "Am", "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m", "E#m"}; assert(smf_event_is_metadata(event)); switch (event->midi_buffer[1]) { case 0x01: return (smf_event_decode_textual(event, "Text")); case 0x02: return (smf_event_decode_textual(event, "Copyright")); case 0x03: return (smf_event_decode_textual(event, "Sequence/Track Name")); case 0x04: return (smf_event_decode_textual(event, "Instrument")); case 0x05: return (smf_event_decode_textual(event, "Lyric")); case 0x06: return (smf_event_decode_textual(event, "Marker")); case 0x07: return (smf_event_decode_textual(event, "Cue Point")); case 0x08: return (smf_event_decode_textual(event, "Program Name")); case 0x09: return (smf_event_decode_textual(event, "Device (Port) Name")); default: break; } buf = (char*)malloc(BUFFER_SIZE); if (buf == NULL) { g_critical("smf_event_decode_metadata: malloc failed."); return (NULL); } switch (event->midi_buffer[1]) { case 0x00: off += snprintf(buf + off, BUFFER_SIZE - off, "Sequence number"); break; /* http://music.columbia.edu/pipermail/music-dsp/2004-August/061196.html */ case 0x20: if (event->midi_buffer_length < 4) { g_critical("smf_event_decode_metadata: truncated MIDI message."); goto error; } off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Prefix: %d", event->midi_buffer[3]); break; case 0x21: if (event->midi_buffer_length < 4) { g_critical("smf_event_decode_metadata: truncated MIDI message."); goto error; } off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Port: %d", event->midi_buffer[3]); break; case 0x2F: off += snprintf(buf + off, BUFFER_SIZE - off, "End Of Track"); break; case 0x51: if (event->midi_buffer_length < 6) { g_critical("smf_event_decode_metadata: truncated MIDI message."); goto error; } mspqn = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5]; off += snprintf(buf + off, BUFFER_SIZE - off, "Tempo: %d microseconds per quarter note, %.2f BPM", mspqn, 60000000.0 / (double)mspqn); break; case 0x54: off += snprintf(buf + off, BUFFER_SIZE - off, "SMPTE Offset"); break; case 0x58: if (event->midi_buffer_length < 7) { g_critical("smf_event_decode_metadata: truncated MIDI message."); goto error; } off += snprintf(buf + off, BUFFER_SIZE - off, "Time Signature: %d/%d, %d clocks per click, %d notated 32nd notes per quarter note", event->midi_buffer[3], (int)pow((double)2, event->midi_buffer[4]), event->midi_buffer[5], event->midi_buffer[6]); break; case 0x59: if (event->midi_buffer_length < 5) { g_critical("smf_event_decode_metadata: truncated MIDI message."); goto error; } flats = event->midi_buffer[3]; isminor = event->midi_buffer[4]; if (isminor != 0 && isminor != 1) { g_critical("smf_event_decode_metadata: last byte of the Key Signature event has invalid value %d.", isminor); goto error; } off += snprintf(buf + off, BUFFER_SIZE - off, "Key Signature: "); if (flats > 8 && flats < 248) { off += snprintf(buf + off, BUFFER_SIZE - off, "%d %s, %s key", abs((int8_t)flats), flats > 127 ? "flats" : "sharps", isminor ? "minor" : "major"); } else { int i = (flats - 248) & 255; assert(i >= 0 && (size_t)i < sizeof(minor_keys) / sizeof(*minor_keys)); assert(i >= 0 && (size_t)i < sizeof(major_keys) / sizeof(*major_keys)); if (isminor) off += snprintf(buf + off, BUFFER_SIZE - off, "%s", minor_keys[i]); else off += snprintf(buf + off, BUFFER_SIZE - off, "%s", major_keys[i]); } break; case 0x7F: off += snprintf(buf + off, BUFFER_SIZE - off, "Proprietary (aka Sequencer) Event, length %zu", event->midi_buffer_length); break; default: goto error; } assert (off <= BUFFER_SIZE); return (buf); error: free(buf); return (NULL); }
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; } }