1 /*
2 Standard MIDI File component
3 Copyright (C) 2006-2021, Pedro Lopez-Cabanillas <plcl@users.sf.net>
4
5 Based on midifile.c by Tim Thompson, M.Czeiszperger and Greg Lee
6
7 This library is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <QDataStream>
22 #include <QFile>
23 #include <QList>
24 #include <QTextCodec>
25 #include <cmath>
26 #include <drumstick/qsmf.h>
27 #include <limits>
28
29 DISABLE_WARNING_PUSH
30 DISABLE_WARNING_DEPRECATED_DECLARATIONS
31
32 /**
33 * @file qsmf.cpp
34 * Implementation of a class managing Standard MIDI Files input/output
35 */
36
37 namespace drumstick {
38 namespace File {
39
40 /**
41 * @addtogroup SMF
42 * @{
43 *
44 * QSmf provides a mechanism to parse and encode Standard MIDI Files, without
45 * the burden of a policy forcing to use some internal sequence representation.
46 *
47 * This class is not related or based on the ALSA library.
48 *
49 * @}
50 */
51
52 class QSmf::QSmfPrivate {
53 public:
QSmfPrivate()54 QSmfPrivate():
55 m_Interactive(false),
56 m_CurrTime(0),
57 m_RealTime(0),
58 m_DblRealTime(0),
59 m_DblOldRealtime(0),
60 m_Division(96),
61 m_CurrTempo(500000),
62 m_OldCurrTempo(500000),
63 m_OldRealTime(0),
64 m_OldCurrTime(0),
65 m_RevisedTime(0),
66 m_TempoChangeTime(0),
67 m_ToBeRead(0),
68 m_NumBytesWritten(0),
69 m_Tracks(0),
70 m_fileFormat(0),
71 m_LastStatus(0),
72 m_codec(nullptr),
73 m_IOStream(nullptr)
74 { }
75
76 bool m_Interactive; /**< file and track headers are not required */
77 quint64 m_CurrTime; /**< current time in delta-time units */
78 quint64 m_RealTime; /**< current time in 1/16 centisecond-time units */
79 double m_DblRealTime; /**< as above, floating */
80 double m_DblOldRealtime;
81 int m_Division; /**< ticks per beat. Default = 96 */
82 quint64 m_CurrTempo; /**< microseconds per quarter note */
83 quint64 m_OldCurrTempo;
84 quint64 m_OldRealTime;
85 quint64 m_OldCurrTime;
86 quint64 m_RevisedTime;
87 quint64 m_TempoChangeTime;
88 quint64 m_ToBeRead;
89 quint64 m_NumBytesWritten;
90 int m_Tracks;
91 int m_fileFormat;
92 int m_LastStatus;
93 QTextCodec *m_codec;
94 QDataStream *m_IOStream;
95 QByteArray m_MsgBuff;
96 QList<QSmfRecTempo> m_TempoList;
97 };
98
99 /**
100 * Constructor
101 * @param parent Optional parent object
102 */
QSmf(QObject * parent)103 QSmf::QSmf(QObject * parent) :
104 QObject(parent),
105 d(new QSmfPrivate)
106 { }
107
108 /**
109 * Destructor
110 */
~QSmf()111 QSmf::~QSmf()
112 {
113 d->m_TempoList.clear();
114 }
115
116 /**
117 * Check if the SMF stream is positioned at the end.
118 * @return True if the SMF stream is at the end
119 */
endOfSmf()120 bool QSmf::endOfSmf()
121 {
122 return d->m_IOStream->atEnd();
123 }
124
125 /**
126 * Gets a single byte from the SMF stream
127 * @return A Single byte
128 */
getByte()129 quint8 QSmf::getByte()
130 {
131 quint8 b = 0;
132 if (!endOfSmf())
133 {
134 *d->m_IOStream >> b;
135 d->m_ToBeRead--;
136 }
137 return b;
138 }
139
140 /**
141 * Puts a single byte to the SMF stream
142 * @param value A Single byte
143 */
putByte(quint8 value)144 void QSmf::putByte(quint8 value)
145 {
146 *d->m_IOStream << value;
147 d->m_NumBytesWritten++;
148 }
149
150 /**
151 * Adds a tempo change to the internal tempo list
152 * @param tempo Tempo in microseconds per quarter
153 * @param time Location in ticks
154 */
addTempo(quint64 tempo,quint64 time)155 void QSmf::addTempo(quint64 tempo, quint64 time)
156 {
157 QSmfRecTempo tempoRec;
158 tempoRec.tempo = tempo;
159 tempoRec.time = time;
160 d->m_TempoList.append(tempoRec);
161 }
162
163 /**
164 * Reads a SMF header
165 */
readHeader()166 void QSmf::readHeader()
167 {
168 d->m_CurrTime = 0;
169 d->m_RealTime = 0;
170 d->m_Division = 96;
171 d->m_CurrTempo = 500000;
172 d->m_OldCurrTempo = 500000;
173 addTempo(d->m_CurrTempo, 0);
174 if (d->m_Interactive)
175 {
176 d->m_fileFormat= 0;
177 d->m_Tracks = 1;
178 d->m_Division = 96;
179 }
180 else
181 {
182 readExpected("MThd");
183 d->m_ToBeRead = read32bit();
184 d->m_fileFormat = read16bit();
185 d->m_Tracks = read16bit();
186 d->m_Division = read16bit();
187 }
188 emit signalSMFHeader(d->m_fileFormat, d->m_Tracks, d->m_Division);
189
190 /* flush any extra stuff, in case the length of header is not */
191 while ((d->m_ToBeRead > 0) && !endOfSmf())
192 {
193 getByte();
194 }
195 if (d->m_ToBeRead > 0)
196 {
197 SMFError("Unexpected end of input");
198 }
199 }
200
201 /**
202 * Reads a track chunk
203 */
readTrack()204 void QSmf::readTrack()
205 {
206 /* This array is indexed by the high half of a status byte. It's
207 value is either the number of bytes needed (1 or 2) for a channel
208 message, or 0 (meaning it's not a channel message). */
209 static const quint8 chantype[16] =
210 { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0 };
211
212 quint64 lookfor;
213 quint8 c, c1, type;
214 bool sysexcontinue; // 1 if last message was an unfinished SysEx
215 bool running; // 1 when running status used
216 quint8 status; // status value (e.g. 0x90==note-on)
217 int needed;
218 double delta_secs;
219 quint64 delta_ticks, save_time, save_tempo;
220
221 sysexcontinue = false;
222 status = 0;
223 if (d->m_Interactive)
224 {
225 d->m_ToBeRead = std::numeric_limits<unsigned long long>::max();
226 }
227 else
228 {
229 readExpected("MTrk");
230 d->m_ToBeRead = read32bit();
231 }
232 d->m_CurrTime = 0;
233 d->m_RealTime = 0;
234 d->m_DblRealTime = 0;
235 d->m_DblOldRealtime = 0;
236 d->m_OldCurrTime = 0;
237 d->m_OldRealTime = 0;
238 d->m_CurrTempo = findTempo();
239
240 emit signalSMFTrackStart();
241
242 while (!endOfSmf() && (d->m_Interactive || d->m_ToBeRead > 0))
243 {
244 lookfor = 0;
245 if (d->m_Interactive)
246 {
247 d->m_CurrTime++;
248 }
249 else
250 {
251 delta_ticks = unsigned(readVarLen());
252 d->m_RevisedTime = d->m_CurrTime;
253 d->m_CurrTime += delta_ticks;
254 while (d->m_RevisedTime < d->m_CurrTime)
255 {
256 save_time = d->m_RevisedTime;
257 save_tempo = d->m_CurrTempo;
258 d->m_CurrTempo = findTempo();
259 if (d->m_CurrTempo != d->m_OldCurrTempo)
260 {
261 d->m_OldCurrTempo = d->m_CurrTempo;
262 d->m_OldRealTime = d->m_RealTime;
263 if (d->m_RevisedTime != d->m_TempoChangeTime)
264 {
265 d->m_DblOldRealtime = d->m_DblRealTime;
266 d->m_OldCurrTime = save_time;
267 }
268 delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime,
269 quint16(d->m_Division), save_tempo);
270 d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0;
271 d->m_RealTime = llround(d->m_DblRealTime);
272 if (d->m_RevisedTime == d->m_TempoChangeTime)
273 {
274 d->m_OldCurrTime = d->m_RevisedTime;
275 d->m_DblOldRealtime = d->m_DblRealTime;
276 }
277 }
278 else
279 {
280 delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime,
281 quint16(d->m_Division), d->m_CurrTempo);
282 d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0;
283 d->m_RealTime = llround(d->m_DblRealTime);
284 }
285 }
286 }
287
288 c = getByte();
289 if (sysexcontinue && (c != end_of_sysex))
290 {
291 SMFError("didn't find expected continuation of a SysEx");
292 }
293 if (c < 0xf8)
294 {
295 if ((c & 0x80) == 0)
296 {
297 if (status == 0)
298 {
299 SMFError("unexpected running status");
300 }
301 running = true;
302 }
303 else
304 {
305 status = c;
306 running = false;
307 }
308 needed = chantype[status >> 4 & 0x0f];
309 if (needed != 0)
310 {
311 if (running)
312 {
313 c1 = c;
314 }
315 else
316 {
317 c1 = getByte();
318 }
319 if (needed > 1)
320 {
321 channelMessage(status, c1, getByte());
322 }
323 else
324 {
325 channelMessage(status, c1, 0);
326 }
327 continue;
328 }
329 }
330
331 switch (c)
332 {
333 case meta_event:
334 type = getByte();
335 lookfor = quint64(readVarLen());
336 lookfor = d->m_ToBeRead - lookfor;
337 msgInit();
338 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
339 {
340 msgAdd(getByte());
341 }
342 metaEvent(type);
343 break;
344 case system_exclusive:
345 lookfor = quint64(readVarLen());
346 lookfor = d->m_ToBeRead - lookfor;
347 msgInit();
348 msgAdd(system_exclusive);
349 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
350 {
351 c = getByte();
352 msgAdd(c);
353 }
354 if (c == end_of_sysex)
355 {
356 sysEx();
357 }
358 else
359 {
360 sysexcontinue = true;
361 }
362 break;
363 case end_of_sysex:
364 lookfor = readVarLen();
365 lookfor = d->m_ToBeRead - lookfor;
366 if (!sysexcontinue)
367 {
368 msgInit();
369 }
370 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
371 {
372 c = getByte();
373 msgAdd(c);
374 }
375 if (sysexcontinue)
376 {
377 if (c == end_of_sysex)
378 {
379 sysEx();
380 sysexcontinue = false;
381 }
382 }
383 break;
384 default:
385 badByte(c, d->m_IOStream->device()->pos() - 1);
386 break;
387 }
388 if ((d->m_ToBeRead > lookfor) && endOfSmf())
389 {
390 SMFError("Unexpected end of input");
391 }
392 }
393 emit signalSMFTrackEnd();
394 }
395
396 /**
397 * Reads a SMF stream.
398 */
SMFRead()399 void QSmf::SMFRead()
400 {
401 int i;
402 readHeader();
403 for ( i = d->m_Tracks; (i > 0) && !endOfSmf(); i--)
404 {
405 readTrack();
406 }
407 }
408
409 /**
410 * Writes a SMF stream.
411 *
412 * Every MIDI file starts with a header.
413 * In format 1 files, the first track is a tempo map.
414 * The rest of the file is a series of tracks
415 */
SMFWrite()416 void QSmf::SMFWrite()
417 {
418 int i;
419 d->m_LastStatus = 0;
420 writeHeaderChunk(d->m_fileFormat, d->m_Tracks, d->m_Division);
421 d->m_LastStatus = 0;
422 if (d->m_fileFormat == 1)
423 {
424 emit signalSMFWriteTempoTrack();
425 }
426 for (i = 0; i < d->m_Tracks; ++i)
427 {
428 writeTrackChunk(i);
429 }
430 }
431
432 /**
433 * Reads a SMF stream.
434 * @param stream Pointer to an existing and opened stream
435 */
readFromStream(QDataStream * stream)436 void QSmf::readFromStream(QDataStream *stream)
437 {
438 d->m_IOStream = stream;
439 SMFRead();
440 }
441
442 /**
443 * Reads a SMF stream from a disk file.
444 * @param fileName Name of an existing file.
445 */
readFromFile(const QString & fileName)446 void QSmf::readFromFile(const QString& fileName)
447 {
448 QFile file(fileName);
449 file.open(QIODevice::ReadOnly);
450 QDataStream ds(&file);
451 readFromStream(&ds);
452 file.close();
453 }
454
455 /**
456 * Writes a SMF stream
457 * @param stream Pointer to an existing and opened stream
458 */
writeToStream(QDataStream * stream)459 void QSmf::writeToStream(QDataStream *stream)
460 {
461 d->m_IOStream = stream;
462 SMFWrite();
463 }
464
465 /**
466 * Writes a SMF stream to a disk file
467 * @param fileName File name
468 */
writeToFile(const QString & fileName)469 void QSmf::writeToFile(const QString& fileName)
470 {
471 QFile file(fileName);
472 file.open(QIODevice::WriteOnly);
473 QDataStream ds(&file);
474 writeToStream(&ds);
475 file.close();
476 }
477
478 /**
479 * Writes a SMF header chuck
480 * @param format SMF Format (0/1/2)
481 * @param ntracks Number of tracks
482 * @param division Resolution in ticks per quarter note
483 */
writeHeaderChunk(int format,int ntracks,int division)484 void QSmf::writeHeaderChunk(int format, int ntracks, int division)
485 {
486 write32bit(MThd);
487 write32bit(6);
488 write16bit(quint16(format));
489 write16bit(quint16(ntracks));
490 write16bit(quint16(division));
491 }
492
493 /**
494 * Writes a track chuck
495 * @param track Number of the track
496 */
writeTrackChunk(int track)497 void QSmf::writeTrackChunk(int track)
498 {
499 quint32 trkhdr;
500 quint32 trklength;
501 qint64 offset;
502 qint64 place_marker;
503
504 d->m_LastStatus = 0;
505 trkhdr = MTrk;
506 trklength = 0;
507 offset = d->m_IOStream->device()->pos();
508 write32bit(trkhdr);
509 write32bit(trklength);
510 d->m_NumBytesWritten = 0;
511
512 emit signalSMFWriteTrack(track);
513
514 place_marker = d->m_IOStream->device()->pos();
515 d->m_IOStream->device()->seek(offset);
516 trklength = d->m_NumBytesWritten;
517 write32bit(trkhdr);
518 write32bit(trklength);
519 d->m_IOStream->device()->seek(place_marker);
520 }
521
522 /**
523 * Writes a variable length Meta Event
524 * @param deltaTime Time offset in ticks
525 * @param type Meta event type
526 * @param data Message data
527 */
writeMetaEvent(long deltaTime,int type,const QByteArray & data)528 void QSmf::writeMetaEvent(long deltaTime, int type, const QByteArray& data)
529 {
530 writeVarLen(deltaTime);
531 d->m_LastStatus = meta_event;
532 putByte(d->m_LastStatus);
533 putByte(type);
534 writeVarLen(data.size());
535 foreach(char byte, data)
536 putByte(byte);
537 }
538
539 /**
540 * Writes a Text Meta Event
541 * @param deltaTime Time offset in ticks
542 * @param type Meta event type
543 * @param data Message text
544 */
writeMetaEvent(long deltaTime,int type,const QString & data)545 void QSmf::writeMetaEvent(long deltaTime, int type, const QString& data)
546 {
547 writeVarLen(deltaTime);
548 putByte(d->m_LastStatus = meta_event);
549 putByte(type);
550 QByteArray lcldata;
551 if (d->m_codec == nullptr)
552 lcldata = data.toLatin1();
553 else
554 lcldata = d->m_codec->fromUnicode(data);
555 writeVarLen(lcldata.length());
556 foreach(char byte, lcldata)
557 putByte(byte);
558 }
559
560 /**
561 * Writes a simple Meta event
562 * @param deltaTime Time offset in ticks
563 * @param type Meta event type
564 * @param data Meta event data
565 * @since 0.2.0
566 */
writeMetaEvent(long deltaTime,int type,int data)567 void QSmf::writeMetaEvent(long deltaTime, int type, int data)
568 {
569 writeVarLen(deltaTime);
570 putByte(d->m_LastStatus = meta_event);
571 putByte(type);
572 putByte(1);
573 putByte(data);
574 }
575
576 /**
577 * Writes a simple Meta event
578 * @param deltaTime Time offset in ticks
579 * @param type Meta event type
580 */
writeMetaEvent(long deltaTime,int type)581 void QSmf::writeMetaEvent(long deltaTime, int type)
582 {
583 writeVarLen(deltaTime);
584 putByte(d->m_LastStatus = meta_event);
585 putByte(type);
586 putByte(0);
587 }
588
589 /**
590 * Writes a variable length MIDI message
591 * @param deltaTime Time offset in ticks
592 * @param type MIDI event type
593 * @param chan MIDI Channel
594 * @param data Message data
595 */
writeMidiEvent(long deltaTime,int type,int chan,const QByteArray & data)596 void QSmf::writeMidiEvent(long deltaTime, int type, int chan,
597 const QByteArray& data)
598 {
599 unsigned int i, j, size;
600 quint8 c;
601 writeVarLen(quint64(deltaTime));
602 if ((type == system_exclusive) || (type == end_of_sysex))
603 {
604 c = type;
605 d->m_LastStatus = 0;
606 }
607 else
608 {
609 if (chan > 15)
610 {
611 SMFError("error: MIDI channel greater than 16");
612 }
613 c = type | chan;
614 }
615 if (d->m_LastStatus != c)
616 {
617 d->m_LastStatus = c;
618 putByte(c);
619 }
620 c = quint8(data[0]);
621 if (type == system_exclusive || type == end_of_sysex)
622 {
623 size = data.size();
624 if (type == c)
625 --size;
626 writeVarLen(size);
627 }
628 j = (c == type ? 1 : 0);
629 for (i = j; i < unsigned(data.size()); ++i)
630 {
631 putByte(quint8(data[i]));
632 }
633 }
634
635 /**
636 * Writes a MIDI message with a single parameter
637 * @param deltaTime Time offset in ticks
638 * @param type MIDI event type
639 * @param chan MIDI Channel
640 * @param b1 Message parameter
641 */
writeMidiEvent(long deltaTime,int type,int chan,int b1)642 void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1)
643 {
644 quint8 c;
645 writeVarLen(deltaTime);
646 if ((type == system_exclusive) || (type == end_of_sysex))
647 {
648 SMFError("error: Wrong method for a system exclusive event");
649 }
650 if (chan > 15)
651 {
652 SMFError("error: MIDI channel greater than 16");
653 }
654 c = type | chan;
655 if (d->m_LastStatus != c)
656 {
657 d->m_LastStatus = c;
658 putByte(c);
659 }
660 putByte(b1);
661 }
662
663 /**
664 * Writes a MIDI message with two parameters
665 * @param deltaTime Time offset in ticks
666 * @param type MIDI event type
667 * @param chan MIDI Channel
668 * @param b1 Message parameter 1
669 * @param b2 Message parameter 2
670 */
writeMidiEvent(long deltaTime,int type,int chan,int b1,int b2)671 void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1, int b2)
672 {
673 quint8 c;
674 writeVarLen(deltaTime);
675 if ((type == system_exclusive) || (type == end_of_sysex))
676 {
677 SMFError("error: Wrong method for a system exclusive event");
678 }
679 if (chan > 15)
680 {
681 SMFError("error: MIDI channel greater than 16");
682 }
683 c = type | chan;
684 if (d->m_LastStatus != c)
685 {
686 d->m_LastStatus = c;
687 putByte(c);
688 }
689 putByte(b1);
690 putByte(b2);
691 }
692
693 /**
694 * Writes a variable length MIDI message
695 * @param deltaTime Time offset in ticks
696 * @param type MIDI event type
697 * @param len Message length
698 * @param data Message data
699 */
writeMidiEvent(long deltaTime,int type,long len,char * data)700 void QSmf::writeMidiEvent(long deltaTime, int type, long len, char* data)
701 {
702 unsigned int i, j, size;
703 quint8 c;
704 writeVarLen(quint64(deltaTime));
705 if ((type != system_exclusive) && (type != end_of_sysex))
706 {
707 SMFError("error: type should be system exclusive");
708 }
709 d->m_LastStatus = 0;
710 c = quint8(type);
711 putByte(c);
712 size = unsigned(len);
713 c = quint8(data[0]);
714 if (c == type)
715 --size;
716 writeVarLen(size);
717 j = (c == type ? 1 : 0);
718 for (i = j; i < unsigned(len); ++i)
719 {
720 putByte(quint8(data[i]));
721 }
722 }
723
724 /**
725 * Writes a MIDI Sequence number
726 * @param deltaTime Time offset in ticks
727 * @param seqnum Sequence number
728 */
writeSequenceNumber(long deltaTime,int seqnum)729 void QSmf::writeSequenceNumber(long deltaTime, int seqnum)
730 {
731 writeVarLen(deltaTime);
732 d->m_LastStatus = meta_event;
733 putByte(d->m_LastStatus);
734 putByte(sequence_number);
735 putByte(2);
736 putByte((seqnum >> 8) & 0xff);
737 putByte(seqnum & 0xff);
738 }
739
740 /**
741 * Writes a Tempo change message
742 * @param deltaTime Time offset in ticks
743 * @param tempo Tempo in microseconds per quarter note
744 */
writeTempo(long deltaTime,long tempo)745 void QSmf::writeTempo(long deltaTime, long tempo)
746 {
747 writeVarLen(deltaTime);
748 putByte(d->m_LastStatus = meta_event);
749 putByte(set_tempo);
750 putByte(3);
751 putByte((tempo >> 16) & 0xff);
752 putByte((tempo >> 8) & 0xff);
753 putByte(tempo & 0xff);
754 }
755
756 /**
757 * Writes a Tempo change message
758 * @param deltaTime Time offset in ticks
759 * @param tempo Tempo expressed in quarter notes per minute
760 */
writeBpmTempo(long deltaTime,int tempo)761 void QSmf::writeBpmTempo(long deltaTime, int tempo)
762 {
763 long us_tempo = 60000000l / tempo;
764 writeTempo(deltaTime, us_tempo);
765 }
766
767 /**
768 * Writes a Time Signature message
769 * @param deltaTime Time offset in ticks
770 * @param num Numerator
771 * @param den Denominator (exponent for a power of two)
772 * @param cc Number of MIDI clocks in a metronome click
773 * @param bb Number of notated 32nd notes in 24 MIDI clocks
774 */
writeTimeSignature(long deltaTime,int num,int den,int cc,int bb)775 void QSmf::writeTimeSignature(long deltaTime, int num, int den, int cc, int bb)
776 {
777 writeVarLen(deltaTime);
778 putByte(d->m_LastStatus = meta_event);
779 putByte(time_signature);
780 putByte(4);
781 putByte(num & 0xff);
782 putByte(den & 0xff);
783 putByte(cc & 0xff);
784 putByte(bb & 0xff);
785 }
786
787 /**
788 * Writes a key Signature message
789 * @param deltaTime Time offset in ticks
790 * @param tone Number of alterations (positive=sharps, negative=flats)
791 * @param mode Scale mode (0=major, 1=minor)
792 */
writeKeySignature(long deltaTime,int tone,int mode)793 void QSmf::writeKeySignature(long deltaTime, int tone, int mode)
794 {
795 writeVarLen(quint64(deltaTime));
796 putByte(d->m_LastStatus = meta_event);
797 putByte(key_signature);
798 putByte(2);
799 putByte(quint8(tone));
800 putByte(mode & 0x01);
801 }
802
803 /**
804 * Writes multi-length bytes
805 * @param value Integer value
806 */
writeVarLen(quint64 value)807 void QSmf::writeVarLen(quint64 value)
808 {
809 quint64 buffer;
810
811 buffer = value & 0x7f;
812 while ((value >>= 7) > 0)
813 {
814 buffer <<= 8;
815 buffer |= 0x80;
816 buffer += (value & 0x7f);
817 }
818 while (true)
819 {
820 putByte(buffer & 0xff);
821 if (buffer & 0x80)
822 buffer >>= 8;
823 else
824 break;
825 }
826 }
827
828 /* These routines are used to make sure that the byte order of
829 the various data types remains constant between machines. */
write32bit(quint32 data)830 void QSmf::write32bit(quint32 data)
831 {
832 putByte((data >> 24) & 0xff);
833 putByte((data >> 16) & 0xff);
834 putByte((data >> 8) & 0xff);
835 putByte(data & 0xff);
836 }
837
write16bit(quint16 data)838 void QSmf::write16bit(quint16 data)
839 {
840 putByte((data >> 8) & 0xff);
841 putByte(data & 0xff);
842 }
843
to16bit(quint8 c1,quint8 c2)844 quint16 QSmf::to16bit(quint8 c1, quint8 c2)
845 {
846 quint16 value;
847 value = quint16(c1 << 8);
848 value += c2;
849 return value;
850 }
851
to32bit(quint8 c1,quint8 c2,quint8 c3,quint8 c4)852 quint32 QSmf::to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4)
853 {
854 quint32 value;
855 value = unsigned(c1 << 24);
856 value += unsigned(c2 << 16);
857 value += unsigned(c3 << 8);
858 value += c4;
859 return value;
860 }
861
read16bit()862 quint16 QSmf::read16bit()
863 {
864 quint8 c1, c2;
865 c1 = getByte();
866 c2 = getByte();
867 return to16bit(c1, c2);
868 }
869
read32bit()870 quint32 QSmf::read32bit()
871 {
872 quint8 c1, c2, c3, c4;
873 c1 = getByte();
874 c2 = getByte();
875 c3 = getByte();
876 c4 = getByte();
877 return to32bit(c1, c2, c3, c4);
878 }
879
readVarLen()880 long QSmf::readVarLen()
881 {
882 quint64 value;
883 quint8 c;
884
885 c = getByte();
886 value = c;
887 if ((c & 0x80) != 0)
888 {
889 value &= 0x7f;
890 do
891 {
892 c = getByte();
893 value = (value << 7) + (c & 0x7f);
894 } while ((c & 0x80) != 0);
895 }
896 return long(value);
897 }
898
readExpected(const QString & s)899 void QSmf::readExpected(const QString& s)
900 {
901 int j;
902 quint8 b;
903 for (j = 0; j < s.length(); ++j)
904 {
905 b = getByte();
906 if (QChar(b) != s[j])
907 {
908 SMFError(QString("Invalid (%1) SMF format at %2").arg(b, 0, 16).arg(d->m_IOStream->device()->pos()));
909 break;
910 }
911 }
912 }
913
findTempo()914 quint64 QSmf::findTempo()
915 {
916 quint64 result, old_tempo, new_tempo;
917 QSmfRecTempo rec = d->m_TempoList.last();
918 old_tempo = d->m_CurrTempo;
919 new_tempo = d->m_CurrTempo;
920 QList<QSmfRecTempo>::Iterator it;
921 for( it = d->m_TempoList.begin(); it != d->m_TempoList.end(); ++it )
922 {
923 rec = (*it);
924 if (rec.time <= d->m_CurrTime)
925 {
926 old_tempo = rec.tempo;
927 }
928 new_tempo = rec.tempo;
929 if (rec.time > d->m_RevisedTime)
930 {
931 break;
932 }
933 }
934 if ((rec.time <= d->m_RevisedTime) || (rec.time > d->m_CurrTime))
935 {
936 d->m_RevisedTime = d->m_CurrTime;
937 result = old_tempo;
938 }
939 else
940 {
941 d->m_RevisedTime = rec.time;
942 d->m_TempoChangeTime = d->m_RevisedTime;
943 result = new_tempo;
944 }
945 return result;
946 }
947
948 /* This routine converts delta times in ticks into seconds. The
949 else statement is needed because the formula is different for tracks
950 based on notes and tracks based on SMPTE times. */
ticksToSecs(quint64 ticks,quint16 division,quint64 tempo)951 double QSmf::ticksToSecs(quint64 ticks, quint16 division, quint64 tempo)
952 {
953 double result;
954 double smpte_format;
955 double smpte_resolution;
956
957 if (division > 0)
958 {
959 result = double(ticks * tempo)/(division * 1000000.0);
960 }
961 else
962 {
963 smpte_format = upperByte(division);
964 smpte_resolution = lowerByte(division);
965 result = double(ticks)/(smpte_format * smpte_resolution
966 * 1000000.0);
967 }
968 return result;
969 }
970
SMFError(const QString & s)971 void QSmf::SMFError(const QString& s)
972 {
973 emit signalSMFError(s);
974 }
975
channelMessage(quint8 status,quint8 c1,quint8 c2)976 void QSmf::channelMessage(quint8 status, quint8 c1, quint8 c2)
977 {
978 quint8 chan;
979 int k;
980 chan = status & midi_channel_mask;
981 if (c1 > 127)
982 {
983 SMFError(QString("ChannelMessage with bad c1 = %1").arg(c1));
984 //c1 &= 127;
985 }
986 if (c2 > 127)
987 {
988 SMFError(QString("ChannelMessage with bad c2 = %1").arg(c2));
989 //c2 &= 127;
990 }
991 switch (status & midi_command_mask)
992 {
993 case note_off:
994 emit signalSMFNoteOff(chan, c1, c2);
995 break;
996 case note_on:
997 emit signalSMFNoteOn(chan, c1, c2);
998 break;
999 case poly_aftertouch:
1000 emit signalSMFKeyPress(chan, c1, c2);
1001 break;
1002 case control_change:
1003 emit signalSMFCtlChange(chan, c1, c2);
1004 break;
1005 case program_chng:
1006 emit signalSMFProgram(chan, c1);
1007 break;
1008 case channel_aftertouch:
1009 emit signalSMFChanPress(chan, c1);
1010 break;
1011 case pitch_wheel:
1012 k = c1 + (c2 << 7) - 8192;
1013 emit signalSMFPitchBend(chan, k);
1014 break;
1015 default:
1016 SMFError(QString("Invalid MIDI status %1. Unhandled event").arg(status));
1017 break;
1018 }
1019 }
1020
metaEvent(quint8 b)1021 void QSmf::metaEvent(quint8 b)
1022 {
1023 QSmfRecTempo rec;
1024 QByteArray m(d->m_MsgBuff);
1025
1026 switch (b)
1027 {
1028 case sequence_number:
1029 emit signalSMFSequenceNum(to16bit(m[0], m[1]));
1030 break;
1031 case text_event:
1032 case copyright_notice:
1033 case sequence_name:
1034 case instrument_name:
1035 case lyric:
1036 case marker:
1037 case cue_point: {
1038 QString s;
1039 if (d->m_codec == nullptr) {
1040 emit signalSMFText2(b, m);
1041 } else {
1042 s = d->m_codec->toUnicode(m);
1043 emit signalSMFText(b, s);
1044 }
1045 }
1046 break;
1047 case forced_channel:
1048 emit signalSMFforcedChannel(m[0]);
1049 break;
1050 case forced_port:
1051 emit signalSMFforcedPort(m[0]);
1052 break;
1053 case end_of_track:
1054 emit signalSMFendOfTrack();
1055 break;
1056 case set_tempo:
1057 d->m_CurrTempo = to32bit(0, m[0], m[1], m[2]);
1058 emit signalSMFTempo(d->m_CurrTempo);
1059 rec = d->m_TempoList.last();
1060 if (rec.tempo == d->m_CurrTempo)
1061 {
1062 return;
1063 }
1064 if (rec.time > d->m_CurrTime)
1065 {
1066 return;
1067 }
1068 addTempo(d->m_CurrTempo, d->m_CurrTime);
1069 break;
1070 case smpte_offset:
1071 emit signalSMFSmpte(m[0], m[1], m[2], m[3], m[4]);
1072 break;
1073 case time_signature:
1074 emit signalSMFTimeSig(m[0], m[1], m[2], m[3]);
1075 break;
1076 case key_signature:
1077 emit signalSMFKeySig(m[0], m[1]);
1078 break;
1079 case sequencer_specific:
1080 emit signalSMFSeqSpecific(m);
1081 break;
1082 default:
1083 emit signalSMFMetaUnregistered(b, m);
1084 break;
1085 }
1086 emit signalSMFMetaMisc(b, m);
1087 }
1088
sysEx()1089 void QSmf::sysEx()
1090 {
1091 QByteArray varr(d->m_MsgBuff);
1092 emit signalSMFSysex(varr);
1093 }
1094
badByte(quint8 b,int p)1095 void QSmf::badByte(quint8 b, int p)
1096 {
1097 SMFError(QString("Unexpected byte (%1) at %2").arg(b, 2, 16).arg(p));
1098 }
1099
lowerByte(quint16 x)1100 quint8 QSmf::lowerByte(quint16 x)
1101 {
1102 return (x & 0xff);
1103 }
1104
upperByte(quint16 x)1105 quint8 QSmf::upperByte(quint16 x)
1106 {
1107 return ((x >> 8) & 0xff);
1108 }
1109
msgInit()1110 void QSmf::msgInit()
1111 {
1112 d->m_MsgBuff.truncate(0);
1113 }
1114
msgAdd(quint8 b)1115 void QSmf::msgAdd(quint8 b)
1116 {
1117 int s = d->m_MsgBuff.size();
1118 d->m_MsgBuff.resize(s + 1);
1119 d->m_MsgBuff[s] = b;
1120 }
1121
1122 /* public properties (accessors) */
1123
1124 /**
1125 * Gets the current time in ticks
1126 * @return Time in ticks
1127 */
getCurrentTime()1128 long QSmf::getCurrentTime()
1129 {
1130 return d->m_CurrTime;
1131 }
1132
1133 /**
1134 * Gets the current tempo
1135 * @return Tempo in us per quarter
1136 */
getCurrentTempo()1137 long QSmf::getCurrentTempo()
1138 {
1139 return d->m_CurrTempo;
1140 }
1141
1142 /**
1143 * Gets the real time in seconds
1144 * @return Time in seconds
1145 */
getRealTime()1146 long QSmf::getRealTime()
1147 {
1148 return d->m_RealTime;
1149 }
1150
1151 /**
1152 * Gets the resolution
1153 * @return Resolution in ticks per quarter note
1154 */
getDivision()1155 int QSmf::getDivision()
1156 {
1157 return d->m_Division;
1158 }
1159
1160 /**
1161 * Sets the resolution
1162 * @param division Resolution in ticks per quarter note
1163 */
setDivision(int division)1164 void QSmf::setDivision(int division)
1165 {
1166 d->m_Division = division;
1167 }
1168
1169 /**
1170 * Gets the number of tracks
1171 * @return Number of tracks
1172 */
getTracks()1173 int QSmf::getTracks()
1174 {
1175 return d->m_Tracks;
1176 }
1177
1178 /**
1179 * Sets the number of tracks
1180 * @param tracks Number of tracks
1181 */
setTracks(int tracks)1182 void QSmf::setTracks(int tracks)
1183 {
1184 d->m_Tracks = tracks;
1185 }
1186
1187 /**
1188 * Gets the SMF file format
1189 * @return File format (0, 1, or 2)
1190 */
getFileFormat()1191 int QSmf::getFileFormat()
1192 {
1193 return d->m_fileFormat;
1194 }
1195
1196 /**
1197 * Sets the SMF file format
1198 * @param fileFormat File format (0, 1, or 2)
1199 */
setFileFormat(int fileFormat)1200 void QSmf::setFileFormat(int fileFormat)
1201 {
1202 d->m_fileFormat = fileFormat;
1203 }
1204
1205 /**
1206 * Gets the position in the SMF stream
1207 * @return Position offset in the stream
1208 */
getFilePos()1209 long QSmf::getFilePos()
1210 {
1211 return long(d->m_IOStream->device()->pos());
1212 }
1213
1214 /**
1215 * Gets the text codec used for text meta-events I/O
1216 * @return QTextCodec pointer
1217 * @since 0.2.0
1218 * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
1219 */
getTextCodec()1220 QTextCodec* QSmf::getTextCodec()
1221 {
1222 return d->m_codec;
1223 }
1224
1225 /**
1226 * Sets the text codec for text meta-events.
1227 * The engine doesn't take ownership of the codec instance.
1228 *
1229 * @param codec QTextCodec pointer
1230 * @since 0.2.0
1231 * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
1232 */
setTextCodec(QTextCodec * codec)1233 void QSmf::setTextCodec(QTextCodec *codec)
1234 {
1235 d->m_codec = codec;
1236 }
1237
1238 /**
1239 * @brief drumstickLibraryVersion provides the Drumstick version as an edited QString
1240 * @return Drumstick library version
1241 */
drumstickLibraryVersion()1242 QString drumstickLibraryVersion()
1243 {
1244 return QStringLiteral(QT_STRINGIFY(VERSION));
1245 }
1246
1247 } // namespace File
1248 } // namespace drumstick
1249
1250 DISABLE_WARNING_POP
1251