3 Ann Hell Ex Machina - Music Software
4 Copyright (C) 2003/2005 Angel Ortega <angel@triptico.com>
6 midi_song.c - MIDI song event stream management
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 http://www.triptico.com
41 struct midi_ev_generic
43 song_ev_type type
; /* event type */
44 int msecs
; /* time in milliseconds */
45 int trk_id
; /* track id */
46 int channel
; /* MIDI channel */
49 struct midi_ev_program
51 song_ev_type type
; /* SONG_EV_MIDI_PROGRAM */
55 int program
; /* MIDI program number */
58 struct midi_ev_note_off
60 song_ev_type type
; /* SONG_EV_NOTE_OFF */
64 int note
; /* MIDI note */
67 struct midi_ev_note_on
69 song_ev_type type
; /* SONG_EV_NOTE_ON */
73 int note
; /* MIDI note */
74 int vel
; /* velocity (volume) */
79 struct midi_ev_generic generic
;
80 struct midi_ev_program midi_program
;
81 struct midi_ev_note_on note_on
;
82 struct midi_ev_note_off note_off
;
86 /* the MIDI song stream */
88 static union midi_ev
* midi_song
=NULL
;
89 static int n_midi_ev
=0;
91 /* the MIDI tracks: just a track/channel table */
93 #define MIDI_TRACK_NUM 256
94 static int track_channel
[MIDI_TRACK_NUM
];
96 /* MIDI message types */
98 #define MIDI_MSG_NOTE_ON 0x90
99 #define MIDI_MSG_NOTE_OFF 0x80
100 #define MIDI_MSG_CONTROLLER 0xB0
101 #define MIDI_MSG_PROGRAM 0xC0
109 ********************/
111 static void add_midi_ev(union midi_ev
* e
)
112 /* adds a MIDI song event */
115 midi_song
=(union midi_ev
*)realloc(midi_song
,
116 (n_midi_ev
+ 1) * sizeof(union midi_ev
));
119 memcpy(&midi_song
[n_midi_ev
], e
, sizeof(union midi_ev
));
125 static int midi_ev_cmp_by_time(const void * v1
, const void * v2
)
126 /* MIDI song event compare function for qsort(), by time (for playing) */
128 struct midi_ev_generic
* e1
;
129 struct midi_ev_generic
* e2
;
131 e1
=(struct midi_ev_generic
*)v1
; e2
=(struct midi_ev_generic
*)v2
;
133 if(e1
->msecs
== e2
->msecs
)
134 return(e1
->type
- e2
->type
);
136 return(e1
->msecs
- e2
->msecs
);
140 static int midi_song_convert_events(void)
141 /* converts generic song_ev events to MIDI events */
152 /* resets the MIDI stream */
153 if(midi_song
!= NULL
)
169 /* sets the default track channels sparsely */
170 for(n
=0;n
< MIDI_TRACK_NUM
;n
++)
171 track_channel
[n
]=n
% 16;
173 /* travels the song events generating MIDI song events */
174 for(n
=0;n
< n_song_ev
;n
++)
176 /* gets the song event */
179 /* calculates the msecs */
180 msecs
=((e
->generic
.time
- time_ac
) * mspw
) + msecs_ac
;
182 /* generic event data */
183 me
.generic
.type
=e
->generic
.type
;
184 me
.generic
.msecs
=msecs
;
185 me
.generic
.trk_id
=e
->generic
.trk_id
;
187 /* if it's not a generic message, set MIDI channel */
188 if(e
->generic
.trk_id
>= 0)
189 me
.generic
.channel
=track_channel
[e
->generic
.trk_id
];
191 /* account the biggest track number seen */
192 if(b_track
< e
->generic
.trk_id
) b_track
=e
->generic
.trk_id
;
194 switch(e
->generic
.type
)
198 /* updates accumulations */
200 time_ac
+= e
->generic
.time
;
202 /* calculates milliseconds-per-whole based on new tempo */
203 mspw
= 1000.0 * 60.0;
204 mspw
/= (e
->tempo
.tempo
/ 4.0);
210 /* just store the values */
216 case SONG_EV_MEASURE
:
218 printf("measure boundary check (must be 0): %d\n",
219 ((int) (e
->generic
.time
* den
)) % num
);
223 case SONG_EV_MIDI_CHANNEL
:
225 /* stores the channel for this track */
226 track_channel
[e
->midi_channel
.trk_id
]=
227 e
->midi_channel
.channel
- 1;
232 /* convert to note on / off pairs */
234 me
.note_on
.type
=SONG_EV_NOTE_ON
;
235 me
.note_on
.note
=e
->note
.note
;
236 me
.note_on
.vel
=(int)(e
->note
.vol
* 127.0);
240 msecs
+= (int)(e
->note
.len
* mspw
);
242 me
.note_off
.type
=SONG_EV_NOTE_OFF
;
243 me
.note_off
.msecs
=msecs
;
248 case SONG_EV_MIDI_PROGRAM
:
250 /* set MIDI program (instrument) */
251 me
.midi_program
.program
=e
->midi_program
.program
;
256 case SONG_EV_SS_PITCH_STRETCH
:
259 case SONG_EV_SS_SUSTAIN
:
260 case SONG_EV_SS_VIBRATO
:
261 case SONG_EV_SS_CHANNEL
:
262 case SONG_EV_SS_EFF_DELAY
:
263 case SONG_EV_SS_EFF_ECHO
:
264 case SONG_EV_SS_EFF_COMB
:
265 case SONG_EV_SS_EFF_ALLPASS
:
266 case SONG_EV_SS_EFF_FLANGER
:
267 case SONG_EV_SS_EFF_WOBBLE
:
268 case SONG_EV_SS_EFF_SQWOBBLE
:
269 case SONG_EV_SS_EFF_FADER
:
270 case SONG_EV_SS_EFF_REVERB
:
271 case SONG_EV_SS_EFF_OFF
:
273 /* softsynth events are ignored */
276 case SONG_EV_NOTE_ON
:
277 case SONG_EV_NOTE_OFF
:
280 /* never found in generic song streams */
285 /* generates an end of event mark, a time after the last one */
286 me
.generic
.type
=SONG_EV_END
;
287 me
.generic
.msecs
=msecs
+ 1000;
290 /* return the number of tracks */
295 int midi_song_play(void)
298 int msecs
, msecs_p
, msecs_d
;
302 unsigned char midimsg
[10];
304 /* convert the song to MIDI events */
305 n_tracks
=midi_song_convert_events();
308 qsort(midi_song
, n_midi_ev
, sizeof(union midi_ev
), midi_ev_cmp_by_time
);
314 /* loop the events */
317 /* process all events for this exact time */
318 while(e
->generic
.msecs
== msecs
)
320 switch(e
->generic
.type
)
322 case SONG_EV_NOTE_ON
:
324 midimsg
[0]=MIDI_MSG_NOTE_ON
|e
->note_on
.channel
;
325 midimsg
[1]=e
->note_on
.note
;
326 midimsg
[2]=e
->note_on
.vel
;
328 write(midi_fd
, midimsg
, 3);
332 case SONG_EV_NOTE_OFF
:
334 midimsg
[0]=MIDI_MSG_NOTE_OFF
|e
->note_off
.channel
;
335 midimsg
[1]=e
->note_off
.note
;
338 write(midi_fd
, midimsg
, 3);
342 case SONG_EV_MIDI_PROGRAM
:
344 midimsg
[0]=MIDI_MSG_PROGRAM
|e
->midi_program
.channel
;
345 midimsg
[1]=e
->midi_program
.program
;
347 write(midi_fd
, midimsg
, 2);
357 /* ignore the rest */
368 /* get time of next event */
370 msecs
=e
->generic
.msecs
;
371 msecs_d
=msecs
- msecs_p
;
373 /* calculate the time to sleep */
374 ts
.tv_sec
=(time_t) msecs_d
/ 1000;
375 ts
.tv_nsec
=(long) ((msecs_d
* 1000000) % 1000000000);
377 nanosleep(&ts
, NULL
);