1 /*
2     WRK File component
3     Copyright (C) 2010-2021, Pedro Lopez-Cabanillas <plcl@users.sf.net>
4 
5     This library is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 3 of the License, or
8     (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <QDataStream>
20 #include <QFile>
21 #include <QIODevice>
22 #include <QStringList>
23 #include <QTextCodec>
24 #include <QTextStream>
25 #include <cmath>
26 #include <drumstick/qwrk.h>
27 
28 DISABLE_WARNING_PUSH
29 DISABLE_WARNING_DEPRECATED_DECLARATIONS
30 
31 /**
32  * @file qwrk.cpp
33  * Implementation of a class managing Cakewalk WRK Files input
34  */
35 
36 namespace drumstick { namespace File {
37 
38 /**
39  * @addtogroup WRK
40  * @{
41  *
42  * QWrk provides a mechanism to parse Cakewalk WRK Files, without
43  * the burden of a policy forcing to use some internal sequence representation.
44  *
45  * This class is not related or based on the ALSA library.
46  *
47  * @}
48  */
49 
50 class QWrk::QWrkPrivate {
51 public:
QWrkPrivate()52     QWrkPrivate():
53     m_Now(0),
54     m_From(0),
55     m_Thru(11930),
56     m_KeySig(0),
57     m_Clock(0),
58     m_AutoSave(0),
59     m_PlayDelay(0),
60     m_ZeroCtrls(false),
61     m_SendSPP(true),
62     m_SendCont(true),
63     m_PatchSearch(false),
64     m_AutoStop(false),
65     m_StopTime(4294967295U),
66     m_AutoRewind(false),
67     m_RewindTime(0),
68     m_MetroPlay(false),
69     m_MetroRecord(true),
70     m_MetroAccent(false),
71     m_CountIn(1),
72     m_ThruOn(true),
73     m_AutoRestart(false),
74     m_CurTempoOfs(1),
75     m_TempoOfs1(32),
76     m_TempoOfs2(64),
77     m_TempoOfs3(128),
78     m_PunchEnabled(false),
79     m_PunchInTime(0),
80     m_PunchOutTime(0),
81     m_EndAllTime(0),
82     m_division(120),
83     m_codec(nullptr),
84     m_IOStream(nullptr)
85     { }
86 
87     quint32 m_Now;          ///< Now marker time
88     quint32 m_From;         ///< From marker time
89     quint32 m_Thru;         ///< Thru marker time
90     quint8 m_KeySig;        ///< Key signature (0=C, 1=C#, ... 11=B)
91     quint8 m_Clock;         ///< Clock Source (0=Int, 1=MIDI, 2=FSK, 3=SMPTE)
92     quint8 m_AutoSave;      ///< Auto save (0=disabled, 1..256=minutes)
93     quint8 m_PlayDelay;     ///< Play Delay
94     bool m_ZeroCtrls;       ///< Zero continuous controllers?
95     bool m_SendSPP;         ///< Send Song Position Pointer?
96     bool m_SendCont;        ///< Send MIDI Continue?
97     bool m_PatchSearch;     ///< Patch/controller search-back?
98     bool m_AutoStop;        ///< Auto-stop?
99     quint32 m_StopTime;     ///< Auto-stop time
100     bool m_AutoRewind;      ///< Auto-rewind?
101     quint32 m_RewindTime;   ///< Auto-rewind time
102     bool m_MetroPlay;       ///< Metronome on during playback?
103     bool m_MetroRecord;     ///< Metronome on during recording?
104     bool m_MetroAccent;     ///< Metronome accents primary beats?
105     quint8 m_CountIn;       ///< Measures of count-in (0=no count-in)
106     bool m_ThruOn;          ///< MIDI Thru enabled? (only used if no THRU rec)
107     bool m_AutoRestart;     ///< Auto-restart?
108     quint8 m_CurTempoOfs;   ///< Which of the 3 tempo offsets is used: 0..2
109     quint8 m_TempoOfs1;     ///< Fixed-point ratio value of offset 1
110     quint8 m_TempoOfs2;     ///< Fixed-point ratio value of offset 2
111     quint8 m_TempoOfs3;     ///< Fixed-point ratio value of offset 3
112     bool m_PunchEnabled;    ///< Auto-Punch enabled?
113     quint32 m_PunchInTime;  ///< Punch-in time
114     quint32 m_PunchOutTime;	///< Punch-out time
115     quint32 m_EndAllTime;   ///< Time of latest event (incl. all tracks)
116 
117     int m_division;
118     QTextCodec *m_codec;
119     QDataStream *m_IOStream;
120     QByteArray m_lastChunkData;
121     QList<RecTempo> m_tempos;
122 
123     qint64 m_lastChunkPos;
124     qint64 internalFilePos();
125 };
126 
127 /**
128  * Constructor
129  * @param parent Object owner
130  */
QWrk(QObject * parent)131 QWrk::QWrk(QObject * parent) :
132     QObject(parent),
133     d(new QWrkPrivate)
134 { }
135 
136 /**
137  * Destructor
138  */
~QWrk()139 QWrk::~QWrk()
140 { }
141 
142 /**
143  * Gets the text codec used for text meta-events I/O.
144  *
145  * @return QTextCodec pointer
146  * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
147  */
getTextCodec()148 QTextCodec* QWrk::getTextCodec()
149 {
150     return d->m_codec;
151 }
152 
153 /**
154  * Sets the text codec for text meta-events.
155  * The engine doesn't take ownership of the codec instance.
156  *
157  * @param codec QTextCodec pointer
158  * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
159  */
setTextCodec(QTextCodec * codec)160 void QWrk::setTextCodec(QTextCodec *codec)
161 {
162     d->m_codec = codec;
163 }
164 
165 /**
166  * Gets the last chunk raw data (undecoded)
167  *
168  * @return last chunk raw data
169  */
getLastChunkRawData() const170 QByteArray QWrk::getLastChunkRawData() const
171 {
172     return d->m_lastChunkData;
173 }
174 
175 /**
176  * Read the chunk raw data (undecoded)
177  */
readRawData(int size)178 void QWrk::readRawData(int size)
179 {
180     if (size > 0) {
181         d->m_lastChunkData = d->m_IOStream->device()->read(size);
182     } else {
183         d->m_lastChunkData.clear();
184         //qDebug() << Q_FUNC_INFO << "Size error:" << size;
185     }
186 }
187 
188 /**
189  * Now marker time
190  * @return Now marker time
191  */
getNow() const192 int QWrk::getNow() const
193 {
194     return d->m_Now;
195 }
196 
197 /**
198  * From marker time
199  * @return From marker time
200  */
getFrom() const201 int QWrk::getFrom() const
202 {
203     return d->m_From;
204 }
205 
206 /**
207  * Thru marker time
208  * @return Thru marker time
209  */
getThru() const210 int QWrk::getThru() const
211 {
212     return d->m_Thru;
213 }
214 
215 /**
216  * Key signature (0=C, 1=C#, ... 11=B)
217  * @return Key signature
218  */
getKeySig() const219 int QWrk::getKeySig() const
220 {
221     return d->m_KeySig;
222 }
223 
224 /**
225  * Clock Source (0=Int, 1=MIDI, 2=FSK, 3=SMPTE)
226  * @return Clock Source
227  */
getClock() const228 int QWrk::getClock() const
229 {
230     return d->m_Clock;
231 }
232 
233 /**
234  * Auto save (0=disabled, 1..256=minutes)
235  * @return Auto save
236  */
getAutoSave() const237 int QWrk::getAutoSave() const
238 {
239     return d->m_AutoSave;
240 }
241 
242 /**
243  * Play Delay
244  * @return Play Delay
245  */
getPlayDelay() const246 int QWrk::getPlayDelay() const
247 {
248     return d->m_PlayDelay;
249 }
250 
251 /**
252  * Zero continuous controllers?
253  * @return Zero continuous controllers
254  */
getZeroCtrls() const255 bool QWrk::getZeroCtrls() const
256 {
257     return d->m_ZeroCtrls;
258 }
259 
260 /**
261  * Send Song Position Pointer?
262  * @return Send Song Position Pointer
263  */
getSendSPP() const264 bool QWrk::getSendSPP() const
265 {
266     return d->m_SendSPP;
267 }
268 
269 /**
270  * Send MIDI Continue?
271  * @return Send MIDI Continue
272  */
getSendCont() const273 bool QWrk::getSendCont() const
274 {
275     return d->m_SendCont;
276 }
277 
278 /**
279  * Patch/controller search-back?
280  * @return Patch/controller search-back
281  */
getPatchSearch() const282 bool QWrk::getPatchSearch() const
283 {
284     return d->m_PatchSearch;
285 }
286 
287 /**
288  * Auto-stop?
289  * @return Auto-stop
290  */
getAutoStop() const291 bool QWrk::getAutoStop() const
292 {
293     return d->m_AutoStop;
294 }
295 
296 /**
297  * Auto-stop time
298  * @return Auto-stop time
299  */
getStopTime() const300 unsigned int QWrk::getStopTime() const
301 {
302     return d->m_StopTime;
303 }
304 
305 /**
306  * Auto-rewind?
307  * @return Auto-rewind
308  */
getAutoRewind() const309 bool QWrk::getAutoRewind() const
310 {
311     return d->m_AutoRewind;
312 }
313 
314 /**
315  * Auto-rewind time
316  * @return Auto-rewind time
317  */
getRewindTime() const318 int QWrk::getRewindTime() const
319 {
320     return d->m_RewindTime;
321 }
322 
323 /**
324  * Metronome on during playback?
325  * @return Metronome on during playback
326  */
getMetroPlay() const327 bool QWrk::getMetroPlay() const
328 {
329     return d->m_MetroPlay;
330 }
331 
332 /**
333  * Metronome on during recording?
334  * @return Metronome on during recording
335  */
getMetroRecord() const336 bool QWrk::getMetroRecord() const
337 {
338     return d->m_MetroRecord;
339 }
340 
341 /**
342  * Metronome accents primary beats?
343  * @return Metronome accents primary beats
344  */
getMetroAccent() const345 bool QWrk::getMetroAccent() const
346 {
347     return d->m_MetroAccent;
348 }
349 
350 /**
351  * Measures of count-in (0=no count-in)
352  * @return Measures of count-in
353  */
getCountIn() const354 int QWrk::getCountIn() const
355 {
356     return d->m_CountIn;
357 }
358 
359 /**
360  * MIDI Thru enabled? (only used if no THRU rec)
361  * @return MIDI Thru enabled
362  */
getThruOn() const363 bool QWrk::getThruOn() const
364 {
365     return d->m_ThruOn;
366 }
367 
368 /**
369  * Auto-restart?
370  * @return Auto-restart
371  */
getAutoRestart() const372 bool QWrk::getAutoRestart() const
373 {
374     return d->m_AutoRestart;
375 }
376 
377 /**
378  * Which of the 3 tempo offsets is used: 0..2
379  * @return tempo offset index
380  */
getCurTempoOfs() const381 int QWrk::getCurTempoOfs() const
382 {
383     return d->m_CurTempoOfs;
384 }
385 
386 /**
387  * Fixed-point ratio value of tempo offset 1
388  *
389  * NOTE: The offset ratios are expressed as a numerator in the expression
390  * n/64.  To get a ratio from this number, divide the number by 64.  To get
391  * this number from a ratio, multiply the ratio by 64.
392  * Examples:
393  *   32 ==>  32/64 = 0.5
394  *   63 ==>  63/64 = 0.9
395  *   64 ==>  64/64 = 1.0
396  *  128 ==> 128/64 = 2.0
397  *
398  * @return tempo offset 1
399  */
getTempoOfs1() const400 int QWrk::getTempoOfs1() const
401 {
402     return d->m_TempoOfs1;
403 }
404 
405 /**
406  * Fixed-point ratio value of tempo offset 2
407  *
408  * NOTE: The offset ratios are expressed as a numerator in the expression
409  * n/64.  To get a ratio from this number, divide the number by 64.  To get
410  * this number from a ratio, multiply the ratio by 64.
411  * Examples:
412  *   32 ==>  32/64 = 0.5
413  *   63 ==>  63/64 = 0.9
414  *   64 ==>  64/64 = 1.0
415  *  128 ==> 128/64 = 2.0
416  *
417  * @return tempo offset 2
418  */
getTempoOfs2() const419 int QWrk::getTempoOfs2() const
420 {
421     return d->m_TempoOfs2;
422 }
423 
424 /**
425  * Fixed-point ratio value of tempo offset 3
426  *
427  * NOTE: The offset ratios are expressed as a numerator in the expression
428  * n/64.  To get a ratio from this number, divide the number by 64.  To get
429  * this number from a ratio, multiply the ratio by 64.
430  * Examples:
431  *   32 ==>  32/64 = 0.5
432  *   63 ==>  63/64 = 0.9
433  *   64 ==>  64/64 = 1.0
434  *  128 ==> 128/64 = 2.0
435  *
436  * @return tempo offset 3
437  */
getTempoOfs3() const438 int QWrk::getTempoOfs3() const
439 {
440     return d->m_TempoOfs3;
441 }
442 
443 /**
444  * Auto-Punch enabled?
445  * @return Auto-Punch enabled
446  */
getPunchEnabled() const447 bool QWrk::getPunchEnabled() const
448 {
449     return d->m_PunchEnabled;
450 }
451 
452 /**
453  * Punch-in time
454  * @return punch-in time
455  */
getPunchInTime() const456 int QWrk::getPunchInTime() const
457 {
458     return d->m_PunchInTime;
459 }
460 
461 /**
462  * Punch-out time
463  * @return Punch-out time
464  */
getPunchOutTime() const465 int QWrk::getPunchOutTime() const
466 {
467     return d->m_PunchOutTime;
468 }
469 
470 /**
471  * Time of latest event (incl. all tracks)
472  * @return Time of latest event
473  */
getEndAllTime() const474 int QWrk::getEndAllTime() const
475 {
476     return d->m_EndAllTime;
477 }
478 
479 /**
480  * Gets a single byte from the stream
481  * @return A Single byte
482  */
readByte()483 quint8 QWrk::readByte()
484 {
485     quint8 b = 0xff;
486     if (!d->m_IOStream->atEnd())
487         *d->m_IOStream >> b;
488     return b;
489 }
490 
491 /**
492  * Converts two bytes into a single 16-bit value
493  * @param c1 first byte
494  * @param c2 second byte
495  * @return 16-bit value
496  */
to16bit(quint8 c1,quint8 c2)497 quint16 QWrk::to16bit(quint8 c1, quint8 c2)
498 {
499     quint16 value = (c1 << 8);
500     value += c2;
501     return value;
502 }
503 
504 /**
505  * Converts four bytes into a single 32-bit value
506  * @param c1 1st byte
507  * @param c2 2nd byte
508  * @param c3 3rd byte
509  * @param c4 4th byte
510  * @return 32-bit value
511  */
to32bit(quint8 c1,quint8 c2,quint8 c3,quint8 c4)512 quint32 QWrk::to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4)
513 {
514     quint32 value = (c1 << 24);
515     value += (c2 << 16);
516     value += (c3 << 8);
517     value += c4;
518     return value;
519 }
520 
521 /**
522  * Reads a 16-bit value
523  * @return 16-bit value
524  */
read16bit()525 quint16 QWrk::read16bit()
526 {
527     quint8 c1, c2;
528     c1 = readByte();
529     c2 = readByte();
530     return to16bit(c2, c1);
531 }
532 
533 /**
534  * Reads a 24-bit value
535  * @return 32-bit value
536  */
read24bit()537 quint32 QWrk::read24bit()
538 {
539     quint8 c1, c2, c3;
540     c1 = readByte();
541     c2 = readByte();
542     c3 = readByte();
543     return to32bit(0, c3, c2, c1);
544 }
545 
546 /**
547  * Reads a 32-bit value
548  * @return 32-bit value
549  */
read32bit()550 quint32 QWrk::read32bit()
551 {
552     quint8 c1, c2, c3, c4;
553     c1 = readByte();
554     c2 = readByte();
555     c3 = readByte();
556     c4 = readByte();
557     return to32bit(c4, c3, c2, c1);
558 }
559 
560 /**
561  * Reads a string assuming local encoding if getTextCodec() is null
562  * @return a string
563  */
readString(int len)564 QString QWrk::readString(int len)
565 {
566     QString s;
567     if ( len > 0 ) {
568         QByteArray data = readByteArray(len);
569         if (d->m_codec == nullptr) {
570             s = QString::fromLatin1(data);
571         } else {
572             s = d->m_codec->toUnicode(data);
573         }
574     }
575     return s;
576 }
577 
578 /**
579  * Reads a string as a QByteArray (without decoding)
580  * @return a string
581  */
readByteArray(int len)582 QByteArray QWrk::readByteArray(int len)
583 {
584     QByteArray data;
585     if ( len > 0 ) {
586         quint8 c = 0xff;
587         for ( int i = 0; i < len && c != 0 && !atEnd(); ++i ) {
588             c = readByte();
589             if ( c != 0)
590                 data += c;
591         }
592     }
593     return data;
594 }
595 
596 /**
597  * Reads a variable length string (C-style)
598  * (assuming local encoding if getTextCodec() is null)
599  * @return a string
600  */
readVarString()601 QString QWrk::readVarString()
602 {
603     QString s;
604     QByteArray data = readVarByteArray();
605     if (d->m_codec == nullptr) {
606         s = QString::fromLatin1(data);
607     } else {
608         s = d->m_codec->toUnicode(data);
609     }
610     return s;
611 }
612 
613 /**
614  * Reads a variable length string (C-style) as a QByteArray (without decoding)
615  * @return a string
616  */
readVarByteArray()617 QByteArray QWrk::readVarByteArray()
618 {
619     QByteArray data;
620     quint8 b;
621     do {
622         b = readByte();
623         if (b != 0)
624             data += b;
625     } while (b != 0 && !atEnd());
626     return data;
627 }
628 
processMarkers()629 void QWrk::processMarkers()
630 {
631     int num = read32bit();
632     for (int i = 0; (i < num) && (d->internalFilePos() < d->m_lastChunkPos) && !atEnd(); ++i) {
633         int smpte = readByte();
634         readGap(1);
635         long time = read24bit();
636         readGap(5);
637         int len = readByte();
638         if (d->m_codec == nullptr) {
639             QByteArray data = readByteArray(len);
640             Q_EMIT signalWRKMarker2(time, smpte, data);
641         } else {
642             QString name = readString(len);
643             Q_EMIT signalWRKMarker(time, smpte, name);
644         }
645     }
646 }
647 
648 /**
649  * Current position in the data stream
650  * @return current position
651  */
getFilePos()652 long QWrk::getFilePos()
653 {
654     return d->internalFilePos();
655 }
656 
657 /**
658  * Seeks to a new position in the data stream
659  * @param pos new position
660  */
seek(qint64 pos)661 void QWrk::seek(qint64 pos)
662 {
663     if (!d->m_IOStream->device()->seek(pos)) {
664         //qDebug() << Q_FUNC_INFO << "Error, pos:" << pos;
665     }
666 }
667 
668 /**
669  * Checks if the data stream pointer has reached the end position
670  * @return true if the read pointer is at end
671  */
atEnd()672 bool QWrk::atEnd()
673 {
674     return d->m_IOStream->atEnd();
675 }
676 
677 /**
678  * Jumps the given size in the data stream
679  * @param size the gap size
680  */
readGap(int size)681 void QWrk::readGap(int size)
682 {
683     if ( size > 0)
684         seek( d->internalFilePos() + size );
685 }
686 
687 /**
688  * Reads a stream.
689  * @param stream Pointer to an existing and opened stream
690  */
readFromStream(QDataStream * stream)691 void QWrk::readFromStream(QDataStream *stream)
692 {
693     d->m_IOStream = stream;
694     wrkRead();
695 }
696 
697 /**
698  * Reads a stream from a disk file.
699  * @param fileName Name of an existing file.
700  */
readFromFile(const QString & fileName)701 void QWrk::readFromFile(const QString& fileName)
702 {
703     QFile file(fileName);
704     file.open(QIODevice::ReadOnly);
705     QDataStream ds(&file);
706     readFromStream(&ds);
707     file.close();
708 }
709 
processTrackChunk()710 void QWrk::processTrackChunk()
711 {
712     int namelen;
713     QString name[2];
714     QByteArray data[2];
715     int trackno;
716     int channel;
717     int pitch;
718     int velocity;
719     int port;
720     bool selected;
721     bool muted;
722     bool loop;
723 
724     trackno = read16bit();
725     for(int i=0; i<2; ++i) {
726         namelen = readByte();
727         if (d->m_codec == nullptr) {
728             data[i] = readByteArray(namelen);
729         } else {
730             name[i] = readString(namelen);
731         }
732     }
733     channel = readByte() & 0x0f;
734     pitch = readByte();
735     velocity = readByte();
736     port = readByte();
737     quint8 flags = readByte();
738     selected = ((flags & 1) != 0);
739     muted = ((flags & 2) != 0);
740     loop = ((flags & 4) != 0);
741     if (d->m_codec == nullptr) {
742         Q_EMIT signalWRKTrack2( data[0], data[1],
743                                trackno, channel, pitch,
744                                velocity, port, selected,
745                                muted, loop );
746     } else {
747         Q_EMIT signalWRKTrack( name[0], name[1],
748                                trackno, channel, pitch,
749                                velocity, port, selected,
750                                muted, loop );
751     }
752 }
753 
processVarsChunk()754 void QWrk::processVarsChunk()
755 {
756     d->m_Now = read32bit();
757     d->m_From = read32bit();
758     d->m_Thru = read32bit();
759     d->m_KeySig = readByte();
760     d->m_Clock = readByte();
761     d->m_AutoSave = readByte();
762     d->m_PlayDelay = readByte();
763     readGap(1);
764     d->m_ZeroCtrls = (readByte() != 0);
765     d->m_SendSPP = (readByte() != 0);
766     d->m_SendCont = (readByte() != 0);
767     d->m_PatchSearch = (readByte() != 0);
768     d->m_AutoStop = (readByte() != 0);
769     d->m_StopTime = read32bit();
770     d->m_AutoRewind = (readByte() != 0);
771     d->m_RewindTime = read32bit();
772     d->m_MetroPlay = (readByte() != 0);
773     d->m_MetroRecord = (readByte() != 0);
774     d->m_MetroAccent = (readByte() != 0);
775     d->m_CountIn = readByte();
776     readGap(2);
777     d->m_ThruOn = (readByte() != 0);
778     readGap(19);
779     d->m_AutoRestart = (readByte() != 0);
780     d->m_CurTempoOfs = readByte();
781     d->m_TempoOfs1 = readByte();
782     d->m_TempoOfs2 = readByte();
783     d->m_TempoOfs3 = readByte();
784     readGap(2);
785     d->m_PunchEnabled = (readByte() != 0);
786     d->m_PunchInTime = read32bit();
787     d->m_PunchOutTime = read32bit();
788     d->m_EndAllTime = read32bit();
789 
790     Q_EMIT signalWRKGlobalVars();
791 }
792 
processTimebaseChunk()793 void QWrk::processTimebaseChunk()
794 {
795     quint16 timebase = read16bit();
796     d->m_division = timebase;
797     Q_EMIT signalWRKTimeBase(timebase);
798 }
799 
processNoteArray(int track,int events)800 void QWrk::processNoteArray(int track, int events)
801 {
802     quint32 time = 0;
803     quint8  status = 0, data1 = 0, data2 = 0, i = 0;
804     quint16 dur = 0;
805     int value = 0, type = 0, channel = 0, len = 0;
806     QString text;
807     QByteArray data;
808     for ( i = 0; (i < events) && (d->internalFilePos() < d->m_lastChunkPos) && !atEnd(); ++i ) {
809         time = read24bit();
810         status = readByte();
811         dur = 0;
812         if (status >= 0x90) {
813             type = status & 0xf0;
814             channel = status & 0x0f;
815             data1 = readByte();
816             if (type == 0x90 || type == 0xA0  || type == 0xB0 || type == 0xE0)
817                 data2 = readByte();
818             if (type == 0x90)
819                 dur = read16bit();
820             switch (type) {
821                 case 0x90:
822                     Q_EMIT signalWRKNote(track, time, channel, data1, data2, dur);
823                     break;
824                 case 0xA0:
825                     Q_EMIT signalWRKKeyPress(track, time, channel, data1, data2);
826                     break;
827                 case 0xB0:
828                     Q_EMIT signalWRKCtlChange(track, time, channel, data1, data2);
829                     break;
830                 case 0xC0:
831                     Q_EMIT signalWRKProgram(track, time, channel, data1);
832                     break;
833                 case 0xD0:
834                     Q_EMIT signalWRKChanPress(track, time, channel, data1);
835                     break;
836                 case 0xE0:
837                     value = (data2 << 7) + data1 - 8192;
838                     Q_EMIT signalWRKPitchBend(track, time, channel, value);
839                     break;
840                 case 0xF0:
841                     Q_EMIT signalWRKSysexEvent(track, time, data1);
842                     break;
843             }
844         } else if (status == 5) {
845             int code = read16bit();
846             len = read32bit();
847             if (d->m_codec == nullptr) {
848                 data = readByteArray(len);
849                 Q_EMIT signalWRKExpression2(track, time, code, data);
850             } else {
851                 text = readString(len);
852                 Q_EMIT signalWRKExpression(track, time, code, text);
853             }
854         } else if (status == 6) {
855             int code = read16bit();
856             dur = read16bit();
857             readGap(4);
858             Q_EMIT signalWRKHairpin(track, time, code, dur);
859         } else if (status == 7) {
860             len = read32bit();
861             text = readString(len);
862             data.clear();
863             for(int j=0; j<13; ++j) {
864                 int byte = readByte();
865                 data += byte;
866             }
867             Q_EMIT signalWRKChord(track, time, text, data);
868         } else if (status == 8) {
869             len = read16bit();
870             data.clear();
871             for(int j=0; j<len; ++j) {
872                 int byte = readByte();
873                 data += byte;
874             }
875             Q_EMIT signalWRKSysex(0, QString(), false, 0, data);
876         } else {
877             len = read32bit();
878             if (d->m_codec == nullptr) {
879                 data = readByteArray(len);
880                 Q_EMIT signalWRKText2(track, time, status, data);
881             } else {
882                 text = readString(len);
883                 Q_EMIT signalWRKText(track, time, status, text);
884             }
885         }
886     }
887     if ((i < events) && atEnd()) {
888         Q_EMIT signalWRKError("Corrupted file");
889     }
890     Q_EMIT signalWRKStreamEnd(time + dur);
891 }
892 
processStreamChunk()893 void QWrk::processStreamChunk()
894 {
895     long time = 0;
896     int dur = 0, value = 0, type = 0, channel = 0, i = 0;
897     quint8 status = 0, data1 = 0, data2 = 0;
898     quint16 track = read16bit();
899     int events = read16bit();
900     for ( i = 0; (i < events) && (d->internalFilePos() < d->m_lastChunkPos) && !atEnd(); ++i ) {
901         time = read24bit();
902         status = readByte();
903         data1 = readByte();
904         data2 = readByte();
905         dur = read16bit();
906         type = status & 0xf0;
907         channel = status & 0x0f;
908         switch (type) {
909             case 0x90:
910                 Q_EMIT signalWRKNote(track, time, channel, data1, data2, dur);
911                 break;
912             case 0xA0:
913                 Q_EMIT signalWRKKeyPress(track, time, channel, data1, data2);
914                 break;
915             case 0xB0:
916                 Q_EMIT signalWRKCtlChange(track, time, channel, data1, data2);
917                 break;
918             case 0xC0:
919                 Q_EMIT signalWRKProgram(track, time, channel, data1);
920                 break;
921             case 0xD0:
922                 Q_EMIT signalWRKChanPress(track, time, channel, data1);
923                 break;
924             case 0xE0:
925                 value = (data2 << 7) + data1 - 8192;
926                 Q_EMIT signalWRKPitchBend(track, time, channel, value);
927                 break;
928             case 0xF0:
929                 Q_EMIT signalWRKSysexEvent(track, time, data1);
930                 break;
931         }
932     }
933     if ((i < events) && atEnd()) {
934         Q_EMIT signalWRKError("Corrupted file");
935     }
936     Q_EMIT signalWRKStreamEnd(time + dur);
937 }
938 
processMeterChunk()939 void QWrk::processMeterChunk()
940 {
941     int count = read16bit();
942     for (int i = 0; i < count; ++i) {
943         readGap(4);
944         int measure = read16bit();
945         int  num = readByte();
946         int  den = pow(2.0, readByte());
947         readGap(4);
948         Q_EMIT signalWRKTimeSig(measure, num, den);
949     }
950 }
951 
processMeterKeyChunk()952 void QWrk::processMeterKeyChunk()
953 {
954     int count = read16bit();
955     for (int i = 0; i < count; ++i) {
956         int measure = read16bit();
957         int  num = readByte();
958         int  den = pow(2.0, readByte());
959         qint8 alt = readByte();
960         Q_EMIT signalWRKTimeSig(measure, num, den);
961         Q_EMIT signalWRKKeySig(measure, alt);
962     }
963 }
964 
getRealTime(long ticks) const965 double QWrk::getRealTime(long ticks) const
966 {
967     double division = 1.0 * d->m_division;
968     RecTempo last;
969     last.time = 0;
970     last.tempo = 100.0;
971     last.seconds = 0.0;
972     if (!d->m_tempos.isEmpty()) {
973         foreach(const RecTempo& rec, d->m_tempos) {
974             if (rec.time >= ticks)
975                 break;
976             last = rec;
977         }
978     }
979     return last.seconds + (((ticks - last.time) / division) * (60.0 / last.tempo));
980 }
981 
processTempoChunk(int factor)982 void QWrk::processTempoChunk(int factor)
983 {
984     double division = 1.0 * d->m_division;
985     int count = read16bit();
986     RecTempo last, next;
987     for (int i = 0; i < count; ++i) {
988 
989         long time = read32bit();
990         readGap(4);
991         long tempo = read16bit() * factor;
992         readGap(8);
993 
994         next.time = time;
995         next.tempo = tempo / 100.0;
996         next.seconds = 0.0;
997         last.time = 0;
998         last.tempo = next.tempo;
999         last.seconds = 0.0;
1000         if (! d->m_tempos.isEmpty()) {
1001             foreach(const RecTempo& rec, d->m_tempos) {
1002                 if (rec.time >= time)
1003                     break;
1004                 last = rec;
1005             }
1006             next.seconds = last.seconds +
1007                 (((time - last.time) / division) * (60.0 / last.tempo));
1008         }
1009         d->m_tempos.append(next);
1010 
1011         Q_EMIT signalWRKTempo(time, tempo);
1012     }
1013 }
1014 
processSysexChunk()1015 void QWrk::processSysexChunk()
1016 {
1017     int j;
1018     QString name;
1019     QByteArray data;
1020     int bank = readByte();
1021     int length = read16bit();
1022     bool autosend = (readByte() != 0);
1023     int namelen = readByte();
1024     name = readString(namelen);
1025     for(j=0; j<length; ++j) {
1026         int byte = readByte();
1027         data += byte;
1028     }
1029     Q_EMIT signalWRKSysex(bank, name, autosend, 0, data);
1030 }
1031 
processSysex2Chunk()1032 void QWrk::processSysex2Chunk()
1033 {
1034     int j;
1035     QString name;
1036     QByteArray data;
1037     int bank = read16bit();
1038     int length = read32bit();
1039     quint8 b = readByte();
1040     int port = ( b & 0xf0 ) >> 4;
1041     bool autosend = ( (b & 0x0f) != 0);
1042     int namelen = readByte();
1043     name = readString(namelen);
1044     for(j=0; j<length; ++j) {
1045         int byte = readByte();
1046         data += byte;
1047     }
1048     Q_EMIT signalWRKSysex(bank, name, autosend, port, data);
1049 }
1050 
processNewSysexChunk()1051 void QWrk::processNewSysexChunk()
1052 {
1053     int j;
1054     QString name;
1055     QByteArray data;
1056     int bank = read16bit();
1057     int length = read32bit();
1058     int port = read16bit();
1059     bool autosend = (readByte() != 0);
1060     int namelen = readByte();
1061     name = readString(namelen);
1062     for(j=0; j<length; ++j) {
1063         int byte = readByte();
1064         data += byte;
1065     }
1066     Q_EMIT signalWRKSysex(bank, name, autosend, port, data);
1067 }
1068 
processThruChunk()1069 void QWrk::processThruChunk()
1070 {
1071     readGap(2);
1072     qint8 port = readByte();    // 0->127
1073     qint8 channel = readByte(); // -1, 0->15
1074     qint8 keyPlus = readByte(); // 0->127
1075     qint8 velPlus = readByte(); // 0->127
1076     qint8 localPort = readByte();
1077     qint8 mode = readByte();
1078     Q_EMIT signalWRKThru(mode, port, channel, keyPlus, velPlus, localPort);
1079 }
1080 
processTrackOffset()1081 void QWrk::processTrackOffset()
1082 {
1083     quint16 track = read16bit();
1084     qint16 offset = read16bit();
1085     Q_EMIT signalWRKTrackOffset(track, offset);
1086 }
1087 
processTrackReps()1088 void QWrk::processTrackReps()
1089 {
1090     quint16 track = read16bit();
1091     quint16 reps = read16bit();
1092     Q_EMIT signalWRKTrackReps(track, reps);
1093 }
1094 
processTrackPatch()1095 void QWrk::processTrackPatch()
1096 {
1097     quint16 track = read16bit();
1098     qint8 patch = readByte();
1099     Q_EMIT signalWRKTrackPatch(track, patch);
1100 }
1101 
processTimeFormat()1102 void QWrk::processTimeFormat()
1103 {
1104     quint16 fmt = read16bit();
1105     quint16 ofs = read16bit();
1106     Q_EMIT signalWRKTimeFormat(fmt, ofs);
1107 }
1108 
processComments()1109 void QWrk::processComments()
1110 {
1111     int len = read16bit();
1112     if (d->m_codec == nullptr) {
1113         QByteArray data = readByteArray(len);
1114         Q_EMIT signalWRKComments2(data);
1115     } else {
1116         QString text = readString(len);
1117         Q_EMIT signalWRKComments(text);
1118     }
1119 }
1120 
processVariableRecord(int max)1121 void QWrk::processVariableRecord(int max)
1122 {
1123     int datalen = max - 32;
1124     QByteArray data;
1125     QString name = readVarString();
1126     readGap(31 - name.length());
1127     for ( int i = 0; i < datalen; ++i ) {
1128         data += readByte();
1129     }
1130     while (data.endsWith('\0')) {
1131         data.chop(1);
1132     }
1133     Q_EMIT signalWRKVariableRecord(name, data);
1134 }
1135 
processUnknown(int id)1136 void QWrk::processUnknown(int id)
1137 {
1138     Q_EMIT signalWRKUnknownChunk(id, d->m_lastChunkData);
1139 }
1140 
processNewTrack()1141 void QWrk::processNewTrack()
1142 {
1143     QByteArray data;
1144     QString name;
1145     qint16 bank = -1;
1146     qint16 patch = -1;
1147     //qint16 vol = -1;
1148     //qint16 pan = -1;
1149     qint8 key = -1;
1150     qint8 vel = 0;
1151     quint8 port = 0;
1152     qint8 channel = 0;
1153     bool selected = false;
1154     bool muted = false;
1155     bool loop = false;
1156     quint16 track = read16bit();
1157     quint8 len = readByte();
1158     if (d->m_codec == nullptr) {
1159         data = readByteArray(len);
1160     } else {
1161         name = readString(len);
1162     }
1163     bank = read16bit();
1164     patch = read16bit();
1165     /*vol =*/ read16bit();
1166     /*pan =*/ read16bit();
1167     key = readByte();
1168     vel = readByte();
1169     readGap(7);
1170     port = readByte();
1171     channel = readByte();
1172     muted = (readByte() != 0);
1173     if (d->m_codec == nullptr) {
1174         Q_EMIT signalWRKNewTrack2(data, track, channel, key, vel, port, selected, muted, loop);
1175     } else {
1176         Q_EMIT signalWRKNewTrack(name, track, channel, key, vel, port, selected, muted, loop);
1177     }
1178     if (bank > -1)
1179         Q_EMIT signalWRKTrackBank(track, bank);
1180     if (patch > -1) {
1181         if (channel > -1)
1182             Q_EMIT signalWRKProgram(track, 0, channel, patch);
1183         else
1184             Q_EMIT signalWRKTrackPatch(track, patch);
1185     }
1186 }
1187 
processSoftVer()1188 void QWrk::processSoftVer()
1189 {
1190     int len = readByte();
1191     QString vers = readString(len);
1192     Q_EMIT signalWRKSoftVer(vers);
1193 }
1194 
processTrackName()1195 void QWrk::processTrackName()
1196 {
1197     int track = read16bit();
1198     int len = readByte();
1199     if (d->m_codec == nullptr) {
1200         QByteArray data = readByteArray(len);
1201         Q_EMIT signalWRKTrackName2(track, data);
1202     } else {
1203         QString name = readString(len);
1204         Q_EMIT signalWRKTrackName(track, name);
1205     }
1206 }
1207 
processStringTable()1208 void QWrk::processStringTable()
1209 {
1210     if (d->m_codec == nullptr) {
1211         QList<QByteArray> table;
1212         int rows = read16bit();
1213         for (int i = 0; i < rows; ++i) {
1214             int len = readByte();
1215             QByteArray name = readByteArray(len);
1216             /*int idx =*/ readByte();
1217             table.insert(i, name);
1218         }
1219         Q_EMIT signalWRKStringTable2(table);
1220     } else {
1221         QStringList table;
1222         int rows = read16bit();
1223         for (int i = 0; i < rows; ++i) {
1224             int len = readByte();
1225             QString name = readString(len);
1226             /*int idx =*/ readByte();
1227             table.insert(i, name);
1228         }
1229         Q_EMIT signalWRKStringTable(table);
1230     }
1231 }
1232 
processLyricsStream()1233 void QWrk::processLyricsStream()
1234 {
1235     quint16 track = read16bit();
1236     int events = read32bit();
1237     processNoteArray(track, events);
1238 }
1239 
processTrackVol()1240 void QWrk::processTrackVol()
1241 {
1242     quint16 track = read16bit();
1243     int vol = read16bit();
1244     Q_EMIT signalWRKTrackVol(track, vol);
1245 }
1246 
processNewTrackOffset()1247 void QWrk::processNewTrackOffset()
1248 {
1249     quint16 track = read16bit();
1250     int offset = read32bit();
1251     Q_EMIT signalWRKTrackOffset(track, offset);
1252 }
1253 
processTrackBank()1254 void QWrk::processTrackBank()
1255 {
1256     quint16 track = read16bit();
1257     int bank = read16bit();
1258     Q_EMIT signalWRKTrackBank(track, bank);
1259 }
1260 
processSegmentChunk()1261 void QWrk::processSegmentChunk()
1262 {
1263     QString name;
1264     QByteArray data;
1265     int track = read16bit();
1266     int offset = read32bit();
1267     readGap(8);
1268     int len = readByte();
1269     if (d->m_codec == nullptr) {
1270         data = readByteArray(len);
1271     } else {
1272         name = readString(len);
1273     }
1274     readGap(20);
1275     if (d->m_codec == nullptr) {
1276         Q_EMIT signalWRKSegment2(track, offset, data);
1277     } else {
1278         Q_EMIT signalWRKSegment(track, offset, name);
1279     }
1280     int events = read32bit();
1281     processNoteArray(track, events);
1282 }
1283 
processNewStream()1284 void QWrk::processNewStream()
1285 {
1286     QString name;
1287     QByteArray data;
1288     int track = read16bit();
1289     int len = readByte();
1290     if (d->m_codec == nullptr) {
1291         data = readByteArray(len);
1292         Q_EMIT signalWRKSegment2(track, 0, data);
1293     } else {
1294         name = readString(len);
1295         Q_EMIT signalWRKSegment(track, 0, name);
1296     }
1297     int events = read32bit();
1298     processNoteArray(track, events);
1299 }
1300 
processEndChunk()1301 void QWrk::processEndChunk()
1302 {
1303     emit signalWRKEnd();
1304 }
1305 
readChunk()1306 int QWrk::readChunk()
1307 {
1308     qint64 start_pos = d->internalFilePos();
1309     int ck = readByte();
1310     if (ck != END_CHUNK) {
1311         quint32 ck_len = read32bit();
1312         if (ck_len > d->m_IOStream->device()->bytesAvailable()) {
1313             Q_EMIT signalWRKError("Corrupted file");
1314             seek(start_pos);
1315             return END_CHUNK;
1316         }
1317         start_pos = d->internalFilePos();
1318         d->m_lastChunkPos = start_pos + ck_len;
1319         readRawData(ck_len);
1320         seek(start_pos);
1321         switch (ck) {
1322         case TRACK_CHUNK:
1323             processTrackChunk();
1324             break;
1325         case VARS_CHUNK:
1326             processVarsChunk();
1327             break;
1328         case TIMEBASE_CHUNK:
1329             processTimebaseChunk();
1330             break;
1331         case STREAM_CHUNK:
1332             processStreamChunk();
1333             break;
1334         case METER_CHUNK:
1335             processMeterChunk();
1336             break;
1337         case TEMPO_CHUNK:
1338             processTempoChunk(100);
1339             break;
1340         case NTEMPO_CHUNK:
1341             processTempoChunk();
1342             break;
1343         case SYSEX_CHUNK:
1344             processSysexChunk();
1345             break;
1346         case THRU_CHUNK:
1347             processThruChunk();
1348             break;
1349         case TRKOFFS_CHUNK:
1350             processTrackOffset();
1351             break;
1352         case TRKREPS_CHUNK:
1353             processTrackReps();
1354             break;
1355         case TRKPATCH_CHUNK:
1356             processTrackPatch();
1357             break;
1358         case TIMEFMT_CHUNK:
1359             processTimeFormat();
1360             break;
1361         case COMMENTS_CHUNK:
1362             processComments();
1363             break;
1364         case VARIABLE_CHUNK:
1365             processVariableRecord(ck_len);
1366             break;
1367         case NTRACK_CHUNK:
1368             processNewTrack();
1369             break;
1370         case SOFTVER_CHUNK:
1371             processSoftVer();
1372             break;
1373         case TRKNAME_CHUNK:
1374             processTrackName();
1375             break;
1376         case STRTAB_CHUNK:
1377             processStringTable();
1378             break;
1379         case LYRICS_CHUNK:
1380             processLyricsStream();
1381             break;
1382         case TRKVOL_CHUNK:
1383             processTrackVol();
1384             break;
1385         case NTRKOFS_CHUNK:
1386             processNewTrackOffset();
1387             break;
1388         case TRKBANK_CHUNK:
1389             processTrackBank();
1390             break;
1391         case METERKEY_CHUNK:
1392             processMeterKeyChunk();
1393             break;
1394         case SYSEX2_CHUNK:
1395             processSysex2Chunk();
1396             break;
1397         case NSYSEX_CHUNK:
1398             processNewSysexChunk();
1399             break;
1400         case SGMNT_CHUNK:
1401             processSegmentChunk();
1402             break;
1403         case NSTREAM_CHUNK:
1404             processNewStream();
1405             break;
1406         case MARKERS_CHUNK:
1407             processMarkers();
1408             break;
1409         default:
1410             processUnknown(ck);
1411         }
1412         if (d->internalFilePos() != d->m_lastChunkPos) {
1413             //qDebug() << Q_FUNC_INFO << "Current pos:" << d->internalFilePos() << "should be:" << d->m_lastChunkPos;
1414             seek(d->m_lastChunkPos);
1415         }
1416     }
1417     return ck;
1418 }
1419 
wrkRead()1420 void QWrk::wrkRead()
1421 {
1422     QByteArray hdr(HEADER.length(), ' ');
1423     d->m_tempos.clear();
1424     d->m_IOStream->device()->read(hdr.data(), HEADER.length());
1425     if (hdr == HEADER) {
1426         int vma, vme;
1427         int ck_id;
1428         readGap(1);
1429         vme = readByte();
1430         vma = readByte();
1431         Q_EMIT signalWRKHeader(vma, vme);
1432         do {
1433             ck_id = readChunk();
1434         }  while ((ck_id != END_CHUNK) && !atEnd());
1435         if (!atEnd()) {
1436             //qDebug() << Q_FUNC_INFO << "extra junk past the end at" << d->internalFilePos();
1437             readRawData(d->m_IOStream->device()->bytesAvailable());
1438             processUnknown(ck_id);
1439         }
1440         processEndChunk();
1441     } else
1442         Q_EMIT signalWRKError("Invalid file format");
1443 }
1444 
internalFilePos()1445 qint64 QWrk::QWrkPrivate::internalFilePos()
1446 {
1447     return m_IOStream->device()->pos();
1448 }
1449 
1450 const QByteArray QWrk::HEADER = QByteArrayLiteral("CAKEWALK");
1451 
1452 } // namespace File
1453 } // namespace drumstick
1454 
1455 DISABLE_WARNING_POP
1456