]>
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.net.au>
3 // Licensed under the GPL, see COPYING.txt for more details
6 #include "midiengine.h"
16 /* |---------------------------| */
17 /* | Code for class MidiEngine | */
18 /* |---------------------------| */
21 bool MidiEngine::initSuccess()
26 QString
MidiEngine::getError()
31 bool MidiEngine::stopNote(int num
)
33 return playNote(num
, 0, 0);
36 void MidiEngine::stopAll()
38 // For all notes that have and ending, end them now
39 for(int i
= 0; i
<= 127; i
++) {
40 if(noteEndings
[i
] != 0)
45 void MidiEngine::timerEvent(QTimerEvent
*)
47 // Increment the timer
50 // For all notes that expired after the current time, or on the current time:
52 for(int i
= 0; i
<= 127; i
++) {
53 if(noteEndings
[i
] <= timer
&& noteEndings
[i
] != 0) {
63 /* |-------------------------| */
64 /* | Code for class MidiReal | */
65 /* |-------------------------| */
72 #include <linux/soundcard.h>
73 #include <sys/ioctl.h>
78 class MidiReal::Private
{
80 // For setting up the midi output
84 unsigned char outpacket
[12];
90 // Use to set the sequencer and device
91 QString dev
= "/dev/sequencer";
98 d
->midiDevice
= devicenum
;
102 // Open the sequencer file
103 d
->seqfd
= open(d
->seqDevice
.toLatin1(), O_WRONLY
, 0);
105 errorMessage
= "Cannot open sequencer: " + d
->seqDevice
;
109 // Check if we can access the midi devices
111 if(ioctl(d
->seqfd
, SNDCTL_SEQ_NRMIDIS
, &maxMidi
) != 0) {
112 errorMessage
= "Cannot access MIDI devices on soundcard.";
115 if(d
->midiDevice
>= maxMidi
) {
116 errorMessage
= "Invalid MIDI device. Valid devices are 0-" + QString::number(maxMidi
- 1);
121 // Good, no errors so far
125 // Set all the note endings to zero
126 for(int i
= 0; i
<= 127; i
++) {
130 // Setup the outpacket
131 // The note on command
132 d
->outpacket
[0] = SEQ_MIDIPUTC
;
133 d
->outpacket
[1] = 0x90;
134 d
->outpacket
[2] = d
->midiDevice
;
138 d
->outpacket
[4] = SEQ_MIDIPUTC
;
139 d
->outpacket
[5] = 0; // note number
140 d
->outpacket
[6] = d
->midiDevice
;
143 // Specify the volume
144 d
->outpacket
[8] = SEQ_MIDIPUTC
;
145 d
->outpacket
[9] = 0; // volume
146 d
->outpacket
[10] = d
->midiDevice
;
147 d
->outpacket
[11] = 0;
149 // Start the timer so that we can end the notes
153 MidiReal::~MidiReal()
155 // Stop all the notes
158 // Close the sequencer
167 void MidiReal::setInstrument(int num
)
169 // Setup the MIDI packet
170 unsigned char outpacket
[4];
171 outpacket
[0] = SEQ_MIDIPUTC
;
172 outpacket
[2] = d
->midiDevice
;
175 // Write the "Change Patch" packet
177 write(d
->seqfd
, outpacket
, 4);
179 // Write the patch number packet
180 outpacket
[1] = num
& 0xff;
181 write(d
->seqfd
, outpacket
, 4);
184 bool MidiReal::playNote(int num
, int vel
, int len
)
186 // Check the note and volume are in range
187 if(num
> 127 || num
< 0)
190 if(vel
> 127 || vel
< 0)
194 d
->outpacket
[5] = num
;
196 // Specify the volume
197 d
->outpacket
[9] = vel
;
199 // Send it on it's way
200 write(d
->seqfd
, d
->outpacket
, 12);
202 // Set it up to turn off
203 if(len
!= 0 && vel
!= 0) {
204 noteEndings
[num
] = timer
+ len
;
208 // printf("Playing note: %d\n", num);
221 #include <mmsystem.h>
225 class MidiReal::Private
{
242 /* Open default MIDI Out device */
244 if(err
= midiOutOpen(&(d
->handle
), (UINT
)-1, 0, 0, CALLBACK_NULL
)) {
246 errorMessage
= "Error opening MIDI: " + QString::number(err
);
250 // Set all the note endings to zero
251 for(int i
= 0; i
<= 127; i
++) {
255 // Start the timer so that we can end the notes
259 MidiReal::~MidiReal()
261 // Stop all the notes
264 /* Close the MIDI device */
265 midiOutClose(d
->handle
);
271 void MidiReal::setInstrument(int num
)
273 // Setup the MIDI packet
274 d
->u
.bData
[0] = 0xc0;
275 d
->u
.bData
[1] = num
& 0xff;
280 midiOutShortMsg(d
->handle
, d
->u
.dwData
);
283 bool MidiReal::playNote(int num
, int vel
, int len
)
285 // Check the note and volume are in range
286 if(num
> 127 || num
< 0)
289 if(vel
> 127 || vel
< 0)
292 // Setup the MIDI packet
293 d
->u
.bData
[0] = 0x90;
299 midiOutShortMsg(d
->handle
, d
->u
.dwData
);
301 // Set it up to turn off
302 if(len
!= 0 && vel
!= 0) {
303 noteEndings
[num
] = timer
+ len
;
307 // printf("Playing note: %d\n", num);
328 /* |-------------------------| */
329 /* | Code for class MidiFile | */
330 /* |-------------------------| */
336 class MidiFile::Private
{
338 /* This information found at http://www.borg.com/~jglatt/tech/midifile.htm */
342 /* Here's the 8 byte header that all chunks must have */
343 char ID
[4]; /* This will be 'M','T','h','d' */
344 unsigned long Length
; /* This will be 6 */
346 /* Here are the 6 bytes */
347 unsigned short Format
;
348 unsigned short NumTracks
;
349 unsigned short Division
;
354 /* Here's the 8 byte header that all chunks must have */
355 char ID
[4]; /* This will be 'M','T','r','k' */
356 unsigned long Length
; /* This will be the actual size of Data[] */
358 /* Here are the data bytes */
359 unsigned char *Data
; /* Its actual size is Data[Length] */
364 unsigned long deltaTime
; /* The delta time - 4 bytes*/
366 /* The midi packet data */
367 unsigned char midiData
[7];
373 /* A place to store the MTrk_Packets as we wait for them */
374 QList
<struct MTrk_Packet
> packets
;
376 /* The time the last note was played, relative to MidiEngine::timer */
377 long int prevNoteTime
;
379 /* Where we save the file to */
385 int twistVarLen(register unsigned long value
, unsigned char str
[4]); // 4 bytes
386 void convertLong2Array(unsigned long value
, unsigned char str
[4]);
387 void convertShort2Array(unsigned short value
, unsigned char str
[2]);
388 void writeBytes(unsigned char *str
, int num
);
389 void writeBytes(char *str
, int num
);
393 int MidiFile::Private::twistVarLen(register unsigned long value
, unsigned char str
[4])
395 register unsigned long buffer
;
397 buffer
= value
& 0x7F;
399 for(i
= 0; i
< 4; i
++) str
[i
] = 0;
401 while ( (value
>>= 7) )
404 buffer
|= ((value
& 0x7F) | 0x80);
409 str
[i
] = (unsigned char)buffer
;
417 void MidiFile::Private::convertLong2Array(unsigned long value
, unsigned char str
[4])
424 for(int i
= 0, j
= 3; i
< 4; i
++, j
--)
428 void MidiFile::Private::convertShort2Array(unsigned short value
, unsigned char str
[2])
439 void MidiFile::Private::writeBytes(unsigned char *str
, int num
)
441 for(int i
= 0; i
< num
; i
++)
445 void MidiFile::Private::writeBytes(char *str
, int num
)
447 for(int i
= 0; i
< num
; i
++)
451 void MidiFile::Private::writeMIDI()
456 unsigned char str
[4];
460 // Create the MThd header
471 // Create the MTrk chunk and allocate space for the MIDI packets
477 t
.Length
= packets
.count() * sizeof(struct MTrk_Packet
); // Only an approximation
478 t
.Data
= new unsigned char[t
.Length
];
481 // Store the MIDI packets in the MTrk chunk
482 QList
<struct MTrk_Packet
>::Iterator it
;
484 for(it
= packets
.begin(); it
!= packets
.end(); ++it
) {
485 // Twist the deltaTime, then copy it
486 j
= twistVarLen((*it
).deltaTime
, str
);
488 for(i
= 0; i
<= j
; i
++) t
.Data
[pos
+ i
] = str
[i
];
490 pos
= pos
+ i
; // New position
493 // Copy the MIDI bytes also
494 for(i
= 0; i
<= (*it
).lastGood
; i
++)
495 t
.Data
[pos
+ i
] = (*it
).midiData
[i
];
503 /* Write all the MIDI packets to disk */
505 // Writing the header
509 convertLong2Array(h
.Length
, str
);
512 convertShort2Array(h
.Format
, str
);
514 // Write the NumTracks
515 convertShort2Array(h
.NumTracks
, str
);
517 // Hack hack, write the Division
518 convertShort2Array(h
.Division
, str
);
524 // Now writing the track
528 convertLong2Array(t
.Length
, str
);
530 // Copy the track data
531 writeBytes(t
.Data
, t
.Length
);
539 MidiFile::MidiFile(QString file
)
547 d
->prevNoteTime
= -1;
549 // Open the MIDI file to send output to
550 d
->file
= fopen(d
->filename
.toLatin1(), "wb");
552 errorMessage
= "Cannot open output MIDI file: " + d
->filename
;
556 // Set all the note endings to zero
557 for(int i
= 0; i
<= 127; i
++) {
561 // Start the timer so that we can end the notes
565 MidiFile::~MidiFile()
567 // Stop all the notes
571 struct Private::MTrk_Packet pak
;
573 pak
.midiData
[0] = 0xff;
574 pak
.midiData
[1] = 0x2f;
575 pak
.midiData
[2] = 0x00;
577 d
->packets
.append(pak
);
582 // Close the MIDI file
591 void MidiFile::setInstrument(int num
)
593 // Setup the MIDI packet
594 struct Private::MTrk_Packet pak
;
597 if(d
->prevNoteTime
== -1) // If we're the first note, start from 0, but don't let us count (only a patch change)
600 d
->prevNoteTime
= timer
;
602 pak
.midiData
[0] = 0xc0;
603 pak
.midiData
[1] = num
& 0xff;
604 pak
.lastGood
= 1; // Last byte with data
606 d
->packets
.append(pak
);
609 bool MidiFile::playNote(int num
, int vel
, int len
)
611 // Check the note and volume are in range
612 if(num
> 127 || num
< 0)
615 if(vel
> 127 || vel
< 0)
619 // Setup the MIDI packet
620 struct Private::MTrk_Packet pak
;
623 if(d
->prevNoteTime
== -1 && vel
!= 0) // If we're the first note, start from 0
624 d
->prevNoteTime
= timer
;
625 pak
.deltaTime
= timer
- d
->prevNoteTime
;
626 d
->prevNoteTime
= timer
;
629 // Setup the MIDI packet
630 pak
.midiData
[0] = 0x90;
631 pak
.midiData
[1] = num
;
632 pak
.midiData
[2] = vel
;
633 pak
.lastGood
= 2; // Last byte with good data
636 // Setup the MIDI packet
637 pak
.midiData
[0] = 0x80;
638 pak
.midiData
[1] = num
;
639 pak
.midiData
[2] = 64; // How quickly the note goes away
640 pak
.lastGood
= 2; // Last byte with good data
643 // Set it up to turn off
644 if(len
!= 0 && vel
!= 0) {
645 noteEndings
[num
] = timer
+ len
;
649 // printf("Appending note: %d with deltaTime: %d\n", num, pak.deltaTime);
651 d
->packets
.append(pak
);