]> git.donarmstrong.com Git - lilypond.git/blob - python/midi.c
*** empty log message ***
[lilypond.git] / python / midi.c
1 /*
2   midi.c -- implement Python midi parser module
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2001--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7             Jan Nieuwenhuizen <janneke@gnu.org>
8
9 */
10
11 /*
12
13 python
14 import midi
15 s = open ("s.midi").read ()
16 midi.parse_track (s)
17 midi.parse (s)
18
19 */
20
21 #include <Python.h>
22
23 #if 0
24 int x = 0;
25 int *track = &x;
26 #define debug_print(f, args...) fprintf (stderr, "%s:%d: track: %p :" f, __FUNCTION__, __LINE__, *track, ##args)
27 #else
28 #define debug_print(f, args...)
29 #endif
30
31 static PyObject *Midi_error;
32 static PyObject *Midi_warning;
33
34 static PyObject *
35 midi_error (char const *func, char *s)
36 {
37   char*dest = (char*) malloc (sizeof (char) * (strlen (func) + strlen (s) + 1));
38   strcpy (dest, func);
39   strcat (dest, s);
40   PyErr_SetString (Midi_error, dest);
41   free (dest);
42   
43   return 0;
44 }
45
46 static PyObject *
47 midi_warning (char const *s)
48 {
49   PyErr_SetString (Midi_warning, s);
50   return 0;
51 }
52
53
54 typedef struct message {
55   unsigned char msg;
56   char * description;
57 } message_t;
58
59 message_t channelVoiceMessages[] = {
60   0x80, "NOTE_OFF",
61   0x90, "NOTE_ON",
62   0xA0, "POLYPHONIC_KEY_PRESSURE",
63   0xB0, "CONTROLLER_CHANGE",
64   0xC0, "PROGRAM_CHANGE",
65   0xD0, "CHANNEL_KEY_PRESSURE",
66   0xE0, "PITCH_BEND",
67   0,0
68 };
69
70 message_t channelModeMessages[] = {
71   0x78, "ALL_SOUND_OFF",
72   0x79, "RESET_ALL_CONTROLLERS",
73   0x7A, "LOCAL_CONTROL",
74   0x7B, "ALL_NOTES_OFF",
75   0x7C, "OMNI_MODE_OFF",
76   0x7D, "OMNI_MODE_ON",
77   0x7E, "MONO_MODE_ON",
78   0x7F, "POLY_MODE_ON",
79   0,0
80 };
81
82 message_t metaEvents[] = {
83   0x00, "SEQUENCE_NUMBER",
84   0x01, "TEXT_EVENT",
85   0x02, "COPYRIGHT_NOTICE",
86   0x03, "SEQUENCE_TRACK_NAME",
87   0x04, "INSTRUMENT_NAME",
88   0x05, "LYRIC",
89   0x06, "MARKER",
90   0x07, "CUE_POINT",
91   0x20, "MIDI_CHANNEL_PREFIX",
92   0x21, "MIDI_PORT",
93   0x2F, "END_OF_TRACK",
94   0x51, "SET_TEMPO",
95   0x54, "SMTPE_OFFSET",
96   0x58, "TIME_SIGNATURE",
97   0x59, "KEY_SIGNATURE",
98   0x7F, "SEQUENCER_SPECIFIC_META_EVENT",
99   0xFF, "META_EVENT",
100   0,0
101 };
102
103 void
104 add_constants (PyObject *dict)
105 {
106   message_t * p[] = {metaEvents, channelModeMessages, channelVoiceMessages ,0};
107   int i,j;
108   for ( j =0; p[j]; j++)
109     for ( i = 0; p[j][i].description; i++)
110       PyDict_SetItemString (dict, p[j][i].description, Py_BuildValue ("i", p[j][i].msg));
111 }
112
113 unsigned long int
114 get_number (unsigned char ** str, unsigned char * end_str, int length)
115 {
116   /* # MIDI uses big-endian for everything */
117   long sum = 0;
118   int i = 0;
119   
120   for (; i < length; i++)
121     sum = (sum << 8) + (unsigned char) (*str)[i];
122
123   *str += length;
124   debug_print ("%d:\n", sum);
125   return sum;
126 }
127
128 unsigned long int
129 get_variable_length_number (unsigned char **str, unsigned char * end_str)
130 {
131   long sum = 0;
132   int i = 0;
133   while (*str < end_str)
134     {
135       unsigned char x = **str;
136       (*str) ++;
137       sum = (sum << 7) + (x & 0x7F);
138       if (!(x & 0x80))
139         break;
140     }
141   debug_print ("%d:\n", sum);
142   return sum;
143 }
144
145 PyObject *
146 read_one_byte (unsigned char **track, unsigned char *end, 
147                unsigned char x)
148 {
149   PyObject *pyev = Py_BuildValue ("(i)", x);
150   debug_print ("%x:%s", x, "event\n");
151
152   return pyev;
153 }
154
155 PyObject *
156 read_two_bytes (unsigned char **track, unsigned char *end, 
157                 unsigned char x)
158 {
159   PyObject *pyev = Py_BuildValue ("(ii)", x, (*track)[0]);
160   *track += 1;
161   debug_print ("%x:%s", x, "event\n");
162   return pyev;
163 }
164
165 PyObject *
166 read_three_bytes (unsigned char **track, unsigned char *end, 
167                   unsigned char x)
168 {
169   PyObject *pyev = Py_BuildValue ("(iii)", x, (*track)[0],
170                                   (*track)[1]);
171
172   *track += 2;
173   debug_print ("%x:%s", x, "event\n");
174   return pyev;
175 }
176
177 PyObject *
178 read_string (unsigned char **track, unsigned char *end) 
179 {
180   unsigned long length = get_variable_length_number (track, end);
181   if (length > end - *track)
182     length = end - *track;
183
184   *track += length;
185   return Py_BuildValue ("s#", ((*track) -length), length);
186 }
187
188 typedef PyObject* (*Read_midi_event)
189      (unsigned char **track, unsigned char *end, 
190       unsigned char x);
191
192
193 static PyObject *
194 read_f0_byte (unsigned char **track, unsigned char *end, 
195               unsigned char x)
196               
197 {
198   debug_print ("%x:%s", x, "event\n");
199   if (x == 0xff)
200     {
201       unsigned char z = (*track)[0 ];
202       *track += 1;
203       debug_print ("%x:%s", z, "f0-event\n");
204
205       return Py_BuildValue ("(iiO)", x, z, read_string (track, end));
206     }
207
208   return Py_BuildValue ("(iO)", x, read_string (track, end));
209 }
210
211 Read_midi_event read_midi_event [16] =
212 {
213   read_one_byte,  //  0
214   read_one_byte,  // 10
215   read_one_byte,  // 20
216   read_one_byte,  // 30
217   read_one_byte,  // 40
218   read_one_byte,  // 50
219   read_one_byte,  // 60 data entry.
220   read_two_bytes, // 70 all notes off
221   read_three_bytes, // 80 note off
222   read_three_bytes, // 90 note on
223   read_three_bytes, // a0 poly aftertouch
224   read_three_bytes, // b0 control
225   read_two_bytes,  // c0 prog change
226   read_two_bytes, // d0 ch aftertouch
227   read_three_bytes, // e0 pitchwheel range 
228   read_f0_byte,   // f0
229 };
230
231
232 static PyObject *
233 read_event (unsigned char **track, unsigned char *end, PyObject *time,
234             unsigned char *running_status)
235 {
236   int rsb_skip = ((**track & 0x80)) ? 1 :0;
237
238   unsigned char x = (rsb_skip) ? (*track)[0]: *running_status;
239
240   PyObject * bare_event = 0;
241   debug_print ("%x:%s", x, "event\n");
242   *running_status = x;
243   *track += rsb_skip;
244   
245   //  printf ("%x %x %d next %x\n", x, (*track)[0], rsb_skip, (*track)[1]);
246   bare_event = (*read_midi_event[x >> 4]) (track, end, x);
247   if (bare_event)
248     return Py_BuildValue ("(OO)", time, bare_event);
249   else
250     return NULL;
251 }
252
253 static PyObject *
254 midi_parse_track (unsigned char **track, unsigned char *track_end)
255 {
256   unsigned int time = 0;
257   unsigned char running_status;
258   unsigned long track_len, track_size;
259   PyObject *pytrack = 0;
260
261   debug_print ("%s", "\n");
262   
263   track_size = track_end - *track;
264
265   debug_print ("%s", "\n");
266   if (strcmp (*track, "MTrk"))
267     return midi_error (__FUNCTION__,  ": MTrk expected");
268   
269   *track += 4;
270
271   track_len = get_number (track, *track + 4, 4);
272
273
274   debug_print ("track_len: %u\n", track_len);
275   debug_print ("track_size: %u\n", track_size);
276   debug_print ("track begin: %p\n", track);
277   debug_print ("track end: %p\n", track + track_len);
278   
279   if (track_len > track_size)
280     return midi_error (__FUNCTION__,  ": track size corrupt");
281
282   pytrack = PyList_New (0);
283
284   track_end = *track + track_len;
285
286   {  
287     PyObject *pytime = PyInt_FromLong (0L);
288     while (*track < track_end)
289       {
290         long dt = get_variable_length_number(track, track_end);
291         PyObject *pyev = 0;
292
293         time += dt;
294         if (dt)
295           pytime = PyInt_FromLong (time);
296
297         pyev = read_event (track, track_end, pytime,
298                            &running_status);
299         if (pyev)
300           PyList_Append (pytrack, pyev);
301       }
302   }
303   
304   *track = track_end;
305   return pytrack;
306 }
307
308
309 static PyObject *
310 pymidi_parse_track (PyObject *self, PyObject *args)
311 {
312   unsigned char *track, *track_end;
313   unsigned long track_size, track_len;
314
315   PyObject * sobj = PyTuple_GetItem (args, 0);
316
317   debug_print ("%s", "\n");
318   if (!PyArg_ParseTuple (args, "s#", &track, &track_size))
319     return 0;
320
321   if (track_size < 0)
322     return midi_error (__FUNCTION__,   ": negative track size");
323
324   track_end = track + track_size;
325   
326   return midi_parse_track (&track, track_end);
327 }
328
329 static PyObject *
330 midi_parse (unsigned char **midi,unsigned  char *midi_end)
331 {
332   PyObject *pymidi = 0;
333   unsigned long header_len;
334   unsigned format, tracks;
335   int division;
336   int i;
337   
338   debug_print ("%s", "\n");
339
340   /* Header */
341   header_len = get_number (midi, *midi + 4, 4);
342
343   
344   if (header_len < 6)
345     return midi_error (__FUNCTION__,  ": header too short");
346     
347   format = get_number (midi, *midi + 2, 2);
348   tracks = get_number (midi, *midi + 2, 2);
349
350   if (tracks > 32)
351     return midi_error (__FUNCTION__,  ": too many tracks");
352   
353   division = get_number (midi, *midi + 2, 2) * 4;
354
355
356   if (division < 0)
357     /* return midi_error ("can't handle non-metrical time"); */
358     ;
359   *midi += header_len - 6;
360
361   pymidi = PyList_New (0);
362
363   /* Tracks */
364   for (i = 0; i < tracks; i++)
365     PyList_Append (pymidi, midi_parse_track (midi, midi_end));
366
367   pymidi = Py_BuildValue ("(OO)", Py_BuildValue ("(ii)", format, division),
368                           pymidi);
369   return pymidi;
370 }
371
372 static PyObject *
373 pymidi_parse (PyObject *self, PyObject *args)
374 {
375   unsigned char *midi, *midi_end;
376   unsigned long midi_size, midi_len;
377   
378   PyObject *sobj = PyTuple_GetItem (args, 0);
379
380   debug_print ("%s", "\n");
381   if (!PyArg_ParseTuple (args, "s#", &midi, &midi_size))
382     return 0;
383
384   if (strcmp (midi, "MThd"))
385       return midi_error (__FUNCTION__,  ": MThd expected");
386   
387   midi += 4;
388
389   midi_end = midi + midi_size;
390
391   return midi_parse (&midi, midi_end);
392 }
393
394
395 static PyMethodDef MidiMethods[] = 
396 {
397   {"parse",  pymidi_parse, 1},
398   {"parse_track",  pymidi_parse_track, 1},
399   {0, 0}        /* Sentinel */
400 };
401
402 void
403 initmidi ()
404 {
405   PyObject *m, *d;
406   m = Py_InitModule ("midi", MidiMethods);
407   d = PyModule_GetDict (m);
408   
409   Midi_error = PyString_FromString ("midi.error");
410   PyDict_SetItemString (d, "error", Midi_error);
411   add_constants (d);
412   Midi_warning = PyString_FromString ("midi.warning");
413   PyDict_SetItemString (d, "warning", Midi_warning);
414 }