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