3 Ann Hell Ex Machina - Music Software
4 Copyright (C) 2003/2007 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
34 #include <sys/types.h>
45 song_ev_type type
; /* event type */
46 int msecs
; /* time in milliseconds */
47 int trk_id
; /* track id */
48 int channel
; /* MIDI channel */
49 int note
; /* MIDI note */
50 int vel
; /* velocity (volume) */
51 int program
; /* MIDI program number */
54 /* the MIDI song stream */
56 static struct midi_ev
*midi_song
= NULL
;
57 static int n_midi_ev
= 0;
59 /* the MIDI tracks: just a track/channel table */
61 #define MIDI_TRACK_NUM 256
62 static int track_channel
[MIDI_TRACK_NUM
];
64 /* MIDI message types */
66 #define MIDI_MSG_NOTE_ON 0x90
67 #define MIDI_MSG_NOTE_OFF 0x80
68 #define MIDI_MSG_CONTROLLER 0xB0
69 #define MIDI_MSG_PROGRAM 0xC0
79 static void add_midi_ev(struct midi_ev
*e
)
80 /* adds a MIDI song event */
85 GROW(midi_song
, n_midi_ev
, struct midi_ev
);
88 memcpy(&midi_song
[n_midi_ev
], e
, sizeof(struct midi_ev
));
94 static int midi_ev_cmp_by_time(const void *v1
, const void *v2
)
95 /* MIDI song event compare function for qsort(), by time (for playing) */
100 e1
= (struct midi_ev
*) v1
;
101 e2
= (struct midi_ev
*) v2
;
103 if (e1
->msecs
== e2
->msecs
)
104 return e1
->type
- e2
->type
;
106 return e1
->msecs
- e2
->msecs
;
110 static void midi_song_convert_events(void)
111 /* converts generic song_ev events to MIDI events */
115 int msecs
, msecs_ac
, f_msecs
;
116 double time_ac
, time_ac_m
;
121 /* resets the MIDI stream */
122 if (midi_song
!= NULL
) {
133 msecs
= msecs_ac
= f_msecs
= 0;
134 time_ac
= time_ac_m
= 0;
137 /* by default, all channels are 'mute' until
138 a channel is explicitly set */
139 for (n
= 0; n
< MIDI_TRACK_NUM
; n
++)
140 track_channel
[n
] = -1;
142 /* travels the song events generating MIDI song events */
143 for (n
= 0; n
< n_song_ev
; n
++) {
144 /* gets the song event */
147 /* calculates the msecs */
148 msecs
= ((e
->generic
.time
- time_ac
) * mspw
) + msecs_ac
;
150 /* generic event data */
151 me
.type
= e
->generic
.type
;
153 me
.trk_id
= e
->generic
.trk_id
;
155 /* if it's not a generic message, set MIDI channel */
156 if (e
->generic
.trk_id
>= 0)
157 me
.channel
= track_channel
[e
->generic
.trk_id
];
159 switch (e
->generic
.type
) {
162 /* updates accumulations */
164 time_ac
= e
->generic
.time
;
166 /* calculates milliseconds-per-whole based on new tempo */
167 mspw
= 1000.0 * 60.0;
168 mspw
/= (e
->tempo
.tempo
/ 4.0);
174 /* just store the values */
177 time_ac_m
= e
->meter
.time
;
181 case SONG_EV_MEASURE
:
183 song_test_measure_boundary(e
->measure
.time
- time_ac_m
,
184 num
, den
, e
->measure
.line
);
187 case SONG_EV_MIDI_CHANNEL
:
189 /* stores the channel for this track */
190 track_channel
[e
->midi_channel
.trk_id
] = e
->midi_channel
.channel
;
195 /* convert to note on / off pairs */
197 me
.type
= SONG_EV_NOTE_ON
;
198 me
.note
= e
->note
.note
;
199 me
.vel
= (int) (e
->note
.vol
* 127.0);
203 msecs
+= (int) (e
->note
.len
* mspw
);
205 me
.type
= SONG_EV_NOTE_OFF
;
213 /* move the cursor back */
214 msecs_ac
-= (int) (e
->back
.len
* mspw
);
217 case SONG_EV_MIDI_PROGRAM
:
219 /* set MIDI program (instrument) */
220 me
.program
= e
->midi_program
.program
;
225 case SONG_EV_SS_PITCH_STRETCH
:
226 case SONG_EV_SS_PRINT_WAVE_TEMPO
:
229 case SONG_EV_SS_SUSTAIN
:
230 case SONG_EV_SS_ATTACK
:
231 case SONG_EV_SS_VIBRATO
:
232 case SONG_EV_SS_PORTAMENTO
:
233 case SONG_EV_SS_CHANNEL
:
234 case SONG_EV_SS_EFF_DELAY
:
235 case SONG_EV_SS_EFF_ECHO
:
236 case SONG_EV_SS_EFF_COMB
:
237 case SONG_EV_SS_EFF_ALLPASS
:
238 case SONG_EV_SS_EFF_FLANGER
:
239 case SONG_EV_SS_EFF_WOBBLE
:
240 case SONG_EV_SS_EFF_SQWOBBLE
:
241 case SONG_EV_SS_EFF_HFWOBBLE
:
242 case SONG_EV_SS_EFF_FADER
:
243 case SONG_EV_SS_EFF_REVERB
:
244 case SONG_EV_SS_EFF_FOLDBACK
:
245 case SONG_EV_SS_EFF_ATAN
:
246 case SONG_EV_SS_EFF_DISTORT
:
247 case SONG_EV_SS_EFF_OVERDRIVE
:
248 case SONG_EV_SS_EFF_OFF
:
250 /* softsynth events are ignored */
253 case SONG_EV_SONG_INFO
:
255 /* song info should be used */
260 /* end of track; trigger possible cleaning */
263 case SONG_EV_NOTE_ON
:
264 case SONG_EV_NOTE_OFF
:
267 /* never found in generic song streams */
271 /* store the further time seen */
276 /* generates an end of event mark, a time after the last one */
277 me
.type
= SONG_EV_END
;
278 me
.msecs
= f_msecs
+ 1000;
283 int midi_song_play(int skip_secs
)
286 int msecs
, msecs_p
, msecs_d
;
289 unsigned char midimsg
[1024];
293 /* convert the song to MIDI events */
294 midi_song_convert_events();
297 qsort(midi_song
, n_midi_ev
, sizeof(struct midi_ev
), midi_ev_cmp_by_time
);
303 /* calculate the millisecond to start playing */
304 skip_msecs
= skip_secs
* 1000;
306 /* loop the events */
312 if (msecs
% 1000 == 0) {
313 int m
= msecs
/ 1000;
314 printf("[%02d:%02d]\r", m
/ 60, m
% 60);
319 /* process all events for this exact time */
320 while (e
->msecs
== msecs
) {
322 case SONG_EV_NOTE_ON
:
324 midimsg
[mi
++] = MIDI_MSG_NOTE_ON
| e
->channel
;
325 midimsg
[mi
++] = e
->note
;
326 midimsg
[mi
++] = e
->vel
;
330 case SONG_EV_NOTE_OFF
:
332 midimsg
[mi
++] = MIDI_MSG_NOTE_OFF
| e
->channel
;
333 midimsg
[mi
++] = e
->note
;
338 case SONG_EV_MIDI_PROGRAM
:
340 midimsg
[mi
++] = MIDI_MSG_PROGRAM
| e
->channel
;
341 midimsg
[mi
++] = e
->program
;
351 /* ignore the rest */
362 /* get time of next event */
365 msecs_d
= msecs
- msecs_p
;
367 if (msecs
>= skip_msecs
) {
368 /* if there are pending messages, write them */
370 write(midi_fd
, midimsg
, mi
);
372 /* calculate the time to sleep */
373 ts
.tv_sec
= (time_t) msecs_d
/ 1000;
374 ts
.tv_nsec
= (long) ((msecs_d
* 1000000) % 1000000000);
376 nanosleep(&ts
, NULL
);
387 int midi_device_open(char *devfile
)
390 devfile
= "/dev/midi";
392 return (midi_fd
= open(devfile
, O_WRONLY
));
396 void midi_device_close(void)