drumstick 2.10.0
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-2024, 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
29DISABLE_WARNING_PUSH
30DISABLE_WARNING_DEPRECATED_DECLARATIONS
31
37namespace drumstick {
38namespace File {
39
52class QSmf::QSmfPrivate {
53public:
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;
77 quint64 m_CurrTime;
78 quint64 m_RealTime;
79 double m_DblRealTime;
80 double m_DblOldRealtime;
81 int m_Division;
82 quint64 m_CurrTempo;
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
104 QObject(parent),
105 d(new QSmfPrivate)
106{ }
107
112{
113 d->m_TempoList.clear();
114}
115
120bool QSmf::endOfSmf()
121{
122 return d->m_IOStream->atEnd();
123}
124
129quint8 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
144void QSmf::putByte(quint8 value)
145{
146 *d->m_IOStream << value;
147 d->m_NumBytesWritten++;
148}
149
155void 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
166void 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 Q_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
204void 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; // true if last message was an unfinished SysEx
215 bool running; // true when running status is 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 Q_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 SMFError("Unexpected end of input");
390 }
391 }
392 if (d->m_ToBeRead > 0) {
393 SMFError(QStringLiteral("Track ended before reading last %1 bytes").arg(d->m_ToBeRead));
394 }
395 Q_EMIT signalSMFTrackEnd();
396}
397
401void QSmf::SMFRead()
402{
403 int i;
404 readHeader();
405 for ( i = d->m_Tracks; (i > 0) && !endOfSmf(); i--)
406 {
407 readTrack();
408 }
409 if (i > 0) {
410 SMFError(
411 QStringLiteral("%1 tracks out of a total of %2 are missing").arg(i).arg(d->m_Tracks));
412 }
413}
414
422void QSmf::SMFWrite()
423{
424 int i;
425 d->m_LastStatus = 0;
426 writeHeaderChunk(d->m_fileFormat, d->m_Tracks, d->m_Division);
427 d->m_LastStatus = 0;
428 if (d->m_fileFormat == 1)
429 {
431 }
432 for (i = 0; i < d->m_Tracks; ++i)
433 {
434 writeTrackChunk(i);
435 }
436}
437
442void QSmf::readFromStream(QDataStream *stream)
443{
444 d->m_IOStream = stream;
445 SMFRead();
446}
447
452void QSmf::readFromFile(const QString& fileName)
453{
454 QFile file(fileName);
455 file.open(QIODevice::ReadOnly);
456 QDataStream ds(&file);
457 readFromStream(&ds);
458 file.close();
459}
460
465void QSmf::writeToStream(QDataStream *stream)
466{
467 d->m_IOStream = stream;
468 SMFWrite();
469}
470
475void QSmf::writeToFile(const QString& fileName)
476{
477 QFile file(fileName);
478 file.open(QIODevice::WriteOnly);
479 QDataStream ds(&file);
480 writeToStream(&ds);
481 file.close();
482}
483
490void QSmf::writeHeaderChunk(int format, int ntracks, int division)
491{
492 write32bit(MThd);
493 write32bit(6);
494 write16bit(quint16(format));
495 write16bit(quint16(ntracks));
496 write16bit(quint16(division));
497}
498
503void QSmf::writeTrackChunk(int track)
504{
505 quint32 trkhdr;
506 quint32 trklength;
507 qint64 offset;
508 qint64 place_marker;
509
510 d->m_LastStatus = 0;
511 trkhdr = MTrk;
512 trklength = 0;
513 offset = d->m_IOStream->device()->pos();
514 write32bit(trkhdr);
515 write32bit(trklength);
516 d->m_NumBytesWritten = 0;
517
518 Q_EMIT signalSMFWriteTrack(track);
519
520 place_marker = d->m_IOStream->device()->pos();
521 d->m_IOStream->device()->seek(offset);
522 trklength = d->m_NumBytesWritten;
523 write32bit(trkhdr);
524 write32bit(trklength);
525 d->m_IOStream->device()->seek(place_marker);
526}
527
534void QSmf::writeMetaEvent(long deltaTime, int type, const QByteArray& data)
535{
536 writeVarLen(deltaTime);
537 d->m_LastStatus = meta_event;
538 putByte(d->m_LastStatus);
539 putByte(type);
540 writeVarLen(data.size());
541 foreach(char byte, data)
542 putByte(byte);
543}
544
551void QSmf::writeMetaEvent(long deltaTime, int type, const QString& data)
552{
553 writeVarLen(deltaTime);
554 putByte(d->m_LastStatus = meta_event);
555 putByte(type);
556 QByteArray lcldata;
557 if (d->m_codec == nullptr)
558 lcldata = data.toLatin1();
559 else
560 lcldata = d->m_codec->fromUnicode(data);
561 writeVarLen(lcldata.length());
562 foreach(char byte, lcldata)
563 putByte(byte);
564}
565
573void QSmf::writeMetaEvent(long deltaTime, int type, int data)
574{
575 writeVarLen(deltaTime);
576 putByte(d->m_LastStatus = meta_event);
577 putByte(type);
578 putByte(1);
579 putByte(data);
580}
581
587void QSmf::writeMetaEvent(long deltaTime, int type)
588{
589 writeVarLen(deltaTime);
590 putByte(d->m_LastStatus = meta_event);
591 putByte(type);
592 putByte(0);
593}
594
602void QSmf::writeMidiEvent(long deltaTime, int type, int chan,
603 const QByteArray& data)
604{
605 unsigned int i, j, size;
606 quint8 c;
607 writeVarLen(quint64(deltaTime));
608 if ((type == system_exclusive) || (type == end_of_sysex))
609 {
610 c = type;
611 d->m_LastStatus = 0;
612 }
613 else
614 {
615 if (chan > 15)
616 {
617 SMFError("error: MIDI channel greater than 16");
618 }
619 c = type | chan;
620 }
621 if (d->m_LastStatus != c)
622 {
623 d->m_LastStatus = c;
624 putByte(c);
625 }
626 c = quint8(data[0]);
627 if (type == system_exclusive || type == end_of_sysex)
628 {
629 size = data.size();
630 if (type == c)
631 --size;
632 writeVarLen(size);
633 }
634 j = (c == type ? 1 : 0);
635 for (i = j; i < unsigned(data.size()); ++i)
636 {
637 putByte(quint8(data[i]));
638 }
639}
640
648void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1)
649{
650 quint8 c;
651 writeVarLen(deltaTime);
652 if ((type == system_exclusive) || (type == end_of_sysex))
653 {
654 SMFError("error: Wrong method for a system exclusive event");
655 }
656 if (chan > 15)
657 {
658 SMFError("error: MIDI channel greater than 16");
659 }
660 c = type | chan;
661 if (d->m_LastStatus != c)
662 {
663 d->m_LastStatus = c;
664 putByte(c);
665 }
666 putByte(b1);
667}
668
677void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1, int b2)
678{
679 quint8 c;
680 writeVarLen(deltaTime);
681 if ((type == system_exclusive) || (type == end_of_sysex))
682 {
683 SMFError("error: Wrong method for a system exclusive event");
684 }
685 if (chan > 15)
686 {
687 SMFError("error: MIDI channel greater than 16");
688 }
689 c = type | chan;
690 if (d->m_LastStatus != c)
691 {
692 d->m_LastStatus = c;
693 putByte(c);
694 }
695 putByte(b1);
696 putByte(b2);
697}
698
706void QSmf::writeMidiEvent(long deltaTime, int type, long len, char* data)
707{
708 unsigned int i, j, size;
709 quint8 c;
710 writeVarLen(quint64(deltaTime));
711 if ((type != system_exclusive) && (type != end_of_sysex))
712 {
713 SMFError("error: type should be system exclusive");
714 }
715 d->m_LastStatus = 0;
716 c = quint8(type);
717 putByte(c);
718 size = unsigned(len);
719 c = quint8(data[0]);
720 if (c == type)
721 --size;
722 writeVarLen(size);
723 j = (c == type ? 1 : 0);
724 for (i = j; i < unsigned(len); ++i)
725 {
726 putByte(quint8(data[i]));
727 }
728}
729
735void QSmf::writeSequenceNumber(long deltaTime, int seqnum)
736{
737 writeVarLen(deltaTime);
738 d->m_LastStatus = meta_event;
739 putByte(d->m_LastStatus);
740 putByte(sequence_number);
741 putByte(2);
742 putByte((seqnum >> 8) & 0xff);
743 putByte(seqnum & 0xff);
744}
745
751void QSmf::writeTempo(long deltaTime, long tempo)
752{
753 writeVarLen(deltaTime);
754 putByte(d->m_LastStatus = meta_event);
755 putByte(set_tempo);
756 putByte(3);
757 putByte((tempo >> 16) & 0xff);
758 putByte((tempo >> 8) & 0xff);
759 putByte(tempo & 0xff);
760}
761
767void QSmf::writeBpmTempo(long deltaTime, int tempo)
768{
769 long us_tempo = 60000000l / tempo;
770 writeTempo(deltaTime, us_tempo);
771}
772
781void QSmf::writeTimeSignature(long deltaTime, int num, int den, int cc, int bb)
782{
783 writeVarLen(deltaTime);
784 putByte(d->m_LastStatus = meta_event);
785 putByte(time_signature);
786 putByte(4);
787 putByte(num & 0xff);
788 putByte(den & 0xff);
789 putByte(cc & 0xff);
790 putByte(bb & 0xff);
791}
792
799void QSmf::writeKeySignature(long deltaTime, int tone, int mode)
800{
801 writeVarLen(quint64(deltaTime));
802 putByte(d->m_LastStatus = meta_event);
803 putByte(key_signature);
804 putByte(2);
805 putByte(quint8(tone));
806 putByte(mode & 0x01);
807}
808
813void QSmf::writeVarLen(quint64 value)
814{
815 quint64 buffer;
816
817 buffer = value & 0x7f;
818 while ((value >>= 7) > 0)
819 {
820 buffer <<= 8;
821 buffer |= 0x80;
822 buffer += (value & 0x7f);
823 }
824 while (true)
825 {
826 putByte(buffer & 0xff);
827 if (buffer & 0x80)
828 buffer >>= 8;
829 else
830 break;
831 }
832}
833
834/* These routines are used to make sure that the byte order of
835 the various data types remains constant between machines. */
836void QSmf::write32bit(quint32 data)
837{
838 putByte((data >> 24) & 0xff);
839 putByte((data >> 16) & 0xff);
840 putByte((data >> 8) & 0xff);
841 putByte(data & 0xff);
842}
843
844void QSmf::write16bit(quint16 data)
845{
846 putByte((data >> 8) & 0xff);
847 putByte(data & 0xff);
848}
849
850quint16 QSmf::to16bit(quint8 c1, quint8 c2)
851{
852 quint16 value;
853 value = quint16(c1 << 8);
854 value += c2;
855 return value;
856}
857
858quint32 QSmf::to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4)
859{
860 quint32 value;
861 value = unsigned(c1 << 24);
862 value += unsigned(c2 << 16);
863 value += unsigned(c3 << 8);
864 value += c4;
865 return value;
866}
867
868quint16 QSmf::read16bit()
869{
870 quint8 c1, c2;
871 c1 = getByte();
872 c2 = getByte();
873 return to16bit(c1, c2);
874}
875
876quint32 QSmf::read32bit()
877{
878 quint8 c1, c2, c3, c4;
879 c1 = getByte();
880 c2 = getByte();
881 c3 = getByte();
882 c4 = getByte();
883 return to32bit(c1, c2, c3, c4);
884}
885
886long QSmf::readVarLen()
887{
888 quint64 value;
889 quint8 c;
890
891 c = getByte();
892 value = c;
893 if ((c & 0x80) != 0)
894 {
895 value &= 0x7f;
896 do
897 {
898 c = getByte();
899 value = (value << 7) + (c & 0x7f);
900 } while ((c & 0x80) != 0);
901 }
902 return long(value);
903}
904
905void QSmf::readExpected(const QString& s)
906{
907 int j;
908 quint8 b;
909 for (j = 0; j < s.length(); ++j)
910 {
911 b = getByte();
912 if (QChar(b) != s[j])
913 {
914 SMFError(QString("Invalid (%1) SMF format at %2").arg(b, 0, 16).arg(d->m_IOStream->device()->pos()));
915 break;
916 }
917 }
918}
919
920quint64 QSmf::findTempo()
921{
922 quint64 result, old_tempo, new_tempo;
923 QSmfRecTempo rec = d->m_TempoList.last();
924 old_tempo = d->m_CurrTempo;
925 new_tempo = d->m_CurrTempo;
926 QList<QSmfRecTempo>::Iterator it;
927 for( it = d->m_TempoList.begin(); it != d->m_TempoList.end(); ++it )
928 {
929 rec = (*it);
930 if (rec.time <= d->m_CurrTime)
931 {
932 old_tempo = rec.tempo;
933 }
934 new_tempo = rec.tempo;
935 if (rec.time > d->m_RevisedTime)
936 {
937 break;
938 }
939 }
940 if ((rec.time <= d->m_RevisedTime) || (rec.time > d->m_CurrTime))
941 {
942 d->m_RevisedTime = d->m_CurrTime;
943 result = old_tempo;
944 }
945 else
946 {
947 d->m_RevisedTime = rec.time;
948 d->m_TempoChangeTime = d->m_RevisedTime;
949 result = new_tempo;
950 }
951 return result;
952}
953
954/* This routine converts delta times in ticks into seconds. The
955 else statement is needed because the formula is different for tracks
956 based on notes and tracks based on SMPTE times. */
957double QSmf::ticksToSecs(quint64 ticks, quint16 division, quint64 tempo)
958{
959 double result;
960 double smpte_format;
961 double smpte_resolution;
962
963 if (division > 0)
964 {
965 result = double(ticks * tempo)/(division * 1000000.0);
966 }
967 else
968 {
969 smpte_format = upperByte(division);
970 smpte_resolution = lowerByte(division);
971 result = double(ticks)/(smpte_format * smpte_resolution
972 * 1000000.0);
973 }
974 return result;
975}
976
977void QSmf::SMFError(const QString& s)
978{
979 Q_EMIT signalSMFError(s);
980}
981
982void QSmf::channelMessage(quint8 status, quint8 c1, quint8 c2)
983{
984 quint8 chan;
985 int k;
986 chan = status & midi_channel_mask;
987 if (c1 > 127)
988 {
989 SMFError(QString("ChannelMessage with bad c1 = %1").arg(c1));
990 //c1 &= 127;
991 }
992 if (c2 > 127)
993 {
994 SMFError(QString("ChannelMessage with bad c2 = %1").arg(c2));
995 //c2 &= 127;
996 }
997 switch (status & midi_command_mask)
998 {
999 case note_off:
1000 Q_EMIT signalSMFNoteOff(chan, c1, c2);
1001 break;
1002 case note_on:
1003 Q_EMIT signalSMFNoteOn(chan, c1, c2);
1004 break;
1005 case poly_aftertouch:
1006 Q_EMIT signalSMFKeyPress(chan, c1, c2);
1007 break;
1008 case control_change:
1009 Q_EMIT signalSMFCtlChange(chan, c1, c2);
1010 break;
1011 case program_chng:
1012 Q_EMIT signalSMFProgram(chan, c1);
1013 break;
1014 case channel_aftertouch:
1015 Q_EMIT signalSMFChanPress(chan, c1);
1016 break;
1017 case pitch_wheel:
1018 k = c1 + (c2 << 7) - 8192;
1019 Q_EMIT signalSMFPitchBend(chan, k);
1020 break;
1021 default:
1022 SMFError(QString("Invalid MIDI status %1. Unhandled event").arg(status));
1023 break;
1024 }
1025}
1026
1027void QSmf::metaEvent(quint8 b)
1028{
1029 QSmfRecTempo rec;
1030 QByteArray m(d->m_MsgBuff);
1031
1032 switch (b)
1033 {
1034 case sequence_number:
1035 Q_EMIT signalSMFSequenceNum(to16bit(m[0], m[1]));
1036 break;
1037 case text_event:
1038 case copyright_notice:
1039 case sequence_name:
1040 case instrument_name:
1041 case lyric:
1042 case marker:
1043 case cue_point: {
1044 QString s;
1045 if (d->m_codec == nullptr) {
1046 Q_EMIT signalSMFText2(b, m);
1047 } else {
1048 s = d->m_codec->toUnicode(m);
1049 Q_EMIT signalSMFText(b, s);
1050 }
1051 }
1052 break;
1053 case forced_channel:
1054 Q_EMIT signalSMFforcedChannel(m[0]);
1055 break;
1056 case forced_port:
1057 Q_EMIT signalSMFforcedPort(m[0]);
1058 break;
1059 case end_of_track:
1060 Q_EMIT signalSMFendOfTrack();
1061 break;
1062 case set_tempo:
1063 d->m_CurrTempo = to32bit(0, m[0], m[1], m[2]);
1064 Q_EMIT signalSMFTempo(d->m_CurrTempo);
1065 rec = d->m_TempoList.last();
1066 if (rec.tempo == d->m_CurrTempo)
1067 {
1068 return;
1069 }
1070 if (rec.time > d->m_CurrTime)
1071 {
1072 return;
1073 }
1074 addTempo(d->m_CurrTempo, d->m_CurrTime);
1075 break;
1076 case smpte_offset:
1077 Q_EMIT signalSMFSmpte(m[0], m[1], m[2], m[3], m[4]);
1078 break;
1079 case time_signature:
1080 Q_EMIT signalSMFTimeSig(m[0], m[1], m[2], m[3]);
1081 break;
1082 case key_signature:
1083 Q_EMIT signalSMFKeySig(m[0], m[1]);
1084 break;
1085 case sequencer_specific:
1086 Q_EMIT signalSMFSeqSpecific(m);
1087 break;
1088 default:
1089 Q_EMIT signalSMFMetaUnregistered(b, m);
1090 break;
1091 }
1092 Q_EMIT signalSMFMetaMisc(b, m);
1093}
1094
1095void QSmf::sysEx()
1096{
1097 QByteArray varr(d->m_MsgBuff);
1098 Q_EMIT signalSMFSysex(varr);
1099}
1100
1101void QSmf::badByte(quint8 b, int p)
1102{
1103 SMFError(QString("Unexpected byte (%1) at %2").arg(b, 2, 16).arg(p));
1104}
1105
1106quint8 QSmf::lowerByte(quint16 x)
1107{
1108 return (x & 0xff);
1109}
1110
1111quint8 QSmf::upperByte(quint16 x)
1112{
1113 return ((x >> 8) & 0xff);
1114}
1115
1116void QSmf::msgInit()
1117{
1118 d->m_MsgBuff.truncate(0);
1119}
1120
1121void QSmf::msgAdd(quint8 b)
1122{
1123 int s = d->m_MsgBuff.size();
1124 d->m_MsgBuff.resize(s + 1);
1125 d->m_MsgBuff[s] = b;
1126}
1127
1128/* public properties (accessors) */
1129
1135{
1136 return d->m_CurrTime;
1137}
1138
1144{
1145 return d->m_CurrTempo;
1146}
1147
1153{
1154 return d->m_RealTime;
1155}
1156
1162{
1163 return d->m_Division;
1164}
1165
1170void QSmf::setDivision(int division)
1171{
1172 d->m_Division = division;
1173}
1174
1180{
1181 return d->m_Tracks;
1182}
1183
1188void QSmf::setTracks(int tracks)
1189{
1190 d->m_Tracks = tracks;
1191}
1192
1198{
1199 return d->m_fileFormat;
1200}
1201
1206void QSmf::setFileFormat(int fileFormat)
1207{
1208 d->m_fileFormat = fileFormat;
1209}
1210
1216{
1217 return long(d->m_IOStream->device()->pos());
1218}
1219
1227{
1228 return d->m_codec;
1229}
1230
1239void QSmf::setTextCodec(QTextCodec *codec)
1240{
1241 d->m_codec = codec;
1242}
1243
1249{
1250 return QStringLiteral(QT_STRINGIFY(VERSION));
1251}
1252
1253} // namespace File
1254} // namespace drumstick
1255
1256DISABLE_WARNING_POP
The QObject class is the base class of all Qt objects.
int getTracks()
Gets the number of tracks.
Definition: qsmf.cpp:1179
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:1239
long getRealTime()
Gets the real time in seconds.
Definition: qsmf.cpp:1152
long getCurrentTempo()
Gets the current tempo.
Definition: qsmf.cpp:1143
void signalSMFforcedChannel(int channel)
Emitted after reading a Forced channel message.
void setDivision(int division)
Sets the resolution.
Definition: qsmf.cpp:1170
long getFilePos()
Gets the position in the SMF stream.
Definition: qsmf.cpp:1215
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:1134
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:1188
void writeTimeSignature(long deltaTime, int num, int den, int cc, int bb)
Writes a Time Signature message.
Definition: qsmf.cpp:781
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:1161
QSmf(QObject *parent=nullptr)
Constructor.
Definition: qsmf.cpp:103
void writeToFile(const QString &fileName)
Writes a SMF stream to a disk file.
Definition: qsmf.cpp:475
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:735
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:442
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:534
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:1226
virtual ~QSmf()
Destructor.
Definition: qsmf.cpp:111
void writeBpmTempo(long deltaTime, int tempo)
Writes a Tempo change message.
Definition: qsmf.cpp:767
void readFromFile(const QString &fileName)
Reads a SMF stream from a disk file.
Definition: qsmf.cpp:452
void signalSMFSysex(const QByteArray &data)
Emitted after reading a System Exclusive message.
void writeToStream(QDataStream *stream)
Writes a SMF stream.
Definition: qsmf.cpp:465
void writeTempo(long deltaTime, long tempo)
Writes a Tempo change message.
Definition: qsmf.cpp:751
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:799
void setFileFormat(int fileFormat)
Sets the SMF file format.
Definition: qsmf.cpp:1206
void signalSMFPitchBend(int chan, int value)
Emitted after reading a Bender message.
int getFileFormat()
Gets the SMF file format.
Definition: qsmf.cpp:1197
void writeMidiEvent(long deltaTime, int type, int chan, int b1)
Writes a MIDI message with a single parameter.
Definition: qsmf.cpp:648
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:1248
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 common.
Definition: alsaclient.cpp:71
Standard MIDI Files Input/Output.