drumstick 2.11.1
C++ MIDI libraries using Qt objects, idioms, and style.
qsmf.cpp
Go to the documentation of this file.
1/*
2 Standard MIDI File component
3 Copyright (C) 2006-2026, 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 <QDebug>
23#include <QFile>
24#include <QList>
25#include <QTextCodec>
26
27#include <cmath>
28#include <drumstick/qsmf.h>
29#include <limits>
30
31DISABLE_WARNING_PUSH
32DISABLE_WARNING_DEPRECATED_DECLARATIONS
33
38
39namespace drumstick {
40namespace File {
41
53
54class QSmf::QSmfPrivate {
55public:
56 QSmfPrivate():
57 m_Interactive(false),
58 m_CurrTime(0),
59 m_RealTime(0),
60 m_DblRealTime(0),
61 m_DblOldRealtime(0),
62 m_Division(96),
63 m_CurrTempo(500000),
64 m_OldCurrTempo(500000),
65 m_OldRealTime(0),
66 m_OldCurrTime(0),
67 m_RevisedTime(0),
68 m_TempoChangeTime(0),
69 m_ToBeRead(0),
70 m_NumBytesWritten(0),
71 m_Tracks(0),
72 m_fileFormat(0),
73 m_LastStatus(0),
74 m_codec(nullptr),
75 m_IOStream(nullptr)
76 { }
77
78 bool m_Interactive;
79 quint64 m_CurrTime;
80 quint64 m_RealTime;
81 double m_DblRealTime;
82 double m_DblOldRealtime;
83 int m_Division;
84 quint64 m_CurrTempo;
85 quint64 m_OldCurrTempo;
86 quint64 m_OldRealTime;
87 quint64 m_OldCurrTime;
88 quint64 m_RevisedTime;
89 quint64 m_TempoChangeTime;
90 quint64 m_ToBeRead;
91 quint64 m_NumBytesWritten;
92 int m_Tracks;
93 int m_fileFormat;
94 int m_LastStatus;
95 QTextCodec *m_codec;
96 QDataStream *m_IOStream;
97 QByteArray m_MsgBuff;
98 QList<QSmfRecTempo> m_TempoList;
99};
100
106 QObject(parent),
107 d(new QSmfPrivate)
108{ }
109
114{
115 d->m_TempoList.clear();
116}
117
122bool QSmf::endOfSmf()
123{
124 return d->m_IOStream->atEnd();
125}
126
131quint8 QSmf::getByte()
132{
133 quint8 b = 0;
134 if (!endOfSmf())
135 {
136 *d->m_IOStream >> b;
137 d->m_ToBeRead--;
138 }
139 return b;
140}
141
146void QSmf::putByte(quint8 value)
147{
148 *d->m_IOStream << value;
149 d->m_NumBytesWritten++;
150}
151
157void QSmf::addTempo(quint64 tempo, quint64 time)
158{
159 QSmfRecTempo tempoRec;
160 tempoRec.tempo = tempo;
161 tempoRec.time = time;
162 d->m_TempoList.append(tempoRec);
163}
164
168void QSmf::readHeader()
169{
170 d->m_CurrTime = 0;
171 d->m_RealTime = 0;
172 d->m_Division = 96;
173 d->m_CurrTempo = 500000;
174 d->m_OldCurrTempo = 500000;
175 addTempo(d->m_CurrTempo, 0);
176 if (d->m_Interactive)
177 {
178 d->m_fileFormat= 0;
179 d->m_Tracks = 1;
180 d->m_Division = 96;
181 }
182 else
183 {
184 readExpected("MThd");
185 d->m_ToBeRead = read32bit();
186 d->m_fileFormat = read16bit();
187 d->m_Tracks = read16bit();
188 d->m_Division = read16bit();
189 }
190 Q_EMIT signalSMFHeader(d->m_fileFormat, d->m_Tracks, d->m_Division);
191
192 /* flush any extra stuff, in case the length of header is not */
193 while ((d->m_ToBeRead > 0) && !endOfSmf())
194 {
195 getByte();
196 }
197 if (d->m_ToBeRead > 0)
198 {
199 SMFError("Unexpected end of input");
200 }
201}
202
206void QSmf::readTrack()
207{
208 /* This array is indexed by the high half of a status byte. It's
209 value is either the number of bytes needed (1 or 2) for a channel
210 message, or 0 (meaning it's not a channel message). */
211 static const quint8 chantype[16] =
212 { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0 };
213
214 quint64 lookfor;
215 quint8 c, c1, type;
216 bool sysexcontinue; // true if last message was an unfinished SysEx
217 bool running; // true when running status is used
218 quint8 status; // status value (e.g. 0x90==note-on)
219 int needed;
220 double delta_secs;
221 quint64 delta_ticks, save_time, save_tempo;
222
223 sysexcontinue = false;
224 status = 0;
225 if (d->m_Interactive)
226 {
227 d->m_ToBeRead = std::numeric_limits<unsigned long long>::max();
228 }
229 else
230 {
231 readExpected("MTrk");
232 d->m_ToBeRead = read32bit();
233 }
234 d->m_CurrTime = 0;
235 d->m_RealTime = 0;
236 d->m_DblRealTime = 0;
237 d->m_DblOldRealtime = 0;
238 d->m_OldCurrTime = 0;
239 d->m_OldRealTime = 0;
240 d->m_CurrTempo = findTempo();
241
242 Q_EMIT signalSMFTrackStart();
243
244 while (!endOfSmf() && (d->m_Interactive || d->m_ToBeRead > 0))
245 {
246 lookfor = 0;
247 if (d->m_Interactive)
248 {
249 d->m_CurrTime++;
250 }
251 else
252 {
253 delta_ticks = unsigned(readVarLen());
254 d->m_RevisedTime = d->m_CurrTime;
255 d->m_CurrTime += delta_ticks;
256 while (d->m_RevisedTime < d->m_CurrTime)
257 {
258 save_time = d->m_RevisedTime;
259 save_tempo = d->m_CurrTempo;
260 d->m_CurrTempo = findTempo();
261 if (d->m_CurrTempo != d->m_OldCurrTempo)
262 {
263 d->m_OldCurrTempo = d->m_CurrTempo;
264 d->m_OldRealTime = d->m_RealTime;
265 if (d->m_RevisedTime != d->m_TempoChangeTime)
266 {
267 d->m_DblOldRealtime = d->m_DblRealTime;
268 d->m_OldCurrTime = save_time;
269 }
270 delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime,
271 quint16(d->m_Division), save_tempo);
272 d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0;
273 d->m_RealTime = llround(d->m_DblRealTime);
274 if (d->m_RevisedTime == d->m_TempoChangeTime)
275 {
276 d->m_OldCurrTime = d->m_RevisedTime;
277 d->m_DblOldRealtime = d->m_DblRealTime;
278 }
279 }
280 else
281 {
282 delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime,
283 quint16(d->m_Division), d->m_CurrTempo);
284 d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0;
285 d->m_RealTime = llround(d->m_DblRealTime);
286 }
287 }
288 }
289
290 c = getByte();
291 if (sysexcontinue && (c != end_of_sysex))
292 {
293 SMFError("didn't find expected continuation of a SysEx");
294 }
295 if (c < 0xf8)
296 {
297 if ((c & 0x80) == 0)
298 {
299 if (status == 0)
300 {
301 SMFError("unexpected running status");
302 }
303 running = true;
304 }
305 else
306 {
307 status = c;
308 running = false;
309 }
310 needed = chantype[status >> 4 & 0x0f];
311 if (needed != 0)
312 {
313 if (running)
314 {
315 c1 = c;
316 }
317 else
318 {
319 c1 = getByte();
320 }
321 if (needed > 1)
322 {
323 channelMessage(status, c1, getByte());
324 }
325 else
326 {
327 channelMessage(status, c1, 0);
328 }
329 continue;
330 }
331 }
332
333 switch (c)
334 {
335 case meta_event:
336 type = getByte();
337 lookfor = quint64(readVarLen());
338 lookfor = d->m_ToBeRead - lookfor;
339 msgInit();
340 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
341 {
342 msgAdd(getByte());
343 }
344 metaEvent(type);
345 break;
346 case system_exclusive:
347 lookfor = quint64(readVarLen());
348 lookfor = d->m_ToBeRead - lookfor;
349 msgInit();
350 msgAdd(system_exclusive);
351 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
352 {
353 c = getByte();
354 msgAdd(c);
355 }
356 if (c == end_of_sysex)
357 {
358 sysEx();
359 }
360 else
361 {
362 sysexcontinue = true;
363 }
364 break;
365 case end_of_sysex:
366 lookfor = readVarLen();
367 lookfor = d->m_ToBeRead - lookfor;
368 if (!sysexcontinue)
369 {
370 msgInit();
371 }
372 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
373 {
374 c = getByte();
375 msgAdd(c);
376 }
377 if (sysexcontinue)
378 {
379 if (c == end_of_sysex)
380 {
381 sysEx();
382 sysexcontinue = false;
383 }
384 }
385 break;
386 default:
387 badByte(c, d->m_IOStream->device()->pos() - 1);
388 break;
389 }
390 if ((d->m_ToBeRead > lookfor) && endOfSmf()) {
391 SMFError("Unexpected end of input");
392 }
393 }
394 if (d->m_ToBeRead > 0) {
395 SMFError(QStringLiteral("Track ended before reading last %1 bytes").arg(d->m_ToBeRead));
396 }
397 Q_EMIT signalSMFTrackEnd();
398}
399
403void QSmf::SMFRead()
404{
405 int i;
406 readHeader();
407 for ( i = d->m_Tracks; (i > 0) && !endOfSmf(); i--)
408 {
409 readTrack();
410 }
411 if (i > 0) {
412 SMFError(
413 QStringLiteral("%1 tracks out of a total of %2 are missing").arg(i).arg(d->m_Tracks));
414 }
415}
416
424void QSmf::SMFWrite()
425{
426 int i;
427 d->m_LastStatus = 0;
428 writeHeaderChunk(d->m_fileFormat, d->m_Tracks, d->m_Division);
429 d->m_LastStatus = 0;
430 if (d->m_fileFormat == 1)
431 {
433 }
434 for (i = 0; i < d->m_Tracks; ++i)
435 {
436 writeTrackChunk(i);
437 }
438}
439
444void QSmf::readFromStream(QDataStream *stream)
445{
446 d->m_IOStream = stream;
447 SMFRead();
448}
449
454void QSmf::readFromFile(const QString& fileName)
455{
456 QFile file(fileName);
457 auto ok = file.open(QIODevice::ReadOnly);
458 if (ok) {
459 QDataStream ds(&file);
460 readFromStream(&ds);
461 file.close();
462 } else {
463 qCritical() << "cannot open file" << fileName;
464 }
465}
466
471void QSmf::writeToStream(QDataStream *stream)
472{
473 d->m_IOStream = stream;
474 SMFWrite();
475}
476
481void QSmf::writeToFile(const QString& fileName)
482{
483 QFile file(fileName);
484 auto ok = file.open(QIODevice::WriteOnly);
485 if (ok) {
486 QDataStream ds(&file);
487 writeToStream(&ds);
488 file.close();
489 } else {
490 qCritical() << "cannot open file" << fileName;
491 }
492}
493
500void QSmf::writeHeaderChunk(int format, int ntracks, int division)
501{
502 write32bit(MThd);
503 write32bit(6);
504 write16bit(quint16(format));
505 write16bit(quint16(ntracks));
506 write16bit(quint16(division));
507}
508
513void QSmf::writeTrackChunk(int track)
514{
515 quint32 trkhdr;
516 quint32 trklength;
517 qint64 offset;
518 qint64 place_marker;
519
520 d->m_LastStatus = 0;
521 trkhdr = MTrk;
522 trklength = 0;
523 offset = d->m_IOStream->device()->pos();
524 write32bit(trkhdr);
525 write32bit(trklength);
526 d->m_NumBytesWritten = 0;
527
528 Q_EMIT signalSMFWriteTrack(track);
529
530 place_marker = d->m_IOStream->device()->pos();
531 d->m_IOStream->device()->seek(offset);
532 trklength = d->m_NumBytesWritten;
533 write32bit(trkhdr);
534 write32bit(trklength);
535 d->m_IOStream->device()->seek(place_marker);
536}
537
544void QSmf::writeMetaEvent(long deltaTime, int type, const QByteArray& data)
545{
546 writeVarLen(deltaTime);
547 d->m_LastStatus = meta_event;
548 putByte(d->m_LastStatus);
549 putByte(type);
550 writeVarLen(data.size());
551 foreach(char byte, data)
552 putByte(byte);
553}
554
561void QSmf::writeMetaEvent(long deltaTime, int type, const QString& data)
562{
563 writeVarLen(deltaTime);
564 putByte(d->m_LastStatus = meta_event);
565 putByte(type);
566 QByteArray lcldata;
567 if (d->m_codec == nullptr)
568 lcldata = data.toLatin1();
569 else
570 lcldata = d->m_codec->fromUnicode(data);
571 writeVarLen(lcldata.length());
572 foreach(char byte, lcldata)
573 putByte(byte);
574}
575
583void QSmf::writeMetaEvent(long deltaTime, int type, int data)
584{
585 writeVarLen(deltaTime);
586 putByte(d->m_LastStatus = meta_event);
587 putByte(type);
588 putByte(1);
589 putByte(data);
590}
591
597void QSmf::writeMetaEvent(long deltaTime, int type)
598{
599 writeVarLen(deltaTime);
600 putByte(d->m_LastStatus = meta_event);
601 putByte(type);
602 putByte(0);
603}
604
612void QSmf::writeMidiEvent(long deltaTime, int type, int chan,
613 const QByteArray& data)
614{
615 unsigned int i, j, size;
616 quint8 c;
617 writeVarLen(quint64(deltaTime));
618 if ((type == system_exclusive) || (type == end_of_sysex))
619 {
620 c = type;
621 d->m_LastStatus = 0;
622 }
623 else
624 {
625 if (chan > 15)
626 {
627 SMFError("error: MIDI channel greater than 16");
628 }
629 c = type | chan;
630 }
631 if (d->m_LastStatus != c)
632 {
633 d->m_LastStatus = c;
634 putByte(c);
635 }
636 c = quint8(data[0]);
637 if (type == system_exclusive || type == end_of_sysex)
638 {
639 size = data.size();
640 if (type == c)
641 --size;
642 writeVarLen(size);
643 }
644 j = (c == type ? 1 : 0);
645 for (i = j; i < unsigned(data.size()); ++i)
646 {
647 putByte(quint8(data[i]));
648 }
649}
650
658void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1)
659{
660 quint8 c;
661 writeVarLen(deltaTime);
662 if ((type == system_exclusive) || (type == end_of_sysex))
663 {
664 SMFError("error: Wrong method for a system exclusive event");
665 }
666 if (chan > 15)
667 {
668 SMFError("error: MIDI channel greater than 16");
669 }
670 c = type | chan;
671 if (d->m_LastStatus != c)
672 {
673 d->m_LastStatus = c;
674 putByte(c);
675 }
676 putByte(b1);
677}
678
687void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1, int b2)
688{
689 quint8 c;
690 writeVarLen(deltaTime);
691 if ((type == system_exclusive) || (type == end_of_sysex))
692 {
693 SMFError("error: Wrong method for a system exclusive event");
694 }
695 if (chan > 15)
696 {
697 SMFError("error: MIDI channel greater than 16");
698 }
699 c = type | chan;
700 if (d->m_LastStatus != c)
701 {
702 d->m_LastStatus = c;
703 putByte(c);
704 }
705 putByte(b1);
706 putByte(b2);
707}
708
716void QSmf::writeMidiEvent(long deltaTime, int type, long len, char* data)
717{
718 unsigned int i, j, size;
719 quint8 c;
720 writeVarLen(quint64(deltaTime));
721 if ((type != system_exclusive) && (type != end_of_sysex))
722 {
723 SMFError("error: type should be system exclusive");
724 }
725 d->m_LastStatus = 0;
726 c = quint8(type);
727 putByte(c);
728 size = unsigned(len);
729 c = quint8(data[0]);
730 if (c == type)
731 --size;
732 writeVarLen(size);
733 j = (c == type ? 1 : 0);
734 for (i = j; i < unsigned(len); ++i)
735 {
736 putByte(quint8(data[i]));
737 }
738}
739
745void QSmf::writeSequenceNumber(long deltaTime, int seqnum)
746{
747 writeVarLen(deltaTime);
748 d->m_LastStatus = meta_event;
749 putByte(d->m_LastStatus);
750 putByte(sequence_number);
751 putByte(2);
752 putByte((seqnum >> 8) & 0xff);
753 putByte(seqnum & 0xff);
754}
755
761void QSmf::writeTempo(long deltaTime, long tempo)
762{
763 writeVarLen(deltaTime);
764 putByte(d->m_LastStatus = meta_event);
765 putByte(set_tempo);
766 putByte(3);
767 putByte((tempo >> 16) & 0xff);
768 putByte((tempo >> 8) & 0xff);
769 putByte(tempo & 0xff);
770}
771
777void QSmf::writeBpmTempo(long deltaTime, int tempo)
778{
779 long us_tempo = 60000000l / tempo;
780 writeTempo(deltaTime, us_tempo);
781}
782
791void QSmf::writeTimeSignature(long deltaTime, int num, int den, int cc, int bb)
792{
793 writeVarLen(deltaTime);
794 putByte(d->m_LastStatus = meta_event);
795 putByte(time_signature);
796 putByte(4);
797 putByte(num & 0xff);
798 putByte(den & 0xff);
799 putByte(cc & 0xff);
800 putByte(bb & 0xff);
801}
802
809void QSmf::writeKeySignature(long deltaTime, int tone, int mode)
810{
811 writeVarLen(quint64(deltaTime));
812 putByte(d->m_LastStatus = meta_event);
813 putByte(key_signature);
814 putByte(2);
815 putByte(quint8(tone));
816 putByte(mode & 0x01);
817}
818
823void QSmf::writeVarLen(quint64 value)
824{
825 quint64 buffer;
826
827 buffer = value & 0x7f;
828 while ((value >>= 7) > 0)
829 {
830 buffer <<= 8;
831 buffer |= 0x80;
832 buffer += (value & 0x7f);
833 }
834 while (true)
835 {
836 putByte(buffer & 0xff);
837 if (buffer & 0x80)
838 buffer >>= 8;
839 else
840 break;
841 }
842}
843
844/* These routines are used to make sure that the byte order of
845 the various data types remains constant between machines. */
846void QSmf::write32bit(quint32 data)
847{
848 putByte((data >> 24) & 0xff);
849 putByte((data >> 16) & 0xff);
850 putByte((data >> 8) & 0xff);
851 putByte(data & 0xff);
852}
853
854void QSmf::write16bit(quint16 data)
855{
856 putByte((data >> 8) & 0xff);
857 putByte(data & 0xff);
858}
859
860quint16 QSmf::to16bit(quint8 c1, quint8 c2)
861{
862 quint16 value;
863 value = quint16(c1 << 8);
864 value += c2;
865 return value;
866}
867
868quint32 QSmf::to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4)
869{
870 quint32 value;
871 value = unsigned(c1 << 24);
872 value += unsigned(c2 << 16);
873 value += unsigned(c3 << 8);
874 value += c4;
875 return value;
876}
877
878quint16 QSmf::read16bit()
879{
880 quint8 c1, c2;
881 c1 = getByte();
882 c2 = getByte();
883 return to16bit(c1, c2);
884}
885
886quint32 QSmf::read32bit()
887{
888 quint8 c1, c2, c3, c4;
889 c1 = getByte();
890 c2 = getByte();
891 c3 = getByte();
892 c4 = getByte();
893 return to32bit(c1, c2, c3, c4);
894}
895
896long QSmf::readVarLen()
897{
898 quint64 value;
899 quint8 c;
900
901 c = getByte();
902 value = c;
903 if ((c & 0x80) != 0)
904 {
905 value &= 0x7f;
906 do
907 {
908 c = getByte();
909 value = (value << 7) + (c & 0x7f);
910 } while ((c & 0x80) != 0);
911 }
912 return long(value);
913}
914
915void QSmf::readExpected(const QString& s)
916{
917 int j;
918 quint8 b;
919 for (j = 0; j < s.length(); ++j)
920 {
921 b = getByte();
922 if (QChar(b) != s[j])
923 {
924 SMFError(QString("Invalid (%1) SMF format at %2").arg(b, 0, 16).arg(d->m_IOStream->device()->pos()));
925 break;
926 }
927 }
928}
929
930quint64 QSmf::findTempo()
931{
932 quint64 result, old_tempo, new_tempo;
933 QSmfRecTempo rec = d->m_TempoList.last();
934 old_tempo = d->m_CurrTempo;
935 new_tempo = d->m_CurrTempo;
936 QList<QSmfRecTempo>::Iterator it;
937 for( it = d->m_TempoList.begin(); it != d->m_TempoList.end(); ++it )
938 {
939 rec = (*it);
940 if (rec.time <= d->m_CurrTime)
941 {
942 old_tempo = rec.tempo;
943 }
944 new_tempo = rec.tempo;
945 if (rec.time > d->m_RevisedTime)
946 {
947 break;
948 }
949 }
950 if ((rec.time <= d->m_RevisedTime) || (rec.time > d->m_CurrTime))
951 {
952 d->m_RevisedTime = d->m_CurrTime;
953 result = old_tempo;
954 }
955 else
956 {
957 d->m_RevisedTime = rec.time;
958 d->m_TempoChangeTime = d->m_RevisedTime;
959 result = new_tempo;
960 }
961 return result;
962}
963
964/* This routine converts delta times in ticks into seconds. The
965 else statement is needed because the formula is different for tracks
966 based on notes and tracks based on SMPTE times. */
967double QSmf::ticksToSecs(quint64 ticks, quint16 division, quint64 tempo)
968{
969 double result;
970 double smpte_format;
971 double smpte_resolution;
972
973 if (division > 0)
974 {
975 result = double(ticks * tempo)/(division * 1000000.0);
976 }
977 else
978 {
979 smpte_format = upperByte(division);
980 smpte_resolution = lowerByte(division);
981 result = double(ticks)/(smpte_format * smpte_resolution
982 * 1000000.0);
983 }
984 return result;
985}
986
987void QSmf::SMFError(const QString& s)
988{
989 Q_EMIT signalSMFError(s);
990}
991
992void QSmf::channelMessage(quint8 status, quint8 c1, quint8 c2)
993{
994 quint8 chan;
995 int k;
996 chan = status & midi_channel_mask;
997 if (c1 > 127)
998 {
999 SMFError(QString("ChannelMessage with bad c1 = %1").arg(c1));
1000 //c1 &= 127;
1001 }
1002 if (c2 > 127)
1003 {
1004 SMFError(QString("ChannelMessage with bad c2 = %1").arg(c2));
1005 //c2 &= 127;
1006 }
1007 switch (status & midi_command_mask)
1008 {
1009 case note_off:
1010 Q_EMIT signalSMFNoteOff(chan, c1, c2);
1011 break;
1012 case note_on:
1013 Q_EMIT signalSMFNoteOn(chan, c1, c2);
1014 break;
1015 case poly_aftertouch:
1016 Q_EMIT signalSMFKeyPress(chan, c1, c2);
1017 break;
1018 case control_change:
1019 Q_EMIT signalSMFCtlChange(chan, c1, c2);
1020 break;
1021 case program_chng:
1022 Q_EMIT signalSMFProgram(chan, c1);
1023 break;
1024 case channel_aftertouch:
1025 Q_EMIT signalSMFChanPress(chan, c1);
1026 break;
1027 case pitch_wheel:
1028 k = c1 + (c2 << 7) - 8192;
1029 Q_EMIT signalSMFPitchBend(chan, k);
1030 break;
1031 default:
1032 SMFError(QString("Invalid MIDI status %1. Unhandled event").arg(status));
1033 break;
1034 }
1035}
1036
1037void QSmf::metaEvent(quint8 b)
1038{
1039 QSmfRecTempo rec;
1040 QByteArray m(d->m_MsgBuff);
1041
1042 switch (b)
1043 {
1044 case sequence_number:
1045 Q_EMIT signalSMFSequenceNum(to16bit(m[0], m[1]));
1046 break;
1047 case text_event:
1048 case copyright_notice:
1049 case sequence_name:
1050 case instrument_name:
1051 case lyric:
1052 case marker:
1053 case cue_point: {
1054 QString s;
1055 if (d->m_codec == nullptr) {
1056 Q_EMIT signalSMFText2(b, m);
1057 } else {
1058 s = d->m_codec->toUnicode(m);
1059 Q_EMIT signalSMFText(b, s);
1060 }
1061 }
1062 break;
1063 case forced_channel:
1064 Q_EMIT signalSMFforcedChannel(m[0]);
1065 break;
1066 case forced_port:
1067 Q_EMIT signalSMFforcedPort(m[0]);
1068 break;
1069 case end_of_track:
1070 Q_EMIT signalSMFendOfTrack();
1071 break;
1072 case set_tempo:
1073 d->m_CurrTempo = to32bit(0, m[0], m[1], m[2]);
1074 Q_EMIT signalSMFTempo(d->m_CurrTempo);
1075 rec = d->m_TempoList.last();
1076 if (rec.tempo == d->m_CurrTempo)
1077 {
1078 return;
1079 }
1080 if (rec.time > d->m_CurrTime)
1081 {
1082 return;
1083 }
1084 addTempo(d->m_CurrTempo, d->m_CurrTime);
1085 break;
1086 case smpte_offset:
1087 Q_EMIT signalSMFSmpte(m[0], m[1], m[2], m[3], m[4]);
1088 break;
1089 case time_signature:
1090 Q_EMIT signalSMFTimeSig(m[0], m[1], m[2], m[3]);
1091 break;
1092 case key_signature:
1093 Q_EMIT signalSMFKeySig(m[0], m[1]);
1094 break;
1095 case sequencer_specific:
1096 Q_EMIT signalSMFSeqSpecific(m);
1097 break;
1098 default:
1099 Q_EMIT signalSMFMetaUnregistered(b, m);
1100 break;
1101 }
1102 Q_EMIT signalSMFMetaMisc(b, m);
1103}
1104
1105void QSmf::sysEx()
1106{
1107 QByteArray varr(d->m_MsgBuff);
1108 Q_EMIT signalSMFSysex(varr);
1109}
1110
1111void QSmf::badByte(quint8 b, int p)
1112{
1113 SMFError(QString("Unexpected byte (%1) at %2").arg(b, 2, 16).arg(p));
1114}
1115
1116quint8 QSmf::lowerByte(quint16 x)
1117{
1118 return (x & 0xff);
1119}
1120
1121quint8 QSmf::upperByte(quint16 x)
1122{
1123 return ((x >> 8) & 0xff);
1124}
1125
1126void QSmf::msgInit()
1127{
1128 d->m_MsgBuff.truncate(0);
1129}
1130
1131void QSmf::msgAdd(quint8 b)
1132{
1133 int s = d->m_MsgBuff.size();
1134 d->m_MsgBuff.resize(s + 1);
1135 d->m_MsgBuff[s] = b;
1136}
1137
1138/* public properties (accessors) */
1139
1145{
1146 return d->m_CurrTime;
1147}
1148
1154{
1155 return d->m_CurrTempo;
1156}
1157
1163{
1164 return d->m_RealTime;
1165}
1166
1172{
1173 return d->m_Division;
1174}
1175
1180void QSmf::setDivision(int division)
1181{
1182 d->m_Division = division;
1183}
1184
1190{
1191 return d->m_Tracks;
1192}
1193
1198void QSmf::setTracks(int tracks)
1199{
1200 d->m_Tracks = tracks;
1201}
1202
1208{
1209 return d->m_fileFormat;
1210}
1211
1216void QSmf::setFileFormat(int fileFormat)
1217{
1218 d->m_fileFormat = fileFormat;
1219}
1220
1226{
1227 return long(d->m_IOStream->device()->pos());
1228}
1229
1237{
1238 return d->m_codec;
1239}
1240
1249void QSmf::setTextCodec(QTextCodec *codec)
1250{
1251 d->m_codec = codec;
1252}
1253
1259{
1260 return QStringLiteral(QT_STRINGIFY(VERSION));
1261}
1262
1263} // namespace File
1264} // namespace drumstick
1265
1266DISABLE_WARNING_POP
The QObject class is the base class of all Qt objects.
int getTracks()
Gets the number of tracks.
Definition qsmf.cpp:1189
void signalSMFKeyPress(int chan, int pitch, int press)
Emitted after reading a Polyphonic Aftertouch message.
Q_DECL_DEPRECATED void setTextCodec(QTextCodec *codec)
Sets the text codec for text meta-events.
Definition qsmf.cpp:1249
long getRealTime()
Gets the real time in seconds.
Definition qsmf.cpp:1162
long getCurrentTempo()
Gets the current tempo.
Definition qsmf.cpp:1153
void signalSMFforcedChannel(int channel)
Emitted after reading a Forced channel message.
void setDivision(int division)
Sets the resolution.
Definition qsmf.cpp:1180
long getFilePos()
Gets the position in the SMF stream.
Definition qsmf.cpp:1225
void signalSMFforcedPort(int port)
Emitted after reading a Forced port message.
void signalSMFTimeSig(int b0, int b1, int b2, int b3)
Emitted after reading a SMF Time signature message.
void signalSMFKeySig(int b0, int b1)
Emitted after reading a SMF Key Signature smessage.
long getCurrentTime()
Gets the current time in ticks.
Definition qsmf.cpp:1144
void signalSMFChanPress(int chan, int press)
Emitted after reading a Channel Aftertouch message.
void setTracks(int tracks)
Sets the number of tracks.
Definition qsmf.cpp:1198
void writeTimeSignature(long deltaTime, int num, int den, int cc, int bb)
Writes a Time Signature message.
Definition qsmf.cpp:791
void signalSMFTrackEnd()
Emitted after a track has finished.
void signalSMFNoteOn(int chan, int pitch, int vol)
Emitted after reading a Note On message.
void signalSMFTempo(int tempo)
Emitted after reading a Tempo Change message.
void signalSMFWriteTrack(int track)
Emitted to request the user to write a track.
Q_DECL_DEPRECATED void signalSMFText(int typ, const QString &data)
Emitted after reading a SMF text message.
void signalSMFTrackStart()
Emitted after reading a track prefix.
void signalSMFError(const QString &errorStr)
Emitted for a SMF read or write error.
int getDivision()
Gets the resolution.
Definition qsmf.cpp:1171
QSmf(QObject *parent=nullptr)
Constructor.
Definition qsmf.cpp:105
void writeToFile(const QString &fileName)
Writes a SMF stream to a disk file.
Definition qsmf.cpp:481
void signalSMFSeqSpecific(const QByteArray &data)
Emitted after reading a Sequencer specific message.
void signalSMFWriteTempoTrack()
Emitted to request the user to prepare the tempo track.
void signalSMFendOfTrack()
Emitted after reading a End-Of-Track message.
void writeSequenceNumber(long deltaTime, int seqnum)
Writes a MIDI Sequence number.
Definition qsmf.cpp:745
void signalSMFHeader(int format, int ntrks, int division)
Emitted after reading a SMF header.
void readFromStream(QDataStream *stream)
Reads a SMF stream.
Definition qsmf.cpp:444
void signalSMFProgram(int chan, int patch)
Emitted after reading a Program change message.
void signalSMFText2(int typ, const QByteArray &data)
Emitted after reading a SMF text message.
void signalSMFNoteOff(int chan, int pitch, int vol)
Emitted after reading a Note Off message.
void writeMetaEvent(long deltaTime, int type, const QByteArray &data)
Writes a variable length Meta Event.
Definition qsmf.cpp:544
void signalSMFSequenceNum(int seq)
Emitted after reading a Sequence number message.
void signalSMFMetaMisc(int typ, const QByteArray &data)
Emitted after reading any SMF Meta message.
Q_DECL_DEPRECATED QTextCodec * getTextCodec()
Gets the text codec used for text meta-events I/O.
Definition qsmf.cpp:1236
virtual ~QSmf()
Destructor.
Definition qsmf.cpp:113
void writeBpmTempo(long deltaTime, int tempo)
Writes a Tempo change message.
Definition qsmf.cpp:777
void readFromFile(const QString &fileName)
Reads a SMF stream from a disk file.
Definition qsmf.cpp:454
void signalSMFSysex(const QByteArray &data)
Emitted after reading a System Exclusive message.
void writeToStream(QDataStream *stream)
Writes a SMF stream.
Definition qsmf.cpp:471
void writeTempo(long deltaTime, long tempo)
Writes a Tempo change message.
Definition qsmf.cpp:761
void signalSMFCtlChange(int chan, int ctl, int value)
Emitted after reading a Control Change message.
void writeKeySignature(long deltaTime, int tone, int mode)
Writes a key Signature message.
Definition qsmf.cpp:809
void setFileFormat(int fileFormat)
Sets the SMF file format.
Definition qsmf.cpp:1216
void signalSMFPitchBend(int chan, int value)
Emitted after reading a Bender message.
int getFileFormat()
Gets the SMF file format.
Definition qsmf.cpp:1207
void writeMidiEvent(long deltaTime, int type, int chan, int b1)
Writes a MIDI message with a single parameter.
Definition qsmf.cpp:658
void signalSMFMetaUnregistered(int typ, const QByteArray &data)
Emitted after reading an unregistered SMF Meta message.
void signalSMFSmpte(int b0, int b1, int b2, int b3, int b4)
Emitted after reading a SMPT offset message.
const quint8 cue_point
SMF Cue point.
Definition qsmf.h:70
const quint8 system_exclusive
MIDI event System Exclusive begin.
Definition qsmf.h:88
const quint8 sequencer_specific
SMF Sequencer specific.
Definition qsmf.h:78
const quint32 MTrk
SMF Track prefix.
Definition qsmf.h:59
const quint8 forced_channel
SMF Forced MIDI channel.
Definition qsmf.h:71
const quint8 midi_channel_mask
Mask to extract the channel from the status byte.
Definition qsmf.h:92
QString drumstickLibraryVersion()
drumstickLibraryVersion provides the Drumstick version as an edited QString
Definition qsmf.cpp:1258
const quint8 note_on
MIDI event Note On.
Definition qsmf.h:82
const quint8 control_change
MIDI event Control change.
Definition qsmf.h:84
const quint8 note_off
MIDI event Note Off.
Definition qsmf.h:81
const quint8 smpte_offset
SMF SMPTE offset.
Definition qsmf.h:75
const quint8 sequence_number
SMF Sequence number.
Definition qsmf.h:63
const quint8 sequence_name
SMF Sequence name.
Definition qsmf.h:66
const quint8 pitch_wheel
MIDI event Bender.
Definition qsmf.h:87
const quint8 meta_event
SMF Meta Event prefix.
Definition qsmf.h:62
const quint8 time_signature
SMF Time signature.
Definition qsmf.h:76
const quint8 end_of_track
SMF End of track.
Definition qsmf.h:73
const quint32 MThd
SMF Header prefix.
Definition qsmf.h:58
const quint8 poly_aftertouch
MIDI event Polyphonic pressure.
Definition qsmf.h:83
const quint8 key_signature
SMF Key signature.
Definition qsmf.h:77
const quint8 text_event
SMF Text event.
Definition qsmf.h:64
const quint8 channel_aftertouch
MIDI event Channel after-touch.
Definition qsmf.h:86
const quint8 instrument_name
SMF Instrument name.
Definition qsmf.h:67
const quint8 marker
SMF Marker.
Definition qsmf.h:69
const quint8 forced_port
SMF Forced MIDI port.
Definition qsmf.h:72
const quint8 copyright_notice
SMF Copyright notice.
Definition qsmf.h:65
const quint8 program_chng
MIDI event Program change.
Definition qsmf.h:85
const quint8 midi_command_mask
Mask to extract the command from the status byte.
Definition qsmf.h:91
const quint8 end_of_sysex
MIDI event System Exclusive end.
Definition qsmf.h:89
const quint8 lyric
SMF Lyric.
Definition qsmf.h:68
const quint8 set_tempo
SMF Tempo change.
Definition qsmf.h:74
Drumstick File library.
Definition qsmf.cpp:40
Drumstick common.
Standard MIDI Files Input/Output.