nuclear@0: /*- nuclear@0: * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa nuclear@0: * All rights reserved. nuclear@0: * nuclear@0: * Redistribution and use in source and binary forms, with or without nuclear@0: * modification, are permitted provided that the following conditions nuclear@0: * are met: nuclear@0: * 1. Redistributions of source code must retain the above copyright nuclear@0: * notice, this list of conditions and the following disclaimer. nuclear@0: * 2. Redistributions in binary form must reproduce the above copyright nuclear@0: * notice, this list of conditions and the following disclaimer in the nuclear@0: * documentation and/or other materials provided with the distribution. nuclear@0: * nuclear@0: * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE nuclear@0: * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, nuclear@0: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY nuclear@0: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL nuclear@0: * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED nuclear@0: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, nuclear@0: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY nuclear@0: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING nuclear@0: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS nuclear@0: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: * nuclear@0: */ nuclear@0: nuclear@0: /** nuclear@0: * \file nuclear@0: * nuclear@0: * Event decoding routines. nuclear@0: * nuclear@0: */ nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #ifdef __MINGW32__ nuclear@0: #include nuclear@0: #else /* ! __MINGW32__ */ nuclear@0: #include nuclear@0: #endif /* ! __MINGW32__ */ nuclear@0: #include nuclear@0: #include "smf.h" nuclear@0: #include "smf_private.h" nuclear@0: nuclear@0: #define BUFFER_SIZE 1024 nuclear@0: nuclear@0: /** nuclear@0: * \return Nonzero if event is metaevent. You should never send metaevents; nuclear@0: * they are not really MIDI messages. They carry information like track title, nuclear@0: * time signature etc. nuclear@0: */ nuclear@0: int nuclear@0: smf_event_is_metadata(const smf_event_t *event) nuclear@0: { nuclear@0: assert(event->midi_buffer); nuclear@0: assert(event->midi_buffer_length > 0); nuclear@0: nuclear@0: if (event->midi_buffer[0] == 0xFF) nuclear@0: return (1); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Nonzero if event is System Realtime. nuclear@0: */ nuclear@0: int nuclear@0: smf_event_is_system_realtime(const smf_event_t *event) nuclear@0: { nuclear@0: assert(event->midi_buffer); nuclear@0: assert(event->midi_buffer_length > 0); nuclear@0: nuclear@0: if (smf_event_is_metadata(event)) nuclear@0: return (0); nuclear@0: nuclear@0: if (event->midi_buffer[0] >= 0xF8) nuclear@0: return (1); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Nonzero if event is System Common. nuclear@0: */ nuclear@0: int nuclear@0: smf_event_is_system_common(const smf_event_t *event) nuclear@0: { nuclear@0: assert(event->midi_buffer); nuclear@0: assert(event->midi_buffer_length > 0); nuclear@0: nuclear@0: if (event->midi_buffer[0] >= 0xF0 && event->midi_buffer[0] <= 0xF7) nuclear@0: return (1); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: /** nuclear@0: * \return Nonzero if event is SysEx message. nuclear@0: */ nuclear@0: int nuclear@0: smf_event_is_sysex(const smf_event_t *event) nuclear@0: { nuclear@0: assert(event->midi_buffer); nuclear@0: assert(event->midi_buffer_length > 0); nuclear@0: nuclear@0: if (event->midi_buffer[0] == 0xF0) nuclear@0: return (1); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: static char * nuclear@0: smf_event_decode_textual(const smf_event_t *event, const char *name) nuclear@0: { nuclear@0: int off = 0; nuclear@0: char *buf, *extracted; nuclear@0: nuclear@0: buf = malloc(BUFFER_SIZE); nuclear@0: if (buf == NULL) { nuclear@0: fg_critical("smf_event_decode_textual: malloc failed."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: extracted = smf_event_extract_text(event); nuclear@0: if (extracted == NULL) { nuclear@0: free(buf); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: snprintf(buf + off, BUFFER_SIZE - off, "%s: %s", name, extracted); nuclear@0: nuclear@0: return (buf); nuclear@0: } nuclear@0: nuclear@0: static char * nuclear@0: smf_event_decode_metadata(const smf_event_t *event) nuclear@0: { nuclear@0: int off = 0, mspqn, flats, isminor; nuclear@0: char *buf; nuclear@0: nuclear@0: static const char *const major_keys[] = {"Fb", "Cb", "Gb", "Db", "Ab", nuclear@0: "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#"}; nuclear@0: nuclear@0: static const char *const minor_keys[] = {"Dbm", "Abm", "Ebm", "Bbm", "Fm", nuclear@0: "Cm", "Gm", "Dm", "Am", "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m", "E#m"}; nuclear@0: nuclear@0: assert(smf_event_is_metadata(event)); nuclear@0: nuclear@0: switch (event->midi_buffer[1]) { nuclear@0: case 0x01: nuclear@0: return (smf_event_decode_textual(event, "Text")); nuclear@0: nuclear@0: case 0x02: nuclear@0: return (smf_event_decode_textual(event, "Copyright")); nuclear@0: nuclear@0: case 0x03: nuclear@0: return (smf_event_decode_textual(event, "Sequence/Track Name")); nuclear@0: nuclear@0: case 0x04: nuclear@0: return (smf_event_decode_textual(event, "Instrument")); nuclear@0: nuclear@0: case 0x05: nuclear@0: return (smf_event_decode_textual(event, "Lyric")); nuclear@0: nuclear@0: case 0x06: nuclear@0: return (smf_event_decode_textual(event, "Marker")); nuclear@0: nuclear@0: case 0x07: nuclear@0: return (smf_event_decode_textual(event, "Cue Point")); nuclear@0: nuclear@0: case 0x08: nuclear@0: return (smf_event_decode_textual(event, "Program Name")); nuclear@0: nuclear@0: case 0x09: nuclear@0: return (smf_event_decode_textual(event, "Device (Port) Name")); nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: buf = malloc(BUFFER_SIZE); nuclear@0: if (buf == NULL) { nuclear@0: fg_critical("smf_event_decode_metadata: malloc failed."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: switch (event->midi_buffer[1]) { nuclear@0: case 0x00: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Sequence number"); nuclear@0: break; nuclear@0: nuclear@0: /* http://music.columbia.edu/pipermail/music-dsp/2004-August/061196.html */ nuclear@0: case 0x20: nuclear@0: if (event->midi_buffer_length < 4) { nuclear@0: fg_critical("smf_event_decode_metadata: truncated MIDI message."); nuclear@0: goto error; nuclear@0: } nuclear@0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Prefix: %d", event->midi_buffer[3]); nuclear@0: break; nuclear@0: nuclear@0: case 0x21: nuclear@0: if (event->midi_buffer_length < 4) { nuclear@0: fg_critical("smf_event_decode_metadata: truncated MIDI message."); nuclear@0: goto error; nuclear@0: } nuclear@0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Port: %d", event->midi_buffer[3]); nuclear@0: break; nuclear@0: nuclear@0: case 0x2F: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "End Of Track"); nuclear@0: break; nuclear@0: nuclear@0: case 0x51: nuclear@0: if (event->midi_buffer_length < 6) { nuclear@0: fg_critical("smf_event_decode_metadata: truncated MIDI message."); nuclear@0: goto error; nuclear@0: } nuclear@0: nuclear@0: mspqn = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5]; nuclear@0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Tempo: %d microseconds per quarter note, %.2f BPM", nuclear@0: mspqn, 60000000.0 / (double)mspqn); nuclear@0: break; nuclear@0: nuclear@0: case 0x54: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "SMPTE Offset"); nuclear@0: break; nuclear@0: nuclear@0: case 0x58: nuclear@0: if (event->midi_buffer_length < 7) { nuclear@0: fg_critical("smf_event_decode_metadata: truncated MIDI message."); nuclear@0: goto error; nuclear@0: } nuclear@0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, nuclear@0: "Time Signature: %d/%d, %d clocks per click, %d notated 32nd notes per quarter note", nuclear@0: event->midi_buffer[3], (int)pow(2, event->midi_buffer[4]), event->midi_buffer[5], nuclear@0: event->midi_buffer[6]); nuclear@0: break; nuclear@0: nuclear@0: case 0x59: nuclear@0: if (event->midi_buffer_length < 5) { nuclear@0: fg_critical("smf_event_decode_metadata: truncated MIDI message."); nuclear@0: goto error; nuclear@0: } nuclear@0: nuclear@0: flats = event->midi_buffer[3]; nuclear@0: isminor = event->midi_buffer[4]; nuclear@0: nuclear@0: if (isminor != 0 && isminor != 1) { nuclear@0: fg_critical("smf_event_decode_metadata: last byte of the Key Signature event has invalid value %d.", isminor); nuclear@0: goto error; nuclear@0: } nuclear@0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Key Signature: "); nuclear@0: nuclear@0: if (flats > 8 && flats < 248) { nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "%d %s, %s key", abs((int8_t)flats), nuclear@0: flats > 127 ? "flats" : "sharps", isminor ? "minor" : "major"); nuclear@0: } else { nuclear@0: int i = (flats - 248) & 255; nuclear@0: nuclear@0: assert(i >= 0 && i < sizeof(minor_keys) / sizeof(*minor_keys)); nuclear@0: assert(i >= 0 && i < sizeof(major_keys) / sizeof(*major_keys)); nuclear@0: nuclear@0: if (isminor) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "%s", minor_keys[i]); nuclear@0: else nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "%s", major_keys[i]); nuclear@0: } nuclear@0: nuclear@0: break; nuclear@0: nuclear@0: case 0x7F: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Proprietary (aka Sequencer) Event, length %d", nuclear@0: event->midi_buffer_length); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: goto error; nuclear@0: } nuclear@0: nuclear@0: return (buf); nuclear@0: nuclear@0: error: nuclear@0: free(buf); nuclear@0: nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: static char * nuclear@0: smf_event_decode_system_realtime(const smf_event_t *event) nuclear@0: { nuclear@0: int off = 0; nuclear@0: char *buf; nuclear@0: nuclear@0: assert(smf_event_is_system_realtime(event)); nuclear@0: nuclear@0: if (event->midi_buffer_length != 1) { nuclear@0: fg_critical("smf_event_decode_system_realtime: event length is not 1."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: buf = malloc(BUFFER_SIZE); nuclear@0: if (buf == NULL) { nuclear@0: fg_critical("smf_event_decode_system_realtime: malloc failed."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: switch (event->midi_buffer[0]) { nuclear@0: case 0xF8: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Clock (realtime)"); nuclear@0: break; nuclear@0: nuclear@0: case 0xF9: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Tick (realtime)"); nuclear@0: break; nuclear@0: nuclear@0: case 0xFA: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Start (realtime)"); nuclear@0: break; nuclear@0: nuclear@0: case 0xFB: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Continue (realtime)"); nuclear@0: break; nuclear@0: nuclear@0: case 0xFC: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Stop (realtime)"); nuclear@0: break; nuclear@0: nuclear@0: case 0xFE: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Active Sense (realtime)"); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: free(buf); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: return (buf); nuclear@0: } nuclear@0: nuclear@0: static char * nuclear@0: smf_event_decode_sysex(const smf_event_t *event) nuclear@0: { nuclear@0: int off = 0; nuclear@0: char *buf, manufacturer, subid, subid2; nuclear@0: nuclear@0: assert(smf_event_is_sysex(event)); nuclear@0: nuclear@0: if (event->midi_buffer_length < 5) { nuclear@0: fg_critical("smf_event_decode_sysex: truncated MIDI message."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: buf = malloc(BUFFER_SIZE); nuclear@0: if (buf == NULL) { nuclear@0: fg_critical("smf_event_decode_sysex: malloc failed."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: manufacturer = event->midi_buffer[1]; nuclear@0: nuclear@0: if (manufacturer == 0x7F) { nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, realtime, channel %d", event->midi_buffer[2]); nuclear@0: } else if (manufacturer == 0x7E) { nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, non-realtime, channel %d", event->midi_buffer[2]); nuclear@0: } else { nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, manufacturer 0x%x", manufacturer); nuclear@0: nuclear@0: return (buf); nuclear@0: } nuclear@0: nuclear@0: subid = event->midi_buffer[3]; nuclear@0: subid2 = event->midi_buffer[4]; nuclear@0: nuclear@0: if (subid == 0x01) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Header"); nuclear@0: nuclear@0: else if (subid == 0x02) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Data Packet"); nuclear@0: nuclear@0: else if (subid == 0x03) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Request"); nuclear@0: nuclear@0: else if (subid == 0x04 && subid2 == 0x01) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Master Volume"); nuclear@0: nuclear@0: else if (subid == 0x05 && subid2 == 0x01) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Retransmit"); nuclear@0: nuclear@0: else if (subid == 0x05 && subid2 == 0x02) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Request"); nuclear@0: nuclear@0: else if (subid == 0x06 && subid2 == 0x01) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Request"); nuclear@0: nuclear@0: else if (subid == 0x06 && subid2 == 0x02) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Reply"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x00) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x01) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x02) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x03) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request (Bank)"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x04) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Key Based Tuning Dump"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x05) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 1 byte format"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x06) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 2 byte format"); nuclear@0: nuclear@0: else if (subid == 0x08 && subid2 == 0x07) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change (Bank)"); nuclear@0: nuclear@0: else if (subid == 0x09) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", General MIDI %s", subid2 == 0 ? "disable" : "enable"); nuclear@0: nuclear@0: else if (subid == 0x7C) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Wait"); nuclear@0: nuclear@0: else if (subid == 0x7D) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Cancel"); nuclear@0: nuclear@0: else if (subid == 0x7E) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump NAK"); nuclear@0: nuclear@0: else if (subid == 0x7F) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump ACK"); nuclear@0: nuclear@0: else nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, ", Unknown"); nuclear@0: nuclear@0: return (buf); nuclear@0: } nuclear@0: nuclear@0: static char * nuclear@0: smf_event_decode_system_common(const smf_event_t *event) nuclear@0: { nuclear@0: int off = 0; nuclear@0: char *buf; nuclear@0: nuclear@0: assert(smf_event_is_system_common(event)); nuclear@0: nuclear@0: if (smf_event_is_sysex(event)) nuclear@0: return (smf_event_decode_sysex(event)); nuclear@0: nuclear@0: buf = malloc(BUFFER_SIZE); nuclear@0: if (buf == NULL) { nuclear@0: fg_critical("smf_event_decode_system_realtime: malloc failed."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: switch (event->midi_buffer[0]) { nuclear@0: case 0xF1: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "MTC Quarter Frame"); nuclear@0: break; nuclear@0: nuclear@0: case 0xF2: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Song Position Pointer"); nuclear@0: break; nuclear@0: nuclear@0: case 0xF3: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Song Select"); nuclear@0: break; nuclear@0: nuclear@0: case 0xF6: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Tune Request"); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: free(buf); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: return (buf); nuclear@0: } nuclear@0: nuclear@0: static void nuclear@0: note_from_int(char *buf, int note_number) nuclear@0: { nuclear@0: int note, octave; nuclear@0: char *names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; nuclear@0: nuclear@0: octave = note_number / 12 - 1; nuclear@0: note = note_number % 12; nuclear@0: nuclear@0: sprintf(buf, "%s%d", names[note], octave); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Textual representation of the event given, or NULL, if event is unknown. nuclear@0: * Returned string looks like this: nuclear@0: * nuclear@0: * Note On, channel 1, note F#3, velocity 0 nuclear@0: * nuclear@0: * You should free the returned string afterwards, using free(3). nuclear@0: */ nuclear@0: char * nuclear@0: smf_event_decode(const smf_event_t *event) nuclear@0: { nuclear@0: int off = 0, channel; nuclear@0: char *buf, note[5]; nuclear@0: nuclear@0: if (smf_event_is_metadata(event)) nuclear@0: return (smf_event_decode_metadata(event)); nuclear@0: nuclear@0: if (smf_event_is_system_realtime(event)) nuclear@0: return (smf_event_decode_system_realtime(event)); nuclear@0: nuclear@0: if (smf_event_is_system_common(event)) nuclear@0: return (smf_event_decode_system_common(event)); nuclear@0: nuclear@0: if (!smf_event_length_is_valid(event)) { nuclear@0: fg_critical("smf_event_decode: incorrect MIDI message length."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: buf = malloc(BUFFER_SIZE); nuclear@0: if (buf == NULL) { nuclear@0: fg_critical("smf_event_decode: malloc failed."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: /* + 1, because user-visible channels used to be in range <1-16>. */ nuclear@0: channel = (event->midi_buffer[0] & 0x0F) + 1; nuclear@0: nuclear@0: switch (event->midi_buffer[0] & 0xF0) { nuclear@0: case 0x80: nuclear@0: note_from_int(note, event->midi_buffer[1]); nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Note Off, channel %d, note %s, velocity %d", nuclear@0: channel, note, event->midi_buffer[2]); nuclear@0: break; nuclear@0: nuclear@0: case 0x90: nuclear@0: note_from_int(note, event->midi_buffer[1]); nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Note On, channel %d, note %s, velocity %d", nuclear@0: channel, note, event->midi_buffer[2]); nuclear@0: break; nuclear@0: nuclear@0: case 0xA0: nuclear@0: note_from_int(note, event->midi_buffer[1]); nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Aftertouch, channel %d, note %s, pressure %d", nuclear@0: channel, note, event->midi_buffer[2]); nuclear@0: break; nuclear@0: nuclear@0: case 0xB0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Controller, channel %d, controller %d, value %d", nuclear@0: channel, event->midi_buffer[1], event->midi_buffer[2]); nuclear@0: break; nuclear@0: nuclear@0: case 0xC0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Program Change, channel %d, controller %d", nuclear@0: channel, event->midi_buffer[1]); nuclear@0: break; nuclear@0: nuclear@0: case 0xD0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Pressure, channel %d, pressure %d", nuclear@0: channel, event->midi_buffer[1]); nuclear@0: break; nuclear@0: nuclear@0: case 0xE0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "Pitch Wheel, channel %d, value %d", nuclear@0: channel, ((int)event->midi_buffer[2] << 7) | (int)event->midi_buffer[2]); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: free(buf); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: return (buf); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Textual representation of the data extracted from MThd header, or NULL, if something goes wrong. nuclear@0: * Returned string looks like this: nuclear@0: * nuclear@0: * format: 1 (several simultaneous tracks); number of tracks: 4; division: 192 PPQN. nuclear@0: * nuclear@0: * You should free the returned string afterwards, using free(3). nuclear@0: */ nuclear@0: char * nuclear@0: smf_decode(const smf_t *smf) nuclear@0: { nuclear@0: int off = 0; nuclear@0: char *buf; nuclear@0: nuclear@0: buf = malloc(BUFFER_SIZE); nuclear@0: if (buf == NULL) { nuclear@0: fg_critical("smf_event_decode: malloc failed."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "format: %d ", smf->format); nuclear@0: nuclear@0: switch (smf->format) { nuclear@0: case 0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "(single track)"); nuclear@0: break; nuclear@0: nuclear@0: case 1: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "(several simultaneous tracks)"); nuclear@0: break; nuclear@0: nuclear@0: case 2: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "(several independent tracks)"); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "(INVALID FORMAT)"); nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "; number of tracks: %d", smf->number_of_tracks); nuclear@0: nuclear@0: if (smf->ppqn != 0) nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d PPQN", smf->ppqn); nuclear@0: else nuclear@0: off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d FPS, %d resolution", smf->frames_per_second, smf->resolution); nuclear@0: nuclear@0: return (buf); nuclear@0: } nuclear@0: