]>
code.delx.au - virtualtones/blob - midiengine.cpp
1 // midiengine.cpp - Class to play to a sequencer or write to a MIDI file
2 // Written by James Bunton <james@delx.cjb.net>
3 // Licensed under the GPL, see COPYING.txt for more details
6 #include "midiengine.h"
9 /* |---------------------------| */
10 /* | Code for class MidiEngine | */
11 /* |---------------------------| */
14 bool MidiEngine::initSuccess()
19 QString
MidiEngine::getError()
24 bool MidiEngine::stopNote(int num
)
26 return playNote(num
, 0, 0);
29 void MidiEngine::stopAll()
31 // For all notes that have and ending, end them now
32 for(int i
= 0; i
<= 127; i
++) {
33 if(noteEndings
[i
] != 0)
38 void MidiEngine::timerEvent(QTimerEvent
*)
40 // Increment the timer
43 // For all notes that expired after the current time, or on the current time:
45 for(int i
= 0; i
<= 127; i
++) {
46 if(noteEndings
[i
] <= timer
&& noteEndings
[i
] != 0) {
56 /* |-------------------------| */
57 /* | Code for class MidiReal | */
58 /* |-------------------------| */
65 #include <linux/soundcard.h>
66 #include <sys/ioctl.h>
71 class MidiReal::Private
{
73 // For setting up the midi output
77 unsigned char outpacket
[12];
83 // Use to set the sequencer and device
84 QString dev
= "/dev/sequencer";
91 d
->midiDevice
= devicenum
;
95 // Open the sequencer file
96 d
->seqfd
= open(d
->seqDevice
.latin1(), O_WRONLY
, 0);
98 errorMessage
= "Cannot open sequencer: " + d
->seqDevice
;
102 // Check if we can access the midi devices
104 if(ioctl(d
->seqfd
, SNDCTL_SEQ_NRMIDIS
, &maxMidi
) != 0) {
105 errorMessage
= "Cannot access MIDI devices on soundcard.";
108 if(d
->midiDevice
>= maxMidi
) {
109 errorMessage
= "Invalid MIDI device. Valid devices are 0-" + QString::number(maxMidi
- 1);
114 // Good, no errors so far
118 // Set all the note endings to zero
119 for(int i
= 0; i
<= 127; i
++) {
123 // Setup the outpacket
124 // The note on command
125 d
->outpacket
[0] = SEQ_MIDIPUTC
;
126 d
->outpacket
[1] = 0x90;
127 d
->outpacket
[2] = d
->midiDevice
;
131 d
->outpacket
[4] = SEQ_MIDIPUTC
;
132 d
->outpacket
[5] = 0; // note number
133 d
->outpacket
[6] = d
->midiDevice
;
136 // Specify the volume
137 d
->outpacket
[8] = SEQ_MIDIPUTC
;
138 d
->outpacket
[9] = 0; // volume
139 d
->outpacket
[10] = d
->midiDevice
;
140 d
->outpacket
[11] = 0;
142 // Start the timer so that we can end the notes
146 MidiReal::~MidiReal()
148 // Stop all the notes
151 // Close the sequencer
160 void MidiReal::setInstrument(int num
)
162 // Setup the MIDI packet
163 unsigned char outpacket
[4];
164 outpacket
[0] = SEQ_MIDIPUTC
;
165 outpacket
[2] = d
->midiDevice
;
168 // Write the "Change Patch" packet
170 write(d
->seqfd
, outpacket
, 4);
172 // Write the patch number packet
173 outpacket
[1] = num
& 0xff;
174 write(d
->seqfd
, outpacket
, 4);
177 bool MidiReal::playNote(int num
, int vel
, int len
)
179 // Check the note and volume are in range
180 if(num
> 127 || num
< 0)
183 if(vel
> 127 || vel
< 0)
187 d
->outpacket
[5] = num
;
189 // Specify the volume
190 d
->outpacket
[9] = vel
;
192 // Send it on it's way
193 write(d
->seqfd
, d
->outpacket
, 12);
195 // Set it up to turn off
196 if(len
!= 0 && vel
!= 0) {
197 noteEndings
[num
] = timer
+ len
;
201 // printf("Playing note: %d\n", num);
214 #include <mmsystem.h>
218 class MidiReal::Private
{
235 /* Open default MIDI Out device */
237 if(err
= midiOutOpen(&(d
->handle
), (UINT
)-1, 0, 0, CALLBACK_NULL
)) {
239 errorMessage
= "Error opening MIDI: " + QString::number(err
);
243 // Set all the note endings to zero
244 for(int i
= 0; i
<= 127; i
++) {
248 // Start the timer so that we can end the notes
252 MidiReal::~MidiReal()
254 // Stop all the notes
257 /* Close the MIDI device */
258 midiOutClose(d
->handle
);
264 void MidiReal::setInstrument(int num
)
266 // Setup the MIDI packet
267 d
->u
.bData
[0] = 0xc0;
268 d
->u
.bData
[1] = num
& 0xff;
273 midiOutShortMsg(d
->handle
, d
->u
.dwData
);
276 bool MidiReal::playNote(int num
, int vel
, int len
)
278 // Check the note and volume are in range
279 if(num
> 127 || num
< 0)
282 if(vel
> 127 || vel
< 0)
285 // Setup the MIDI packet
286 d
->u
.bData
[0] = 0x90;
292 midiOutShortMsg(d
->handle
, d
->u
.dwData
);
294 // Set it up to turn off
295 if(len
!= 0 && vel
!= 0) {
296 noteEndings
[num
] = timer
+ len
;
300 // printf("Playing note: %d\n", num);
321 /* |-------------------------| */
322 /* | Code for class MidiFile | */
323 /* |-------------------------| */
328 #include <qvaluelist.h>
333 class MidiFile::Private
{
335 /* This information found at http://www.borg.com/~jglatt/tech/midifile.htm */
339 /* Here's the 8 byte header that all chunks must have */
340 char ID
[4]; /* This will be 'M','T','h','d' */
341 unsigned long Length
; /* This will be 6 */
343 /* Here are the 6 bytes */
344 unsigned short Format
;
345 unsigned short NumTracks
;
346 unsigned short Division
;
351 /* Here's the 8 byte header that all chunks must have */
352 char ID
[4]; /* This will be 'M','T','r','k' */
353 unsigned long Length
; /* This will be the actual size of Data[] */
355 /* Here are the data bytes */
356 unsigned char *Data
; /* Its actual size is Data[Length] */
361 unsigned long deltaTime
; /* The delta time - 4 bytes*/
363 /* The midi packet data */
364 unsigned char midiData
[7];
370 /* A place to store the MTrk_Packets as we wait for them */
371 QValueList
<struct MTrk_Packet
> packets
;
373 /* The time the last note was played, relative to MidiEngine::timer */
374 long int prevNoteTime
;
376 /* Where we save the file to */
382 int twistVarLen(register unsigned long value
, unsigned char str
[4]); // 4 bytes
383 void convertLong2Array(unsigned long value
, unsigned char str
[4]);
384 void convertShort2Array(unsigned short value
, unsigned char str
[2]);
385 void writeBytes(unsigned char *str
, int num
);
386 void writeBytes(char *str
, int num
);
390 int MidiFile::Private::twistVarLen(register unsigned long value
, unsigned char str
[4])
392 register unsigned long buffer
;
394 buffer
= value
& 0x7F;
396 for(i
= 0; i
< 4; i
++) str
[i
] = 0;
398 while ( (value
>>= 7) )
401 buffer
|= ((value
& 0x7F) | 0x80);
406 str
[i
] = (unsigned char)buffer
;
414 void MidiFile::Private::convertLong2Array(unsigned long value
, unsigned char str
[4])
421 for(int i
= 0, j
= 3; i
< 4; i
++, j
--)
425 void MidiFile::Private::convertShort2Array(unsigned short value
, unsigned char str
[2])
436 void MidiFile::Private::writeBytes(unsigned char *str
, int num
)
438 for(int i
= 0; i
< num
; i
++)
442 void MidiFile::Private::writeBytes(char *str
, int num
)
444 for(int i
= 0; i
< num
; i
++)
448 void MidiFile::Private::writeMIDI()
453 unsigned char str
[4];
457 // Create the MThd header
468 // Create the MTrk chunk and allocate space for the MIDI packets
474 t
.Length
= packets
.count() * sizeof(struct MTrk_Packet
); // Only an approximation
475 t
.Data
= new unsigned char[t
.Length
];
478 // Store the MIDI packets in the MTrk chunk
479 QValueList
<struct MTrk_Packet
>::Iterator it
;
481 for(it
= packets
.begin(); it
!= packets
.end(); ++it
) {
482 // Twist the deltaTime, then copy it
483 j
= twistVarLen((*it
).deltaTime
, str
);
485 for(i
= 0; i
<= j
; i
++) t
.Data
[pos
+ i
] = str
[i
];
487 pos
= pos
+ i
; // New position
490 // Copy the MIDI bytes also
491 for(i
= 0; i
<= (*it
).lastGood
; i
++)
492 t
.Data
[pos
+ i
] = (*it
).midiData
[i
];
500 /* Write all the MIDI packets to disk */
502 // Writing the header
506 convertLong2Array(h
.Length
, str
);
509 convertShort2Array(h
.Format
, str
);
511 // Write the NumTracks
512 convertShort2Array(h
.NumTracks
, str
);
514 // Hack hack, write the Division
515 convertShort2Array(h
.Division
, str
);
521 // Now writing the track
525 convertLong2Array(t
.Length
, str
);
527 // Copy the track data
528 writeBytes(t
.Data
, t
.Length
);
536 MidiFile::MidiFile(QString file
)
544 d
->prevNoteTime
= -1;
546 // Open the MIDI file to send output to
547 d
->file
= fopen(d
->filename
.latin1(), "wb");
549 errorMessage
= "Cannot open output MIDI file: " + d
->filename
;
553 // Set all the note endings to zero
554 for(int i
= 0; i
<= 127; i
++) {
558 // Start the timer so that we can end the notes
562 MidiFile::~MidiFile()
564 // Stop all the notes
568 struct Private::MTrk_Packet pak
;
570 pak
.midiData
[0] = 0xff;
571 pak
.midiData
[1] = 0x2f;
572 pak
.midiData
[2] = 0x00;
574 d
->packets
.append(pak
);
579 // Close the MIDI file
588 void MidiFile::setInstrument(int num
)
590 // Setup the MIDI packet
591 struct Private::MTrk_Packet pak
;
594 if(d
->prevNoteTime
== -1) // If we're the first note, start from 0, but don't let us count (only a patch change)
597 d
->prevNoteTime
= timer
;
599 pak
.midiData
[0] = 0xc0;
600 pak
.midiData
[1] = num
& 0xff;
601 pak
.lastGood
= 1; // Last byte with data
603 d
->packets
.append(pak
);
606 bool MidiFile::playNote(int num
, int vel
, int len
)
608 // Check the note and volume are in range
609 if(num
> 127 || num
< 0)
612 if(vel
> 127 || vel
< 0)
616 // Setup the MIDI packet
617 struct Private::MTrk_Packet pak
;
620 if(d
->prevNoteTime
== -1 && vel
!= 0) // If we're the first note, start from 0
621 d
->prevNoteTime
= timer
;
622 pak
.deltaTime
= timer
- d
->prevNoteTime
;
623 d
->prevNoteTime
= timer
;
626 // Setup the MIDI packet
627 pak
.midiData
[0] = 0x90;
628 pak
.midiData
[1] = num
;
629 pak
.midiData
[2] = vel
;
630 pak
.lastGood
= 2; // Last byte with good data
633 // Setup the MIDI packet
634 pak
.midiData
[0] = 0x80;
635 pak
.midiData
[1] = num
;
636 pak
.midiData
[2] = 64; // How quickly the note goes away
637 pak
.lastGood
= 2; // Last byte with good data
640 // Set it up to turn off
641 if(len
!= 0 && vel
!= 0) {
642 noteEndings
[num
] = timer
+ len
;
646 // printf("Appending note: %d with deltaTime: %d\n", num, pak.deltaTime);
648 d
->packets
.append(pak
);