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 #ifndef DRUMSTICK_QWRK_H
20 #define DRUMSTICK_QWRK_H
21 
22 #include "macros.h"
23 #include <QObject>
24 #include <QScopedPointer>
25 
26 class QTextCodec;
27 class QDataStream;
28 
29 /**
30  * @file qwrk.h
31  * Cakewalk WRK Files Input
32  */
33 
34 namespace drumstick { namespace File {
35 
36 /**
37  * @addtogroup WRK Cakewalk WRK File Parser (Input)
38  * @{
39  *
40  * @enum WrkChunkType
41  * Record types within a WRK file
42  */
43 enum WrkChunkType {
44     TRACK_CHUNK = 1,     ///< Track prefix
45     STREAM_CHUNK = 2,    ///< Events stream
46     VARS_CHUNK = 3,      ///< Global variables
47     TEMPO_CHUNK = 4,     ///< Tempo map
48     METER_CHUNK = 5,     ///< Meter map
49     SYSEX_CHUNK = 6,     ///< System exclusive bank
50     MEMRGN_CHUNK = 7,    ///< Memory region
51     COMMENTS_CHUNK = 8,  ///< Comments
52     TRKOFFS_CHUNK = 9,   ///< Track offset
53     TIMEBASE_CHUNK = 10, ///< Timebase. If present is the first chunk in the file.
54     TIMEFMT_CHUNK = 11,  ///< SMPTE time format
55     TRKREPS_CHUNK = 12,  ///< Track repetitions
56     TRKPATCH_CHUNK = 14, ///< Track patch
57     NTEMPO_CHUNK = 15,   ///< New Tempo map
58     THRU_CHUNK = 16,     ///< Extended thru parameters
59     LYRICS_CHUNK = 18,   ///< Events stream with lyrics
60     TRKVOL_CHUNK = 19,   ///< Track volume
61     SYSEX2_CHUNK = 20,   ///< System exclusive bank
62     MARKERS_CHUNK = 21,  ///< Markers
63     STRTAB_CHUNK = 22,   ///< Table of text event types
64     METERKEY_CHUNK = 23, ///< Meter/Key map
65     TRKNAME_CHUNK = 24,  ///< Track name
66     VARIABLE_CHUNK = 26, ///< Variable record chunk
67     NTRKOFS_CHUNK = 27,  ///< Track offset
68     TRKBANK_CHUNK = 30,  ///< Track bank
69     NTRACK_CHUNK = 36,   ///< Track prefix
70     NSYSEX_CHUNK = 44,   ///< System exclusive bank
71     NSTREAM_CHUNK = 45,  ///< Events stream
72     SGMNT_CHUNK = 49,    ///< Segment prefix
73     SOFTVER_CHUNK = 74,  ///< Software version which saved the file
74     END_CHUNK = 255      ///< Last chunk, end of file
75 };
76 
77 /**
78  * Cakewalk WRK file format (input only)
79  *
80  * This class is used to parse Cakewalk WRK Files.
81  * Signals with QString parameters are deprecated because the class QTextCodec was removed from QtCore since Qt6.
82  *
83  * @since 0.3.0
84  */
85 class DRUMSTICK_EXPORT QWrk : public QObject
86 {
87     Q_OBJECT
88     Q_ENUM(WrkChunkType)
89 public:
90     explicit QWrk(QObject * parent = nullptr);
91     virtual ~QWrk();
92 
93     void readFromStream(QDataStream *stream);
94     void readFromFile(const QString& fileName);
95     Q_DECL_DEPRECATED QTextCodec* getTextCodec();
96     Q_DECL_DEPRECATED void setTextCodec(QTextCodec *codec);
97     long getFilePos();
98 
99     int getNow() const;
100     int getFrom() const;
101     int getThru() const;
102     int getKeySig() const;
103     int getClock() const;
104     int getAutoSave() const;
105     int getPlayDelay() const;
106     bool getZeroCtrls() const;
107     bool getSendSPP() const;
108     bool getSendCont() const;
109     bool getPatchSearch() const;
110     bool getAutoStop() const;
111     unsigned int getStopTime() const;
112     bool getAutoRewind() const;
113     int getRewindTime() const;
114     bool getMetroPlay() const;
115     bool getMetroRecord() const;
116     bool getMetroAccent() const;
117     int getCountIn() const;
118     bool getThruOn() const;
119     bool getAutoRestart() const;
120     int getCurTempoOfs() const;
121     int getTempoOfs1() const;
122     int getTempoOfs2() const;
123     int getTempoOfs3() const;
124     bool getPunchEnabled() const;
125     int getPunchInTime() const;
126     int getPunchOutTime() const;
127     int getEndAllTime() const;
128 
129     QByteArray getLastChunkRawData() const;
130     double getRealTime(long ticks) const;
131 
132     /**
133      * Cakewalk WRK file format header string id
134      */
135     static const QByteArray HEADER; ///< Cakewalk WRK File header id
136 
137 Q_SIGNALS:
138 
139     /**
140      * Emitted for a WRK file read error
141      *
142      * @param errorStr Error string
143      */
144     void signalWRKError(const QString& errorStr);
145 
146     /**
147      * Emitted after reading an unknown chunk
148      *
149      * @param type chunk type
150      * @param data chunk data (not decoded)
151      */
152     void signalWRKUnknownChunk(int type, const QByteArray& data);
153 
154     /**
155      * Emitted after reading a WRK header
156      *
157      * @param verh WRK file format version major
158      * @param verl WRK file format version minor
159      */
160     void signalWRKHeader(int verh, int verl);
161 
162     /**
163      * Emitted after reading the last chunk of a WRK file
164      */
165     void signalWRKEnd();
166 
167     /**
168      * Emitted after reading the last event of a event stream
169      * @param time musical time
170      */
171     void signalWRKStreamEnd(long time);
172 
173     /**
174      * Emitted after reading a Note message
175      *
176      * @param track track number
177      * @param time musical time
178      * @param chan MIDI Channel
179      * @param pitch MIDI Note
180      * @param vol Velocity
181      * @param dur Duration
182      */
183     void signalWRKNote(int track, long time, int chan, int pitch, int vol, int dur);
184 
185     /**
186      * Emitted after reading a Polyphonic Aftertouch message
187      *
188      * @param track track number
189      * @param time musical time
190      * @param chan MIDI Channel
191      * @param pitch MIDI Note
192      * @param press Pressure amount
193      */
194     void signalWRKKeyPress(int track, long time, int chan, int pitch, int press);
195 
196     /**
197      * Emitted after reading a Control Change message
198      *
199      * @param track track number
200      * @param time musical time
201      * @param chan MIDI Channel
202      * @param ctl MIDI Controller
203      * @param value Control value
204      */
205     void signalWRKCtlChange(int track, long time, int chan, int ctl, int value);
206 
207     /**
208      * Emitted after reading a Bender message
209      *
210      * @param track track number
211      * @param time musical time
212      * @param chan MIDI Channel
213      * @param value Bender value
214      */
215     void signalWRKPitchBend(int track, long time, int chan, int value);
216 
217     /**
218      * Emitted after reading a Program change message
219      *
220      * @param track track number
221      * @param time musical time
222      * @param chan MIDI Channel
223      * @param patch Program number
224      */
225     void signalWRKProgram(int track, long time, int chan, int patch);
226 
227     /**
228      * Emitted after reading a Channel Aftertouch message
229      *
230      * @param track track number
231      * @param time musical time
232      * @param chan MIDI Channel
233      * @param press Pressure amount
234      */
235     void signalWRKChanPress(int track, long time, int chan, int press);
236 
237     /**
238      * Emitted after reading a System Exclusive event
239      *
240      * @param track track number
241      * @param time musical time
242      * @param bank Sysex Bank number
243      */
244     void signalWRKSysexEvent(int track, long time, int bank);
245 
246     /**
247      * Emitted after reading a System Exclusive Bank
248      *
249      * @param bank Sysex Bank number
250      * @param name Sysex Bank name
251      * @param autosend Send automatically after loading the song
252      * @param port MIDI output port
253      * @param data Sysex bytes
254      */
255     void signalWRKSysex(int bank, const QString& name, bool autosend, int port, const QByteArray& data);
256 
257     /**
258      * Emitted after reading a text message
259      *
260      * @param track track number
261      * @param time musical time
262      * @param type Text type
263      * @param data Text data
264      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
265      * use signalWRKText2() instead
266      */
267     Q_DECL_DEPRECATED void signalWRKText(int track, long time, int type, const QString& data);
268 
269     /**
270      * Emitted after reading a WRK Time signature
271      *
272      * @param bar Measure number
273      * @param num Numerator
274      * @param den Denominator (exponent in a power of two)
275      */
276     void signalWRKTimeSig(int bar, int num, int den);
277 
278     /**
279      * Emitted after reading a WRK Key Signature
280      *
281      * @param bar Measure number
282      * @param alt Number of alterations (negative=flats, positive=sharps)
283      */
284     void signalWRKKeySig(int bar, int alt);
285 
286     /**
287      * Emitted after reading a Tempo Change message.
288      *
289      * Tempo units are given in beats * 100 per minute, so to obtain BPM
290      * it is necessary to divide by 100 the tempo.
291      *
292      * @param time musical time
293      * @param tempo beats per minute multiplied by 100
294      */
295     void signalWRKTempo(long time, int tempo);
296 
297     /**
298      * Emitted after reading a track prefix chunk
299      *
300      * @param name1 track 1st name
301      * @param name2 track 2nd name
302      * @param trackno track number
303      * @param channel track forced channel (-1=no forced)
304      * @param pitch track pitch transpose in semitones (-127..127)
305      * @param velocity track velocity increment (-127..127)
306      * @param port track forced port
307      * @param selected true if track is selected
308      * @param muted true if track is muted
309      * @param loop true if loop is enabled
310      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
311      * use signalWRKTrack2() instead
312      */
313     Q_DECL_DEPRECATED
314     void signalWRKTrack(const QString& name1,
315                         const QString& name2,
316                         int trackno, int channel, int pitch,
317                         int velocity, int port,
318                         bool selected, bool muted, bool loop );
319 
320     /**
321      * Emitted after reading the timebase chunk
322      *
323      * @param timebase ticks per quarter note
324      */
325     void signalWRKTimeBase(int timebase);
326 
327     /**
328      * Emitted after reading the global variables chunk.
329      *
330      * This record contains miscellaneous Cakewalk global variables that can
331      * be retrieved using individual getters.
332      *
333      * @see getNow(), getFrom(), getThru()
334      */
335     void signalWRKGlobalVars();
336 
337     /**
338      * Emitted after reading an Extended Thru parameters chunk.
339      *
340      * It was introduced in Cakewalk version 4.0.  These parameters are
341      * intended to override the global vars Thruon value, so this record should
342      * come after the VARS_CHUNK record. It is optional.
343      *
344      * @param mode (auto, off, on)
345      * @param port MIDI port
346      * @param channel MIDI channel
347      * @param keyPlus Note transpose
348      * @param velPlus Velocity transpose
349      * @param localPort MIDI local port
350      */
351     void signalWRKThru(int mode, int port, int channel, int keyPlus, int velPlus, int localPort);
352 
353     /**
354      * Emitted after reading a track offset chunk
355      *
356      * @param track track number
357      * @param offset time offset
358      */
359     void signalWRKTrackOffset(int track, int offset);
360 
361     /**
362      * Emitted after reading a track offset chunk
363      *
364      * @param track track number
365      * @param reps number of repetitions
366      */
367     void signalWRKTrackReps(int track, int reps);
368 
369     /**
370      * Emitted after reading a track patch chunk
371      *
372      * @param track track number
373      * @param patch
374      */
375     void signalWRKTrackPatch(int track, int patch);
376 
377     /**
378      * Emitted after reading a track bank chunk
379      *
380      * @param track track number
381      * @param bank
382      */
383     void signalWRKTrackBank(int track, int bank);
384 
385     /**
386      * Emitted after reading a SMPTE time format chunk
387      *
388      * @param frames frames/sec (24, 25, 29=30-drop, 30)
389      * @param offset frames of offset
390      */
391     void signalWRKTimeFormat(int frames, int offset);
392 
393     /**
394      * Emitted after reading a comments chunk
395      *
396      * @param data file text comments
397      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
398      * use signalWRKComments2() instead
399      */
400     Q_DECL_DEPRECATED void signalWRKComments(const QString& data);
401 
402     /**
403      * Emitted after reading a variable chunk.
404      * This record may contain data in text or binary format.
405      *
406      * @param name record identifier
407      * @param data record variable data
408      */
409     void signalWRKVariableRecord(const QString& name, const QByteArray& data);
410 
411     /**
412      * Emitted after reading a track volume chunk.
413      *
414      * @param track track number
415      * @param vol initial volume
416      */
417     void signalWRKTrackVol(int track, int vol);
418 
419     /**
420      * Emitted after reading a new track prefix
421      *
422      * @param name track name
423      * @param trackno track number
424      * @param channel forced MIDI channel
425      * @param pitch Note transposition
426      * @param velocity Velocity increment
427      * @param port MIDI port number
428      * @param selected track is selected
429      * @param muted track is muted
430      * @param loop track loop enabled
431      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
432      * use signalWRKNewTrack2() instead
433      */
434     Q_DECL_DEPRECATED
435     void signalWRKNewTrack( const QString& name,
436                             int trackno, int channel, int pitch,
437                             int velocity, int port,
438                             bool selected, bool muted, bool loop );
439 
440     /**
441      * Emitted after reading a software version chunk.
442      *
443      * @param version software version string
444      */
445     void signalWRKSoftVer(const QString& version);
446 
447     /**
448      * Emitted after reading a track name chunk.
449      *
450      * @param track track number
451      * @param name track name
452      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
453      * use signalWRKTrackName2() instead
454      */
455     Q_DECL_DEPRECATED void signalWRKTrackName(int track, const QString& name);
456 
457     /**
458      * Emitted after reading a string event types chunk.
459      *
460      * @param strs list of declared string event types
461      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
462      * use signalWRKStringTable2() instead
463      */
464     Q_DECL_DEPRECATED void signalWRKStringTable(const QStringList& strs);
465 
466     /**
467      * Emitted after reading a segment prefix chunk.
468      *
469      * @param track track number
470      * @param time segment time offset
471      * @param name segment name
472      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
473      * use signalWRKSegment2() instead
474      */
475     Q_DECL_DEPRECATED void signalWRKSegment(int track, long time, const QString& name);
476 
477     /**
478      * Emitted after reading a chord diagram chunk.
479      *
480      * @param track track number
481      * @param time event time in ticks
482      * @param name chord name
483      * @param data chord data definition (not decoded)
484      */
485     void signalWRKChord(int track, long time, const QString& name, const QByteArray& data);
486 
487     /**
488      * Emitted after reading an expression indication (notation) chunk.
489      *
490      * @param track track number
491      * @param time event time in ticks
492      * @param code expression event code
493      * @param text expression text
494      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
495      * use signalWRKExpression2() instead
496      */
497     Q_DECL_DEPRECATED void signalWRKExpression(int track, long time, int code, const QString& text);
498 
499     /**
500      * Emitted after reading a hairpin symbol (notation) chunk.
501      *
502      * @param track track number
503      * @param time event time in ticks
504      * @param code hairpin code
505      * @param dur duration
506      */
507     void signalWRKHairpin(int track, long time, int code, int dur);
508 
509     /**
510      * Emitted after reading a text message
511      * This signal is emitted when getTextCodec() is nullptr
512      *
513      * @param track track number
514      * @param time musical time
515      * @param type Text type
516      * @param data Text data
517      */
518     void signalWRKText2(int track, long time, int type, const QByteArray& data);
519 
520     /**
521      * Emitted after reading a track prefix chunk
522      * This signal is emitted when getTextCodec() is nullptr
523      *
524      * @param name1 track 1st name
525      * @param name2 track 2nd name
526      * @param trackno track number
527      * @param channel track forced channel (-1=no forced)
528      * @param pitch track pitch transpose in semitones (-127..127)
529      * @param velocity track velocity increment (-127..127)
530      * @param port track forced port
531      * @param selected true if track is selected
532      * @param muted true if track is muted
533      * @param loop true if loop is enabled
534      */
535     void signalWRKTrack2(const QByteArray& name1,
536                         const QByteArray& name2,
537                         int trackno, int channel, int pitch,
538                         int velocity, int port,
539                         bool selected, bool muted, bool loop );
540 
541     /**
542      * Emitted after reading a comments chunk
543      * This signal is emitted when getTextCodec() is nullptr
544      *
545      * @param data file text comments
546      */
547     void signalWRKComments2(const QByteArray& data);
548 
549     /**
550      * Emitted after reading a new track prefix
551      * This signal is emitted when getTextCodec() is nullptr
552      *
553      * @param name track name
554      * @param trackno track number
555      * @param channel forced MIDI channel
556      * @param pitch Note transposition
557      * @param velocity Velocity increment
558      * @param port MIDI port number
559      * @param selected track is selected
560      * @param muted track is muted
561      * @param loop track loop enabled
562      */
563     void signalWRKNewTrack2(const QByteArray& name,
564                             int trackno, int channel, int pitch,
565                             int velocity, int port,
566                             bool selected, bool muted, bool loop );
567     /**
568      * Emitted after reading a track name chunk.
569      * This signal is emitted when getTextCodec() is nullptr
570      *
571      * @param track track number
572      * @param name track name
573      */
574     void signalWRKTrackName2(int track, const QByteArray& name);
575 
576     /**
577      * Emitted after reading a string event types chunk.
578      * This signal is emitted when getTextCodec() is nullptr
579      *
580      * @param strs list of declared string event types
581      */
582     void signalWRKStringTable2(const QList<QByteArray>& strs);
583 
584     /**
585      * Emitted after reading a segment prefix chunk.
586      * This signal is emitted when getTextCodec() is nullptr
587      *
588      * @param track track number
589      * @param time segment time offset
590      * @param name segment name
591      */
592     void signalWRKSegment2(int track, long time, const QByteArray& name);
593 
594     /**
595      * Emitted after reading an expression indication (notation) chunk.
596      * This signal is emitted when getTextCodec() is nullptr
597      *
598      * @param track track number
599      * @param time event time in ticks
600      * @param code expression event code
601      * @param text expression text
602      */
603     void signalWRKExpression2(int track, long time, int code, const QByteArray& text);
604 
605     /**
606      * Emitted after reading a text marker
607      * This is deprecated because the class QTextCodec was removed from QtCore since Qt6
608      * Use signalWRKMarker2() instead
609      *
610      * @param time event time in ticks or smpte
611      * @param type tipe of time: 0=ticks or 1=smpte
612      * @param data marker text
613      * @deprecated because the class QTextCodec was removed from QtCore since Qt6.
614      * use signalWRKMarker2() instead
615      */
616     Q_DECL_DEPRECATED void signalWRKMarker(long time, int type, const QString& data);
617 
618     /**
619      * Emitted after reading a text marker
620      * This signal is emitted when getTextCodec() is nullptr
621      *
622      * @param time event time in ticks or smpte
623      * @param type tipe of time: 0=ticks or 1=smpte
624      * @param data marker text
625      */
626     void signalWRKMarker2(long time, int type, const QByteArray& data);
627 
628 private:
629     quint8 readByte();
630     quint16 to16bit(quint8 c1, quint8 c2);
631     quint32 to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4);
632     quint16 read16bit();
633     quint32 read24bit();
634     quint32 read32bit();
635     QString readString(int len);
636     QString readVarString();
637     void readRawData(int size);
638     void readGap(int size);
639     bool atEnd();
640     void seek(qint64 pos);
641 
642     int readChunk();
643     void processTrackChunk();
644     void processVarsChunk();
645     void processTimebaseChunk();
646     void processNoteArray(int track, int events);
647     void processStreamChunk();
648     void processMeterChunk();
649     void processTempoChunk(int factor = 1);
650     void processSysexChunk();
651     void processSysex2Chunk();
652     void processNewSysexChunk();
653     void processThruChunk();
654     void processTrackOffset();
655     void processTrackReps();
656     void processTrackPatch();
657     void processTrackBank();
658     void processTimeFormat();
659     void processComments();
660     void processVariableRecord(int max);
661     void processNewTrack();
662     void processSoftVer();
663     void processTrackName();
664     void processStringTable();
665     void processLyricsStream();
666     void processTrackVol();
667     void processNewTrackOffset();
668     void processMeterKeyChunk();
669     void processSegmentChunk();
670     void processNewStream();
671     void processUnknown(int id);
672     void processEndChunk();
673     void wrkRead();
674     QByteArray readByteArray(int len);
675     QByteArray readVarByteArray();
676     void processMarkers();
677 
678     struct RecTempo {
679         long time;
680         double tempo;
681         double seconds;
682     };
683 
684     class QWrkPrivate;
685     QScopedPointer<QWrkPrivate> d;
686 };
687 
688 /** @} */
689 
690 }} // namespace drumstick::File
691 
692 #endif // DRUMSTICK_QWRK_H
693