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