ugBASIC 1.18
An isomorphic BASIC language compiler for retrocomputers
Loading...
Searching...
No Matches
_music.c
Go to the documentation of this file.
1/*****************************************************************************
2 * ugBASIC - an isomorphic BASIC language compiler for retrocomputers *
3 *****************************************************************************
4 * Copyright 2021-2026 Marco Spedaletti (asimov@mclink.it)
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *----------------------------------------------------------------------------
18 * Concesso in licenza secondo i termini della Licenza Apache, versione 2.0
19 * (la "Licenza"); è proibito usare questo file se non in conformità alla
20 * Licenza. Una copia della Licenza è disponibile all'indirizzo:
21 *
22 * http://www.apache.org/licenses/LICENSE-2.0
23 *
24 * Se non richiesto dalla legislazione vigente o concordato per iscritto,
25 * il software distribuito nei termini della Licenza è distribuito
26 * "COSÌ COM'È", SENZA GARANZIE O CONDIZIONI DI ALCUN TIPO, esplicite o
27 * implicite. Consultare la Licenza per il testo specifico che regola le
28 * autorizzazioni e le limitazioni previste dalla medesima.
29 ****************************************************************************/
30
31/****************************************************************************
32 * INCLUDE SECTION
33 ****************************************************************************/
34
35#include "../../ugbc.h"
36#include "../../libs/midi.h"
37#include "../../libs/sid_file.h"
38#include "../../libs/msc1.h"
39
40/****************************************************************************
41 * CODE SECTION
42 ****************************************************************************/
43
44// SET VOLUME, 2 bytes,
45// FORMAT:
46// 1110cccc -> cccc is hardware audio channel
47// nnnnnnnn -> nnnnnnnn is the value (0...255)
48// #define IMF_SET_VOLUME_CHANNEL( channel ) 0xe0 | ( ( 1 << ( ( channel ) % ( MAX_AUDIO_CHANNELS ) ) ) )
49
50// NOTE OFF, 1 byte, FORMAT: 1110cccc -> cccc is hardware audio channel
51#define IMF_NOTE_OFF( channel ) 0xe0 | ( ( 1 << ( ( channel ) % ( MAX_AUDIO_CHANNELS ) ) ) )
52
53// NOTE ON, 3 bytes,
54// FORMAT:
55// 110ccccc -> cccc is hardware audio channel
56// nnnnnnnn -> nnnnnnnn is the note value
57// nnnnnnnn -> nnnnnnnn is the volume (0...255)
58#define IMF_NOTE_ON_CHANNEL( channel ) 0xc0 | ( ( 1 << ( ( channel ) % ( MAX_AUDIO_CHANNELS ) ) ) )
59#define IMF_NOTE_ON_NOTE( note ) ( ( note ) & 0xff )
60#define IMF_SET_VOLUME_VALUE( value ) ( ( value ) & 0xff )
61
62// SET PROGRAM, 2 bytes,
63// FORMAT:
64// 10cccccc -> cccc is hardware audio channel
65// pppppppp -> pppppppp is the program value
66#define IMF_SET_PROGRAM_CHANNEL( channel ) 0x80 | ( ( 1 << ( ( channel ) % ( MAX_AUDIO_CHANNELS ) ) ) )
67#define IMF_SET_PROGRAM_PROGRAM( program ) ( ( program ) & 0xff )
68
69// DELAY, 1 byte, FORMAT: 0dddddddd -> dddddddd is the delay (in jiffies)
70#define IMF_DELAY( jiffies ) ( ( jiffies ) & 0x7f )
71
72// Maximum size of an IMF stream.
73#define IMF_MAX_STREAM_SIZE ( 16 * MAX_TEMPORARY_STORAGE );
74
75// Maximum number of MIDI channels
76#define MAX_MIDI_CHANNELS 127
77
78// Current position in the produced IMF stream.
79static int imfStreamPos = 0;
80
81// Number of tracks in the MIDI files
82static int numMidiTracks;
83
84// Store if the track is finished
85static int * finishedTrack;
86
87// Store if the track has been used
88static int * usedTrack;
89
90// Store the last note played on that IMF channel
91static int * lastNoteOnChannel;
92
93// Store if IMF channel has been used.
94static int usedChannel[MAX_AUDIO_CHANNELS];
95
96// Store bounded MIDI channel
97static int * lastProgramOnMIDITrack;
98
99// Store bounded IMF channel
100static int lastProgramOnChannel[MAX_AUDIO_CHANNELS];
101
102// Store bounded MIDI channel
103static int lastChannelOnChannel[MAX_AUDIO_CHANNELS];
104
105// Tempo of the MIDI file
106static int tempo = 81;
107
108// IMF buffer
109static char * imfBuffer = NULL;
110
111// This routine will check if the IMF stream can be stored
112// into the allocated IMF stream.
113
114static int decode_midi_inside_memory_limits( ) {
115
116 return imfStreamPos < IMF_MAX_STREAM_SIZE;
117
118}
119
120// This routine will help to decode the (overall) tempo of the MIDI track.
121// It returns always 0.
122static int decode_midi_payload_set_tempo( MidiMessagePayload * _payload ) {
123
124 // printf( "iBPM = %d\n", _payload->MsgData.MetaEvent.Data.Tempo.iBPM );
125
126 tempo = _payload->MsgData.MetaEvent.Data.Tempo.iBPM;
127
128 return 0;
129
130}
131
132// This routine will help to decode a single (explicit) NOTE OFF message.
133// IMF FORMAT: 1 byte = 1110cccc
134// It returns 0 if nothing has been produced on the stream.
135static int decode_midi_payload_note_off( MidiMessagePayload * _payload ) {
136
137 // First of all, we have to select which IMF channel we have to
138 // turn off. Since it is possibile that the MIDI channel has been
139 // ignored, this command could translate in a "NOP", that is:
140 // nothing is generated on IMF stream.
141 int imfChannelIndex;
142 for( imfChannelIndex=0; imfChannelIndex<MAX_AUDIO_CHANNELS; ++imfChannelIndex ) {
143
144 // If the IMF channel is really used, and the note played on that IMF channel
145 // is the same as the MIDI channel, then...
146 if ( usedChannel[ imfChannelIndex ] && lastNoteOnChannel[ imfChannelIndex ] == _payload->MsgData.NoteOff.iNote ) {
147
148 // Turn off the note.
149 lastNoteOnChannel[ imfChannelIndex ] = 0;
150
151 // Free IMF channel.
152 usedChannel[ imfChannelIndex ] = 0;
153
154 // Produce the data on the IMF stream.
155 imfBuffer[imfStreamPos++] = IMF_NOTE_OFF( imfChannelIndex );
156
157 // printf( "%d: NOTE OFF\n", imfChannelIndex );
158
159 // printf( " -> note off on %d\n", imfChannelIndex );
160
161 break;
162
163 }
164
165 }
166
167 if ( imfChannelIndex >= MAX_AUDIO_CHANNELS ) {
168 // printf( " ! NOTE OFF skipped since there is no audio channel\n" );
169 }
170
171 // printf( "\n" );
172
173 return ( imfChannelIndex < MAX_AUDIO_CHANNELS );
174
175}
176
177// This routine will help to decode a single (explicit) NOTE ON message.
178// In order to decode also the NOTE ON as NOTE OFF case, this routine
179// could decode using both formats:
180// IMF NOTE OFF FORMAT: 1 byte = 1110cccc
181// IMF NOTE ON FORMAT: 2 bytes = 1110cccc; nnnnnnnn
182// The routine returns 0 if nothing is produced, 1 if NOTE OFF has been
183// produced, 2 if NOTE ON has been produced.
184static int decode_midi_payload_note_on( MidiMessagePayload * _payload ) {
185
186 // First of all, we have to distinguish between a real NOTE ON
187 // and a NOTE ON used to "mute" the previous NOTE ON (and it is
188 // like a NOTE OFF). All of this is done by using the volume as
189 // indicator. A volume of 0 means "NOTE OFF", otherwise is a
190 // NOTE ON.
191 if ( _payload->MsgData.NoteOn.iVolume > 0 ) {
192
193 int alreadyAllocated = MAX_AUDIO_CHANNELS;
194 int imfChannelIndex = 0;
195
196 // printf( "LOOKING FOR MIDI CHANNEL = %2.2x\n", _payload->MsgData.NoteOn.iChannel );
197
198 for( imfChannelIndex=0; imfChannelIndex<MAX_AUDIO_CHANNELS; ++imfChannelIndex ) {
199 // We exit as soon as we found one.
200 if ( lastChannelOnChannel[ imfChannelIndex ] == _payload->MsgData.NoteOn.iChannel &&
201 (lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] != -1) && (lastProgramOnChannel[ imfChannelIndex ] != -1) && (lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] == lastProgramOnChannel[ imfChannelIndex ]) ) {
202 alreadyAllocated = imfChannelIndex;
203 // printf( "ALREADY ALLOCATED, IMF = %2.2x, MIDI = %2.2x", alreadyAllocated, _payload->MsgData.NoteOn.iChannel );
204 break;
205 }
206 }
207
208 if ( alreadyAllocated == MAX_AUDIO_CHANNELS ) {
209
210 // We have to look for an already setted channel, and not used.
211 for( imfChannelIndex=0; imfChannelIndex<MAX_AUDIO_CHANNELS; ++imfChannelIndex ) {
212 // printf( "%d) IMF: %2.2x MIDI: %2.2x\n", imfChannelIndex, lastProgramOnChannel[ imfChannelIndex ], lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] );
213 // We exit as soon as we found one.
214 if ( (lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] != -1) && (lastProgramOnChannel[ imfChannelIndex ] != -1) && (lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] == lastProgramOnChannel[ imfChannelIndex ]) ) {
215 // printf( "%d: POTENTIAL %2.2x (channel=%2.2x)\n", imfChannelIndex, lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ], _payload->MsgData.NoteOn.iChannel );
216 if ( ! usedChannel[ imfChannelIndex ] ) {
217 // printf( "%d: SELECTED!\n", imfChannelIndex );
218 break;
219 }
220 }
221 }
222
223 if ( imfChannelIndex == MAX_AUDIO_CHANNELS ) {
224 // printf( "NONE POTENTIAL\n" );
225 // We have to look for a free one.
226 // So we have to look for an unused one.
227 for( imfChannelIndex=0; imfChannelIndex<MAX_AUDIO_CHANNELS; ++imfChannelIndex ) {
228 // We exit as soon as we found one.
229 if ( ! usedChannel[ imfChannelIndex ] ) {
230 // printf( "%d: FREE!\n", imfChannelIndex );
231 break;
232 }
233 }
234 }
235
236 } else {
237 imfChannelIndex = alreadyAllocated;
238 }
239
240 // If we found a channel...
241 if ( imfChannelIndex < MAX_AUDIO_CHANNELS ) {
242
243 // Allocate that channel
244 // (note that bounded channels are already allocated)
245 usedChannel[ imfChannelIndex ] = 1;
246
247 if ( lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] != lastProgramOnChannel[ imfChannelIndex ] ) {
248 if ( ( lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] & 0xff ) != 0xff ) {
249 if ( lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] == 0 ) {
250 lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] = IMF_INSTRUMENT_CELLO;
251 }
252 imfBuffer[imfStreamPos++] = IMF_SET_PROGRAM_CHANNEL( imfChannelIndex );
253 imfBuffer[imfStreamPos++] = IMF_SET_PROGRAM_PROGRAM( lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] );
254 // printf( " -> set program $%2.2x on on %d\n", lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ], imfChannelIndex );
255 lastProgramOnChannel[ imfChannelIndex ] = lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ];
256 lastChannelOnChannel[ imfChannelIndex ] = _payload->MsgData.NoteOn.iChannel;
257 // printf( "%d: SET PROGRAM %2.2x (channel=%2.2x)\n", imfChannelIndex, lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ], _payload->MsgData.NoteOn.iChannel );
258 } else {
259 lastProgramOnChannel[ imfChannelIndex ] = IMF_INSTRUMENT_CELLO;
260 lastChannelOnChannel[ imfChannelIndex ] = _payload->MsgData.NoteOn.iChannel;
261 }
262 } else {
263
264 }
265
266 // Produce the 2 bytes data on the stream
267 imfBuffer[imfStreamPos++] = IMF_NOTE_ON_CHANNEL( imfChannelIndex );
268 imfBuffer[imfStreamPos++] = IMF_NOTE_ON_NOTE( _payload->MsgData.NoteOn.iNote );
269
270 // printf( "%d: NOTE ON %d [%2.2x]\n", imfChannelIndex, _payload->MsgData.NoteOn.iNote, lastProgramOnChannel[ imfChannelIndex ] );
271
272 // Produce the 2 bytes with the volume on the stream
273 // imfBuffer[imfStreamPos++] = IMF_SET_VOLUME_CHANNEL( imfChannelIndex );
274 imfBuffer[imfStreamPos++] = IMF_SET_VOLUME_VALUE( _payload->MsgData.NoteOn.iVolume );
275
276 // We have to store, also, the note played on that channel.
277 lastNoteOnChannel[ imfChannelIndex ] = _payload->MsgData.NoteOn.iNote;
278
279 // printf( " -> note on on %d\n", imfChannelIndex );
280
281 return 2;
282 } else {
283
284 if ( imfChannelIndex >= MAX_AUDIO_CHANNELS ) {
285 // printf( " ! NOTE ON skipped since there is no free audio channel\n" );
286 }
287
288 return 0;
289 }
290
291 } else {
292 return decode_midi_payload_note_off( _payload );
293 }
294
295 // printf( "\n" );
296
297}
298
299// This routine will help to decode a single (explicit) SET PROGRAM message.
300// IMF FORMAT: 2 bytes = 10cccccc; nnnnnnnn
301// The routine returns 0 if nothing is produced, 2 if SET PROGRAM
302// has been produced.
303static int decode_midi_payload_set_program( MidiMessagePayload * _payload ) {
304
305 int imfChannelIndex;
306
307 // printf( "MIDI CHANNEL = %2.2x, PROGRAM = %2.2x\n", _payload->MsgData.ChangeProgram.iChannel, _payload->MsgData.ChangeProgram.iProgram );
308
309 // // Produce the 2 bytes data on the stream
310 // imfBuffer[imfStreamPos++] = IMF_SET_PROGRAM_CHANNEL( imfChannelIndex );
311 // imfBuffer[imfStreamPos++] = IMF_SET_PROGRAM_PROGRAM( _payload->MsgData.ChangeProgram.iProgram );
312
313 // We have to store, also, the MIDI channel that this program is
314 // allocated. This is needed, in order to avoid to play a wrong
315 // note on the wrong channel.-
316 char str[MAX_TEMPORARY_STORAGE];
317
318 if ( _payload->MsgData.ChangeProgram.iChannel == 10 ) {
319 if ( _payload->MsgData.ChangeProgram.iProgram == 0 ) {
320 lastProgramOnMIDITrack[ _payload->MsgData.ChangeProgram.iChannel ] = 0;
321 } else {
322 lastProgramOnMIDITrack[ _payload->MsgData.ChangeProgram.iChannel ] = _payload->MsgData.ChangeProgram.iProgram;
323 }
324 } else {
325 lastProgramOnMIDITrack[ _payload->MsgData.ChangeProgram.iChannel ] = _payload->MsgData.ChangeProgram.iProgram;
326 }
327
328 return 0;
329
330}
331
332// This routine will help to decode a single frame of MIDI message, converting
333// it into a (simplier) IMF sequence. The method will return the number of
334// bytes generated on the IMF stream.
335static int decode_midi_payload( MidiMessagePayload * _payload, int _control_only ) {
336
337 // First of all, decode the main event type
338 int ev;
339 if (_payload->bImpliedMsg) {
340 ev = _payload->iImpliedMsg;
341 } else {
342 ev = _payload->iType;
343 }
344
345 // Based on the event type, we produce a different sequence.
346 switch(ev) {
347 case messageNoteOff:
348 // printf( "%4.4x: NOTE OFF iNote = $%2.2x iChannel = $%2.2x\n", _payload->dwAbsPos, _payload->MsgData.NoteOff.iNote, _payload->MsgData.NoteOff.iChannel );
349 if ( !_control_only ) {
350 return decode_midi_payload_note_off( _payload );
351 } else {
352 // printf( " ! ignored since control only\n" );
353 }
354 break;
355 case messageNoteOn:
356 // printf( "%4.4x: NOTE ON iNote = $%2.2x iChannel = $%2.2x iVolume = $%2.2x iProgram = $%2.2x\n", _payload->dwAbsPos, _payload->MsgData.NoteOn.iNote, _payload->MsgData.NoteOn.iChannel, _payload->MsgData.NoteOn.iVolume, lastProgramOnMIDITrack[ _payload->MsgData.NoteOn.iChannel ] & 0xff );
357 if ( !_control_only ) {
358 return decode_midi_payload_note_on( _payload );
359 } else {
360 // printf( " ! ignored since control only\n" );
361 }
362 break;
364 // printf( "%4.4x: SET PROGRAM iProgram = $%2.2x iChannel = $%2.2x\n", _payload->dwAbsPos, _payload->MsgData.ChangeProgram.iProgram, _payload->MsgData.ChangeProgram.iChannel );
365 return decode_midi_payload_set_program( _payload );
366 break;
368 // printf( "%4.4x: NOTE KEY PRESSURE\n", _payload->dwAbsPos );
369 break;
371 // printf( "%4.4x: SET PARAMETER\n", _payload->dwAbsPos );
372 break;
374 // printf( "%4.4x: CHANGE PRESSURE\n", _payload->dwAbsPos );
375 break;
377 // printf( "%4.4x: SET PITCH WHEEL\n", _payload->dwAbsPos );
378 break;
379 case messageMetaEvent:
380 // printf( "%4.4x: META EVENT -", _payload->dwAbsPos );
381 switch(_payload->MsgData.MetaEvent.iType) {
382 case metaInfoMIDIPort:
383 // printf( "metaInfoMIDIPort\n" );
384 break;
386 // printf( "metaInfoSequenceNumber\n" );
387 break;
389 // printf( "metaInfoTextEvent\n" );
390 break;
392 // printf( "metaInfoCopyright\n" );
393 break;
395 // printf( "metaInfoTrackName\n" );
396 break;
398 // printf( "metaInfoInstrument\n" );
399 break;
400 case metaInfoLyric:
401 // printf( "metaInfoLyric\n" );
402 break;
403 case metaInfoMarker:
404 // printf( "metaInfoMarker\n" );
405 break;
406 case metaInfoCuePoint:
407 // printf( "metaInfoCuePoint\n" );
408 break;
410 // printf( "metaInfoEndSequence\n" );
411 break;
413 // printf( "metaInfoSMPTEOffset\n" );
414 break;
415 case metaInfoTimeSig:
416 // printf( "metaInfoTimeSig\n" );
417 break;
418 case metaInfoKeySig:
419 // printf( "metaInfoKeySig\n" );
420 break;
422 // printf( "metaInfoSequencerSpecific\n" );
423 break;
424 case metaInfoSetTempo:
425 // printf( "metaInfoSetTempo\n" );
426 return decode_midi_payload_set_tempo( _payload );
427 break;
428 }
429 break;
430 case messageSysEx1:
431 // printf( "%4.4x: SYS EX1\n", _payload->dwAbsPos );
432 break;
433 case messageSysEx2:
434 // printf( "%4.4x: SYS EX2\n", _payload->dwAbsPos );
435 break;
436 }
437
438 return 0;
439
440}
441
442static Variable * midi_load_to_variable( Environment * _environment, char * _filename, char * _alias, int _bank_expansion ) {
443
444 // Reinitialize all parameters for MIDI>IMF decoding.
445 imfStreamPos = 0;
446 numMidiTracks = 0;
447 finishedTrack = NULL;
448 usedTrack = NULL;
449 lastNoteOnChannel = NULL;
450 memset( usedChannel, 0, MAX_AUDIO_CHANNELS * sizeof( int ) );
451 lastProgramOnMIDITrack = NULL;
452 memset( lastProgramOnChannel, 0, MAX_AUDIO_CHANNELS * sizeof( int ) );
453 memset( lastChannelOnChannel, 0, MAX_AUDIO_CHANNELS * sizeof( int ) );
454 tempo = 81;
455 imfBuffer = NULL;
456
457 Variable * result = NULL;
458
459 int size = 0;
460
461 MidiFile * mf = midiFileOpen(_filename);
462
463 // If the file to read is a MIDI...
464 if ( mf ) {
465
466 imfBuffer = malloc( 16 * MAX_TEMPORARY_STORAGE );
467 int ppq = midiFileGetPPQN(mf);
468 int midiConversionDone = 0;
469 int track = 0;
470
471 // Prepare to read all tracks of the file.
472 int numMidiTracks = midiReadGetNumTracks(mf);
473
474 // Read each track indipendently
475 MidiMessagePayload * msg = malloc( numMidiTracks * sizeof( MidiMessagePayload ) );
476 memset( msg, 0, numMidiTracks * sizeof( MidiMessagePayload ) );
477
478 // Initialize each track.
479 for( track=0; track<numMidiTracks; ++track ) {
480 midiReadInitMessage( &msg[track] );
481 }
482
483 // We take note if the track is finished
484 finishedTrack = malloc( numMidiTracks * sizeof( int ) );
485 memset( finishedTrack, 0, numMidiTracks * sizeof( int ) );
486
487 // We take note if the track has been used
488 usedTrack = malloc( numMidiTracks * sizeof( int ) );
489 memset( usedTrack, 0xff, numMidiTracks * sizeof( int ) );
490
491 // We take note of the last note on channel
492 lastNoteOnChannel = malloc( MAX_AUDIO_CHANNELS * sizeof( int ) );
493 memset( lastNoteOnChannel, 0, MAX_AUDIO_CHANNELS * sizeof( int ) );
494
495 // Used channels
496 memset( usedChannel, 0, MAX_AUDIO_CHANNELS * sizeof( int ) );
497
498 // Bounded midi channels.
499 lastProgramOnMIDITrack = malloc( MAX_MIDI_CHANNELS * sizeof( int ) );
500 memset( lastProgramOnMIDITrack, 0xff, MAX_MIDI_CHANNELS * sizeof( int ) );
501 memset( lastProgramOnChannel, 0xff, MAX_AUDIO_CHANNELS * sizeof( int ) );
502 memset( lastChannelOnChannel, 0xff, MAX_AUDIO_CHANNELS * sizeof( int ) );
503
504 // Current position examinated
505 int midiPosition = 0;
506
507 // Repeat until the MIDI file is finished
508 while( ! midiConversionDone ) {
509
510 // printf("\n-------------------------------------------------\n" );
511 // printf("POSITION %4.4x\n", midiPosition );
512 // printf("-------------------------------------------------\n" );
513
514 // For each track we must check if the current message
515 // is "up to date" in respect of the current position.
516 // If not, we must read another message from the MIDI
517 // file for that track.
518 for( track=0; track<numMidiTracks; ++track ) {
519
520 // printf(" TRACK %d finished = %d\n", track, finishedTrack[ track ] );
521
522 // If the track has not bneen finished...
523 if ( ! finishedTrack[ track ] ) {
524
525 // printf(" TRACK %d msg position = %4.4x\n", track, msg[track].dwAbsPos );
526
527 // If the current position is AFTER the one
528 // in the message, and the message has been
529 // used, the message is outdated.
530 if ( msg[track].dwAbsPos < midiPosition ) {
531
532 // printf(" >> TRANSLATING " );
533
534 while( msg[track].dwAbsPos < midiPosition ) {
535
536 // printf(" >> FORWARD " );
537
538 // Before reading the next message, decode the current
539 // one if of control type.
540 decode_midi_payload( &msg[track], 1 );
541
542 // Try to read the next message. If the track
543 // is finished, we take note of that.
544 if ( !midiReadGetNextMessage(mf, track, &msg[track]) ) {
545 // printf(" !! EOF " );
546 finishedTrack[track] = 1;
547 break;
548 }
549 }
550
551 // If the track is not finished, we can consider
552 // it for the next round.
553 if ( !finishedTrack[track] ) {
554 usedTrack[track] = 0;
555 }
556
557 // printf( "\n" );
558
559 } else if ( msg[track].dwAbsPos == midiPosition && usedTrack[ track ] ) {
560
561 // printf(" >> NEXT MESSAGE\n" );
562
563 // Before reading the next message, decode the current
564 // one if of control type.
565 decode_midi_payload( &msg[track], 1 );
566
567 // Try to read the next message. If the track
568 // is finished, we take note of that.
569 if ( !midiReadGetNextMessage(mf, track, &msg[track]) ) {
570 // printf(" !! EOF\n" );
571 finishedTrack[track] = 1;
572 break;
573 }
574
575 // If the track is not finished, we can consider
576 // it for the next round.
577 if ( !finishedTrack[track] ) {
578 usedTrack[track] = 0;
579 }
580
581 }
582 }
583 }
584
585 // Now we check if the MIDI file is ended, that is
586 // if every track is finished.
587 for(track=0; track<numMidiTracks; ++track ) {
588 if ( !finishedTrack[track] )
589 break;
590 }
591 if ( track >= numMidiTracks ) {
592 midiConversionDone = 1;
593 }
594
595 // If the MIDI file is not finished...
596 if ( ! midiConversionDone ) {
597
598 // For each track...
599 for( track=0; track<numMidiTracks; ++track ) {
600
601 // printf(" PARSING %d\n", track );
602
603 // Skip if the track has been already used.
604 if ( usedTrack[track] ) {
605 // printf(" -- skipped since unused track\n" );
606 continue;
607 }
608
609 // Skip if message is after this moment
610 if ( msg[track].dwAbsPos > midiPosition ) {
611 // printf(" -- skipped since after this position \n" );
612 continue;
613 }
614
615 decode_midi_payload( &msg[track], 0 );
616
617 usedTrack[track] = 1;
618
619 if ( !decode_midi_inside_memory_limits() ) {
621 }
622
623 }
624
625 // Now check for unused messages, to find out
626 // the nearest in terms of position.
627 int midiMinPosition = midiPosition + 0xffffff;
628 for( track=0; track<numMidiTracks; ++track ) {
629 if ( !finishedTrack[track] && msg[track].dwAbsPos < midiMinPosition ) {
630 midiMinPosition = msg[track].dwAbsPos;
631 }
632 }
633
634 // printf(" NEXT MIN POSITION: %4.4x\n", midiMinPosition );
635
636 // If the minimum position is valid...
637 if ( midiMinPosition >= midiPosition && midiMinPosition < (midiPosition + 0xffffff) ) {
638
639 // just divide 60,000 by your tempo and you will get the number of milliseconds per beat.
640 // Thereafter you can divide by two for eighth notes or four for sixteenth notes.
641 // microseconds per tick = microseconds per quarter note / ticks per quarter note
642
643 float kMillisecondsPerBPM = (float)( 60 * 1000 ) / (float)tempo;
644 float kMillisecondsPerTick = kMillisecondsPerBPM / ppq;
645 float deltaTimeInMilliseconds = ( midiMinPosition - midiPosition ) * kMillisecondsPerTick;
646
647 // We must calculate the effective delay (in jiffies)
648 // to introduce into the IMF stream, and it is based
649 // on the real MIDI tempo.
650 int jiffies = deltaTimeInMilliseconds / 20;
651
652 // printf( " ( midiMinPosition - midiPosition ): %d\n", ( midiMinPosition - midiPosition ) );
653 // printf( "deltaTimeInMilliseconds: %f, jiffies: %d\n", deltaTimeInMilliseconds, jiffies );
654
655 // printf(" DELAY: %d jiffies\n", jiffies );
656
657 // Since the IMF format can represent a pause of
658 // maximum 127 jiffies for each byte produced,
659 // we must split a larger delay. The former
660 // will have 127 jiffies of delay, the last one
661 // will be the rest of the jiffies.
662 while( jiffies > 127 ) {
663 imfBuffer[imfStreamPos++] = IMF_DELAY( 127 );
664 jiffies -= 127;
665 // printf( "DELAY 2.54 seconds\n" );
666 if ( !decode_midi_inside_memory_limits( ) ) {
667 CRITICAL_MIDI_OUT_OF_MEMORY( _filename )
668 }
669 }
670 if ( jiffies > 0 ) {
671 imfBuffer[imfStreamPos++] = IMF_DELAY( jiffies );
672 // printf( "DELAY %f seconds (%d jiffies)\n", (((float)jiffies) / ((float)50)), jiffies );
673 if ( !decode_midi_inside_memory_limits( ) ) {
674 CRITICAL_MIDI_OUT_OF_MEMORY( _filename )
675 }
676 }
677
678 // Update the current position on the stream
679 // with the minimum one.
680 midiPosition = midiMinPosition;
681
682 } else {
683 // // There is no "valid" minimum position, so we move
684 // // directly on the next message.
685 // ++midiPosition;
686 }
687
688 }
689
690 }
691
692 // Free each track read.
693 for( track=0; track<numMidiTracks; ++track ) {
694 midiReadFreeMessage( &msg[track] );
695 }
696
697 // Close the file
698 midiFileClose(mf);
699
700 size = imfStreamPos;
701
702 result = variable_temporary( _environment, VT_MUSIC, "(buffer)" );
703
704 variable_store_buffer( _environment, result->name, imfBuffer, size, 0 );
705
706 }
707
708 return result;
709
710}
711
712Variable * sid_load_to_variable( Environment * _environment, char * _filename, char * _alias, int _bank_expansion ) {
713
714 Variable * result = NULL;
715
716 SIDFILE * sidFile = sid_file_read( _filename, _environment->sidRelocAddress );
717
718 if ( sidFile ) {
719
720 result = variable_temporary( _environment, VT_MUSIC, "(buffer)" );
721
722 result->sidFile = sidFile;
723 sidFile->next = _environment->sidFiles;
724 _environment->sidFiles = sidFile;
725
726 }
727
728 return result;
729
730}
731
732Variable * music_load_to_variable( Environment * _environment, char * _filename, char * _alias, int _bank_expansion ) {
733
734 int sidFormat = 0, midiFormat = 0;
735
736 Variable * result = NULL;
737
738 if ( strstrcase( _filename, ".mid") || strstrcase( _filename, ".midi") ) {
739 midiFormat = 1;
740 }
741
742 if ( strstrcase( _filename, ".sid") ) {
743 sidFormat = 1;
744 }
745
746 if ( !sidFormat && !midiFormat ) {
747 result = midi_load_to_variable( _environment, _filename, _alias, _bank_expansion );
748 if ( ! result ) {
749 result = sid_load_to_variable( _environment, _filename, _alias, _bank_expansion );
750 if ( ! result ) {
751 CRITICAL_CANNOT_LOAD_MUSIC( _filename );
752 }
753 }
754 } else if ( sidFormat ) {
755 result = sid_load_to_variable( _environment, _filename, _alias, _bank_expansion );
756 if ( !result ) {
758 }
759 } else if ( midiFormat ) {
760 result = midi_load_to_variable( _environment, _filename, _alias, _bank_expansion );
761 if ( !result ) {
763 }
764 }
765 return result;
766
767}
Variable * variable_temporary(Environment *_environment, VariableType _type, char *_meaning)
Define a temporary variable.
const char * strstrcase(const char *_x, const char *_y)
Variable * variable_store_buffer(Environment *_environment, char *_destination, unsigned char *_buffer, int _size, int _at)
#define IMF_DELAY(jiffies)
Definition _music.c:70
Variable * sid_load_to_variable(Environment *_environment, char *_filename, char *_alias, int _bank_expansion)
Definition _music.c:712
#define IMF_SET_PROGRAM_PROGRAM(program)
Definition _music.c:67
#define MAX_MIDI_CHANNELS
Definition _music.c:76
#define IMF_SET_VOLUME_VALUE(value)
Definition _music.c:60
#define IMF_NOTE_ON_NOTE(note)
Definition _music.c:59
#define IMF_SET_PROGRAM_CHANNEL(channel)
Definition _music.c:66
#define IMF_NOTE_OFF(channel)
Definition _music.c:51
Variable * music_load_to_variable(Environment *_environment, char *_filename, char *_alias, int _bank_expansion)
Definition _music.c:732
#define IMF_NOTE_ON_CHANNEL(channel)
Definition _music.c:58
#define IMF_MAX_STREAM_SIZE
Definition _music.c:73
int size
Definition _optimizer.c:678
#define MAX_AUDIO_CHANNELS
Definition atari.h:149
int midiFileGetPPQN(const MidiFile *_pMF)
Definition midi.c:319
MidiFile * midiFileOpen(const char *pFilename)
Definition midi.c:345
int midiReadGetNextMessage(const MidiFile *_pMF, int iTrack, MidiMessagePayload *pMsg)
Definition midi.c:922
void midiReadInitMessage(MidiMessagePayload *pMsg)
Definition midi.c:1142
int midiReadGetNumTracks(const MidiFile *_pMF)
Definition midi.c:916
void midiReadFreeMessage(MidiMessagePayload *pMsg)
Definition midi.c:1149
int midiFileClose(MidiFile *_pMF)
Definition midi.c:515
@ messageChangePressure
Definition midi.h:42
@ messageNoteOn
Definition midi.h:38
@ messageSysEx1
Definition midi.h:45
@ messageSetPitchWheel
Definition midi.h:43
@ messageMetaEvent
Definition midi.h:44
@ messageNoteKeyPressure
Definition midi.h:39
@ messageNoteOff
Definition midi.h:37
@ messageSetProgram
Definition midi.h:41
@ messageSetParameter
Definition midi.h:40
@ messageSysEx2
Definition midi.h:46
struct _MidiMessagePayload MidiMessagePayload
void MidiFile
Definition midi.h:602
@ metaInfoTrackName
Definition midi.h:267
@ metaInfoTextEvent
Definition midi.h:265
@ metaInfoCuePoint
Definition midi.h:271
@ metaInfoSequenceNumber
Definition midi.h:264
@ metaInfoKeySig
Definition midi.h:277
@ metaInfoMarker
Definition midi.h:270
@ metaInfoSequencerSpecific
Definition midi.h:278
@ metaInfoSetTempo
Definition midi.h:274
@ metaInfoMIDIPort
Definition midi.h:272
@ metaInfoInstrument
Definition midi.h:268
@ metaInfoTimeSig
Definition midi.h:276
@ metaInfoCopyright
Definition midi.h:266
@ metaInfoEndSequence
Definition midi.h:273
@ metaInfoLyric
Definition midi.h:269
@ metaInfoSMPTEOffset
Definition midi.h:275
SIDFILE * sid_file_read(char *_filename, int _reloc_address)
Definition sid_file.c:94
char * sid_file_get_lasterror_string()
Definition sid_file.c:376
struct _SIDFILE SIDFILE
int sidRelocAddress
Definition ugbc.h:3223
SIDFILE * sidFiles
Definition ugbc.h:3221
union _MidiMessagePayload::_MsgData::_MetaEvent::_Data Data
MidiMessage iImpliedMsg
Definition midi.h:612
union _MidiMessagePayload::_MsgData MsgData
MidiMessage iType
Definition midi.h:605
struct _SIDFILE * next
Definition sid_file.h:44
SIDFILE * sidFile
Definition ugbc.h:1213
char * name
Definition ugbc.h:979
void * malloc(YYSIZE_T)
#define MAX_TEMPORARY_STORAGE
Definition ugbc.h:563
#define CRITICAL_CANNOT_LOAD_MIDI_FILE(f)
Definition ugbc.h:3806
#define CRITICAL_CANNOT_LOAD_MUSIC(f)
Definition ugbc.h:3805
struct _Variable Variable
Structure of a single variable.
#define IMF_INSTRUMENT_CELLO
Definition ugbc.h:4616
struct _Environment Environment
Structure of compilation environment.
@ VT_MUSIC
Definition ugbc.h:516
#define CRITICAL(s)
Definition ugbc.h:3399
#define CRITICAL_MIDI_OUT_OF_MEMORY(f)
Definition ugbc.h:3689
struct _MidiMessagePayload::_MsgData::_MetaEvent::_Data::_Tempo Tempo
struct _MidiMessagePayload::_MsgData::_NoteOff NoteOff
struct _MidiMessagePayload::_MsgData::_MetaEvent MetaEvent
struct _MidiMessagePayload::_MsgData::_ChangeProgram ChangeProgram
struct _MidiMessagePayload::_MsgData::_NoteOn NoteOn