X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=python%2Fmidi.c;h=890798c66a4f022139013c81e641b743756aef15;hb=0b544cfb7332615ef809b71b57ab656741311ae1;hp=05d616070606a9bffb649f8c0f2f9758a38dc5ad;hpb=5d04488bc3a94ee9c84c075874831e31f6a888d2;p=lilypond.git diff --git a/python/midi.c b/python/midi.c index 05d6160706..890798c66a 100644 --- a/python/midi.c +++ b/python/midi.c @@ -1,50 +1,67 @@ /* - midi.c -- implement Python midi parser module + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter - - (c) 2001 Han-Wen Nienhuys + Copyright (C) 2001--2014 Han-Wen Nienhuys Jan Nieuwenhuizen + + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . */ /* -python2 +python import midi s = open ("s.midi").read () midi.parse_track (s) midi.parse (s) + +returns a MIDI file as the tuple + + ((format, division), TRACKLIST) + +each track is an EVENTLIST, where EVENT is + + (time, (type, ARG1, [ARG2])) + */ -#include "config.h" - -/* urg */ -#if HAVE_PYTHON2_PYTHON_H -#include -#elif HAVE_PYTHON2_2_PYTHON_H -#include -#elif HAVE_PYTHON2_1_PYTHON_H -#include -#elif HAVE_PYTHON2_0_PYTHON_H -#include -#elif HAVE_PYTHON1_5_PYTHON_H -#include -#elif HAVE_PYTHON_PYTHON_H -#define assert(x) -#include -#elif HAVE_PYTHON_H -#define assert(x) #include -#else -#error Need Python.h + +char * +compat_itoa (int i) +{ + static char buffer[9]; + snprintf (buffer, 8, "%d", i); + return buffer; +} + +/* PyMIDINIT_FUNC isn't defined in Python < 2.3 */ +#ifndef PyMODINIT_FUNC +# if defined(__cplusplus) +# define PyMODINIT_FUNC extern "C" void +# else /* __cplusplus */ +# define PyMODINIT_FUNC void +# endif /* __cplusplus */ #endif #if 0 int x = 0; int *track = &x; -#define debug_print(f, args...) fprintf (stderr, "%s:%d: track: %p :" f, __FUNCTION__, __LINE__, *track, ##args) +#define urg_debug_print(f, args...) fprintf (stderr, "%s:%d: track: %p: " f, __FUNCTION__, __LINE__, *track, ##args) +#define debug_print(f, args...) fprintf (stderr, f, ##args) #else #define debug_print(f, args...) #endif @@ -53,11 +70,13 @@ static PyObject *Midi_error; static PyObject *Midi_warning; static PyObject * -midi_error (char * func, char *s) +midi_error (char const *func, char *s, char *t) { - char*dest = (char*) malloc (sizeof (char) * (strlen (func) + strlen (s) + 1)); + char *dest = (char*) malloc (sizeof (char) + * (strlen (func) + strlen (s) + strlen (t) + 1)); strcpy (dest, func); strcat (dest, s); + strcat (dest, t); PyErr_SetString (Midi_error, dest); free (dest); @@ -65,7 +84,7 @@ midi_error (char * func, char *s) } static PyObject * -midi_warning (char *s) +midi_warning (char const *s) { PyErr_SetString (Midi_warning, s); return 0; @@ -78,47 +97,47 @@ typedef struct message { } message_t; message_t channelVoiceMessages[] = { - 0x80, "NOTE_OFF", - 0x90, "NOTE_ON", - 0xA0, "POLYPHONIC_KEY_PRESSURE", - 0xB0, "CONTROLLER_CHANGE", - 0xC0, "PROGRAM_CHANGE", - 0xD0, "CHANNEL_KEY_PRESSURE", - 0xE0, "PITCH_BEND", - 0,0 + {0x80, "NOTE_OFF"}, + {0x90, "NOTE_ON"}, + {0xA0, "POLYPHONIC_KEY_PRESSURE"}, + {0xB0, "CONTROLLER_CHANGE"}, + {0xC0, "PROGRAM_CHANGE"}, + {0xD0, "CHANNEL_KEY_PRESSURE"}, + {0xE0, "PITCH_BEND"}, + {0,0} }; message_t channelModeMessages[] = { - 0x78, "ALL_SOUND_OFF", - 0x79, "RESET_ALL_CONTROLLERS", - 0x7A, "LOCAL_CONTROL", - 0x7B, "ALL_NOTES_OFF", - 0x7C, "OMNI_MODE_OFF", - 0x7D, "OMNI_MODE_ON", - 0x7E, "MONO_MODE_ON", - 0x7F, "POLY_MODE_ON", - 0,0 + {0x78, "ALL_SOUND_OFF"}, + {0x79, "RESET_ALL_CONTROLLERS"}, + {0x7A, "LOCAL_CONTROL"}, + {0x7B, "ALL_NOTES_OFF"}, + {0x7C, "OMNI_MODE_OFF"}, + {0x7D, "OMNI_MODE_ON"}, + {0x7E, "MONO_MODE_ON"}, + {0x7F, "POLY_MODE_ON"}, + {0,0} }; message_t metaEvents[] = { - 0x00, "SEQUENCE_NUMBER", - 0x01, "TEXT_EVENT", - 0x02, "COPYRIGHT_NOTICE", - 0x03, "SEQUENCE_TRACK_NAME", - 0x04, "INSTRUMENT_NAME", - 0x05, "LYRIC", - 0x06, "MARKER", - 0x07, "CUE_POINT", - 0x20, "MIDI_CHANNEL_PREFIX", - 0x21, "MIDI_PORT", - 0x2F, "END_OF_TRACK", - 0x51, "SET_TEMPO", - 0x54, "SMTPE_OFFSET", - 0x58, "TIME_SIGNATURE", - 0x59, "KEY_SIGNATURE", - 0x7F, "SEQUENCER_SPECIFIC_META_EVENT", - 0xFF, "META_EVENT", - 0,0 + {0x00, "SEQUENCE_NUMBER"}, + {0x01, "TEXT_EVENT"}, + {0x02, "COPYRIGHT_NOTICE"}, + {0x03, "SEQUENCE_TRACK_NAME"}, + {0x04, "INSTRUMENT_NAME"}, + {0x05, "LYRIC"}, + {0x06, "MARKER"}, + {0x07, "CUE_POINT"}, + {0x20, "MIDI_CHANNEL_PREFIX"}, + {0x21, "MIDI_PORT"}, + {0x2F, "END_OF_TRACK"}, + {0x51, "SET_TEMPO"}, + {0x54, "SMTPE_OFFSET"}, + {0x58, "TIME_SIGNATURE"}, + {0x59, "KEY_SIGNATURE"}, + {0x7F, "SEQUENCER_SPECIFIC_META_EVENT"}, + {0xFF, "META_EVENT"}, + {0,0} }; void @@ -138,11 +157,12 @@ get_number (unsigned char ** str, unsigned char * end_str, int length) long sum = 0; int i = 0; - for (; i < length; i++) + for (; i < length && + ((*str) + i < end_str); i++) sum = (sum << 8) + (unsigned char) (*str)[i]; *str += length; - debug_print ("%d:\n", sum); + debug_print ("%ld:\n", sum); return sum; } @@ -150,7 +170,7 @@ unsigned long int get_variable_length_number (unsigned char **str, unsigned char * end_str) { long sum = 0; - int i = 0; + while (*str < end_str) { unsigned char x = **str; @@ -159,7 +179,7 @@ get_variable_length_number (unsigned char **str, unsigned char * end_str) if (!(x & 0x80)) break; } - debug_print ("%d:\n", sum); + debug_print ("%ld:\n", sum); return sum; } @@ -207,8 +227,8 @@ read_string (unsigned char **track, unsigned char *end) } typedef PyObject* (*Read_midi_event) - (unsigned char **track, unsigned char *end, - unsigned char x); + (unsigned char **track, unsigned char *end, + unsigned char x); static PyObject * @@ -272,10 +292,9 @@ read_event (unsigned char **track, unsigned char *end, PyObject *time, } static PyObject * -midi_parse_track (unsigned char **track, unsigned char *track_end) +midi_parse_track (unsigned char **track, unsigned char *track_end, int clocks_max) { unsigned int time = 0; - unsigned char running_status; unsigned long track_len, track_size; PyObject *pytrack = 0; @@ -284,28 +303,33 @@ midi_parse_track (unsigned char **track, unsigned char *track_end) track_size = track_end - *track; debug_print ("%s", "\n"); - if (strcmp (*track, "MTrk")) - return midi_error (__FUNCTION__, ": MTrk expected"); + if (memcmp (*track, "MTrk", 4)) + { + *track[4] = 0; + return midi_error (__FUNCTION__, ": MTrk expected, got: ", *(char**)track); + } *track += 4; track_len = get_number (track, *track + 4, 4); - - debug_print ("track_len: %u\n", track_len); - debug_print ("track_size: %u\n", track_size); + debug_print ("track_len: %lu\n", track_len); + debug_print ("track_size: %lu\n", track_size); debug_print ("track begin: %p\n", track); debug_print ("track end: %p\n", track + track_len); if (track_len > track_size) - return midi_error (__FUNCTION__, ": track size corrupt"); + return midi_error (__FUNCTION__, ": track length corrupt: ", compat_itoa (track_len)); pytrack = PyList_New (0); - track_end = *track + track_len; + if (*track + track_len < track_end) + track_end = *track + track_len; { PyObject *pytime = PyInt_FromLong (0L); + unsigned char running_status = 0; + while (*track < track_end) { long dt = get_variable_length_number(track, track_end); @@ -314,7 +338,8 @@ midi_parse_track (unsigned char **track, unsigned char *track_end) time += dt; if (dt) pytime = PyInt_FromLong (time); - + if (clocks_max && time > clocks_max) + break; pyev = read_event (track, track_end, pytime, &running_status); if (pyev) @@ -331,24 +356,24 @@ static PyObject * pymidi_parse_track (PyObject *self, PyObject *args) { unsigned char *track, *track_end; - unsigned long track_size, track_len; - - PyObject * sobj = PyTuple_GetItem (args, 0); + int track_size; + int clocks_max; debug_print ("%s", "\n"); - if (!PyArg_ParseTuple (args, "s#", &track, &track_size)) + if (!PyArg_ParseTuple (args, "s#|i", &track, &track_size, &clocks_max)) return 0; + debug_print ("clocks_max: %d\n", clocks_max); if (track_size < 0) - return midi_error (__FUNCTION__, ": negative track size"); + return midi_error (__FUNCTION__, ": negative track size: ", compat_itoa (track_size)); track_end = track + track_size; - return midi_parse_track (&track, track_end); + return midi_parse_track (&track, track_end, clocks_max); } static PyObject * -midi_parse (unsigned char **midi,unsigned char *midi_end) +midi_parse (unsigned char **midi,unsigned char *midi_end, int clocks_max) { PyObject *pymidi = 0; unsigned long header_len; @@ -360,30 +385,31 @@ midi_parse (unsigned char **midi,unsigned char *midi_end) /* Header */ header_len = get_number (midi, *midi + 4, 4); - if (header_len < 6) - return midi_error (__FUNCTION__, ": header too short"); + return midi_error (__FUNCTION__, ": header too short: ", compat_itoa (header_len)); format = get_number (midi, *midi + 2, 2); tracks = get_number (midi, *midi + 2, 2); - if (tracks > 32) - return midi_error (__FUNCTION__, ": too many tracks"); + if (tracks > 256) + return midi_error (__FUNCTION__, ": too many tracks: ", compat_itoa (tracks)); division = get_number (midi, *midi + 2, 2) * 4; + /* if (division < 0) - /* return midi_error ("can't handle non-metrical time"); */ + return midi_error (cannot handle non-metrical time"); ; + */ *midi += header_len - 6; pymidi = PyList_New (0); /* Tracks */ for (i = 0; i < tracks; i++) - PyList_Append (pymidi, midi_parse_track (midi, midi_end)); + PyList_Append (pymidi, midi_parse_track (midi, midi_end, clocks_max)); pymidi = Py_BuildValue ("(OO)", Py_BuildValue ("(ii)", format, division), pymidi); @@ -394,33 +420,36 @@ static PyObject * pymidi_parse (PyObject *self, PyObject *args) { unsigned char *midi, *midi_end; - unsigned long midi_size, midi_len; + unsigned long midi_size; + int clocks_max; - PyObject *sobj = PyTuple_GetItem (args, 0); - debug_print ("%s", "\n"); - if (!PyArg_ParseTuple (args, "s#", &midi, &midi_size)) + if (!PyArg_ParseTuple (args, "s#|i", &midi, &midi_size, &clocks_max)) return 0; + debug_print ("clocks_max: %d\n", clocks_max); + + if (memcmp (midi, "MThd", 4)) + { + midi[4] = 0; + return midi_error (__FUNCTION__, ": MThd expected, got: ", (char*)midi); + } - if (strcmp (midi, "MThd")) - return midi_error (__FUNCTION__, ": MThd expected"); - midi += 4; midi_end = midi + midi_size; - return midi_parse (&midi, midi_end); + return midi_parse (&midi, midi_end, clocks_max); } - static PyMethodDef MidiMethods[] = { - {"parse", pymidi_parse, 1}, - {"parse_track", pymidi_parse_track, 1}, + {"parse", pymidi_parse, METH_VARARGS}, + {"parse_track", pymidi_parse_track, METH_VARARGS}, {0, 0} /* Sentinel */ }; -initmidi () +PyMODINIT_FUNC +initmidi (void) { PyObject *m, *d; m = Py_InitModule ("midi", MidiMethods); @@ -431,4 +460,10 @@ initmidi () add_constants (d); Midi_warning = PyString_FromString ("midi.warning"); PyDict_SetItemString (d, "warning", Midi_warning); + + /* + FIXME. + */ + (void) midi_warning; } +