21 #include <QDataStream> 30 DISABLE_WARNING_DEPRECATED_DECLARATIONS
52 class QSmf::QSmfPrivate {
62 m_OldCurrTempo(500000),
80 double m_DblOldRealtime;
83 quint64 m_OldCurrTempo;
84 quint64 m_OldRealTime;
85 quint64 m_OldCurrTime;
86 quint64 m_RevisedTime;
87 quint64 m_TempoChangeTime;
89 quint64 m_NumBytesWritten;
94 QDataStream *m_IOStream;
96 QList<QSmfRecTempo> m_TempoList;
113 d->m_TempoList.clear();
120 bool QSmf::endOfSmf()
122 return d->m_IOStream->atEnd();
129 quint8 QSmf::getByte()
144 void QSmf::putByte(quint8 value)
146 *d->m_IOStream << value;
147 d->m_NumBytesWritten++;
155 void QSmf::addTempo(quint64 tempo, quint64 time)
157 QSmfRecTempo tempoRec;
158 tempoRec.tempo = tempo;
159 tempoRec.time = time;
160 d->m_TempoList.append(tempoRec);
166 void QSmf::readHeader()
171 d->m_CurrTempo = 500000;
172 d->m_OldCurrTempo = 500000;
173 addTempo(d->m_CurrTempo, 0);
174 if (d->m_Interactive)
182 readExpected(
"MThd");
183 d->m_ToBeRead = read32bit();
184 d->m_fileFormat = read16bit();
185 d->m_Tracks = read16bit();
186 d->m_Division = read16bit();
191 while ((d->m_ToBeRead > 0) && !endOfSmf())
195 if (d->m_ToBeRead > 0)
197 SMFError(
"Unexpected end of input");
204 void QSmf::readTrack()
209 static const quint8 chantype[16] =
210 { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0 };
219 quint64 delta_ticks, save_time, save_tempo;
221 sysexcontinue =
false;
223 if (d->m_Interactive)
225 d->m_ToBeRead = std::numeric_limits<unsigned long long>::max();
229 readExpected(
"MTrk");
230 d->m_ToBeRead = read32bit();
234 d->m_DblRealTime = 0;
235 d->m_DblOldRealtime = 0;
236 d->m_OldCurrTime = 0;
237 d->m_OldRealTime = 0;
238 d->m_CurrTempo = findTempo();
242 while (!endOfSmf() && (d->m_Interactive || d->m_ToBeRead > 0))
245 if (d->m_Interactive)
251 delta_ticks = unsigned(readVarLen());
252 d->m_RevisedTime = d->m_CurrTime;
253 d->m_CurrTime += delta_ticks;
254 while (d->m_RevisedTime < d->m_CurrTime)
256 save_time = d->m_RevisedTime;
257 save_tempo = d->m_CurrTempo;
258 d->m_CurrTempo = findTempo();
259 if (d->m_CurrTempo != d->m_OldCurrTempo)
261 d->m_OldCurrTempo = d->m_CurrTempo;
262 d->m_OldRealTime = d->m_RealTime;
263 if (d->m_RevisedTime != d->m_TempoChangeTime)
265 d->m_DblOldRealtime = d->m_DblRealTime;
266 d->m_OldCurrTime = save_time;
268 delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime,
269 quint16(d->m_Division), save_tempo);
270 d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0;
271 d->m_RealTime = llround(d->m_DblRealTime);
272 if (d->m_RevisedTime == d->m_TempoChangeTime)
274 d->m_OldCurrTime = d->m_RevisedTime;
275 d->m_DblOldRealtime = d->m_DblRealTime;
280 delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime,
281 quint16(d->m_Division), d->m_CurrTempo);
282 d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0;
283 d->m_RealTime = llround(d->m_DblRealTime);
291 SMFError(
"didn't find expected continuation of a SysEx");
299 SMFError(
"unexpected running status");
308 needed = chantype[status >> 4 & 0x0f];
321 channelMessage(status, c1, getByte());
325 channelMessage(status, c1, 0);
335 lookfor = quint64(readVarLen());
336 lookfor = d->m_ToBeRead - lookfor;
338 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
345 lookfor = quint64(readVarLen());
346 lookfor = d->m_ToBeRead - lookfor;
349 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
360 sysexcontinue =
true;
364 lookfor = readVarLen();
365 lookfor = d->m_ToBeRead - lookfor;
370 while ((d->m_ToBeRead > lookfor) && !endOfSmf())
380 sysexcontinue =
false;
385 badByte(c, d->m_IOStream->device()->pos() - 1);
388 if ((d->m_ToBeRead > lookfor) && endOfSmf()) {
389 SMFError(
"Unexpected end of input");
392 if (d->m_ToBeRead > 0) {
393 SMFError(QStringLiteral(
"Track ended before reading last %1 bytes").arg(d->m_ToBeRead));
405 for ( i = d->m_Tracks; (i > 0) && !endOfSmf(); i--)
411 QStringLiteral(
"%1 tracks out of a total of %2 are missing").arg(i).arg(d->m_Tracks));
422 void QSmf::SMFWrite()
426 writeHeaderChunk(d->m_fileFormat, d->m_Tracks, d->m_Division);
428 if (d->m_fileFormat == 1)
432 for (i = 0; i < d->m_Tracks; ++i)
444 d->m_IOStream = stream;
454 QFile file(fileName);
455 file.open(QIODevice::ReadOnly);
456 QDataStream ds(&file);
467 d->m_IOStream = stream;
477 QFile file(fileName);
478 file.open(QIODevice::WriteOnly);
479 QDataStream ds(&file);
490 void QSmf::writeHeaderChunk(
int format,
int ntracks,
int division)
494 write16bit(quint16(format));
495 write16bit(quint16(ntracks));
496 write16bit(quint16(division));
503 void QSmf::writeTrackChunk(
int track)
513 offset = d->m_IOStream->device()->pos();
515 write32bit(trklength);
516 d->m_NumBytesWritten = 0;
520 place_marker = d->m_IOStream->device()->pos();
521 d->m_IOStream->device()->seek(offset);
522 trklength = d->m_NumBytesWritten;
524 write32bit(trklength);
525 d->m_IOStream->device()->seek(place_marker);
536 writeVarLen(deltaTime);
538 putByte(d->m_LastStatus);
540 writeVarLen(data.size());
541 foreach(
char byte, data)
553 writeVarLen(deltaTime);
557 if (d->m_codec ==
nullptr)
558 lcldata = data.toLatin1();
560 lcldata = d->m_codec->fromUnicode(data);
561 writeVarLen(lcldata.length());
562 foreach(
char byte, lcldata)
575 writeVarLen(deltaTime);
589 writeVarLen(deltaTime);
603 const QByteArray& data)
605 unsigned int i, j, size;
607 writeVarLen(quint64(deltaTime));
617 SMFError(
"error: MIDI channel greater than 16");
621 if (d->m_LastStatus != c)
634 j = (c == type ? 1 : 0);
635 for (i = j; i < unsigned(data.size()); ++i)
637 putByte(quint8(data[i]));
651 writeVarLen(deltaTime);
654 SMFError(
"error: Wrong method for a system exclusive event");
658 SMFError(
"error: MIDI channel greater than 16");
661 if (d->m_LastStatus != c)
680 writeVarLen(deltaTime);
683 SMFError(
"error: Wrong method for a system exclusive event");
687 SMFError(
"error: MIDI channel greater than 16");
690 if (d->m_LastStatus != c)
708 unsigned int i, j, size;
710 writeVarLen(quint64(deltaTime));
713 SMFError(
"error: type should be system exclusive");
718 size = unsigned(len);
723 j = (c == type ? 1 : 0);
724 for (i = j; i < unsigned(len); ++i)
726 putByte(quint8(data[i]));
737 writeVarLen(deltaTime);
739 putByte(d->m_LastStatus);
742 putByte((seqnum >> 8) & 0xff);
743 putByte(seqnum & 0xff);
753 writeVarLen(deltaTime);
757 putByte((tempo >> 16) & 0xff);
758 putByte((tempo >> 8) & 0xff);
759 putByte(tempo & 0xff);
769 long us_tempo = 60000000l / tempo;
783 writeVarLen(deltaTime);
801 writeVarLen(quint64(deltaTime));
805 putByte(quint8(tone));
806 putByte(mode & 0x01);
813 void QSmf::writeVarLen(quint64 value)
817 buffer = value & 0x7f;
818 while ((value >>= 7) > 0)
822 buffer += (value & 0x7f);
826 putByte(buffer & 0xff);
836 void QSmf::write32bit(quint32 data)
838 putByte((data >> 24) & 0xff);
839 putByte((data >> 16) & 0xff);
840 putByte((data >> 8) & 0xff);
841 putByte(data & 0xff);
844 void QSmf::write16bit(quint16 data)
846 putByte((data >> 8) & 0xff);
847 putByte(data & 0xff);
850 quint16 QSmf::to16bit(quint8 c1, quint8 c2)
853 value = quint16(c1 << 8);
858 quint32 QSmf::to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4)
861 value = unsigned(c1 << 24);
862 value += unsigned(c2 << 16);
863 value += unsigned(c3 << 8);
868 quint16 QSmf::read16bit()
873 return to16bit(c1, c2);
876 quint32 QSmf::read32bit()
878 quint8 c1, c2, c3, c4;
883 return to32bit(c1, c2, c3, c4);
886 long QSmf::readVarLen()
899 value = (value << 7) + (c & 0x7f);
900 }
while ((c & 0x80) != 0);
905 void QSmf::readExpected(
const QString& s)
909 for (j = 0; j < s.length(); ++j)
912 if (QChar(b) != s[j])
914 SMFError(QString(
"Invalid (%1) SMF format at %2").arg(b, 0, 16).arg(d->m_IOStream->device()->pos()));
920 quint64 QSmf::findTempo()
922 quint64 result, old_tempo, new_tempo;
923 QSmfRecTempo rec = d->m_TempoList.last();
924 old_tempo = d->m_CurrTempo;
925 new_tempo = d->m_CurrTempo;
926 QList<QSmfRecTempo>::Iterator it;
927 for( it = d->m_TempoList.begin(); it != d->m_TempoList.end(); ++it )
930 if (rec.time <= d->m_CurrTime)
932 old_tempo = rec.tempo;
934 new_tempo = rec.tempo;
935 if (rec.time > d->m_RevisedTime)
940 if ((rec.time <= d->m_RevisedTime) || (rec.time > d->m_CurrTime))
942 d->m_RevisedTime = d->m_CurrTime;
947 d->m_RevisedTime = rec.time;
948 d->m_TempoChangeTime = d->m_RevisedTime;
957 double QSmf::ticksToSecs(quint64 ticks, quint16 division, quint64 tempo)
961 double smpte_resolution;
965 result = double(ticks * tempo)/(division * 1000000.0);
969 smpte_format = upperByte(division);
970 smpte_resolution = lowerByte(division);
971 result = double(ticks)/(smpte_format * smpte_resolution
977 void QSmf::SMFError(
const QString& s)
982 void QSmf::channelMessage(quint8 status, quint8 c1, quint8 c2)
989 SMFError(QString(
"ChannelMessage with bad c1 = %1").arg(c1));
994 SMFError(QString(
"ChannelMessage with bad c2 = %1").arg(c2));
1018 k = c1 + (c2 << 7) - 8192;
1022 SMFError(QString(
"Invalid MIDI status %1. Unhandled event").arg(status));
1027 void QSmf::metaEvent(quint8 b)
1030 QByteArray m(d->m_MsgBuff);
1045 if (d->m_codec ==
nullptr) {
1048 s = d->m_codec->toUnicode(m);
1063 d->m_CurrTempo = to32bit(0, m[0], m[1], m[2]);
1065 rec = d->m_TempoList.last();
1066 if (rec.tempo == d->m_CurrTempo)
1070 if (rec.time > d->m_CurrTime)
1074 addTempo(d->m_CurrTempo, d->m_CurrTime);
1097 QByteArray varr(d->m_MsgBuff);
1101 void QSmf::badByte(quint8 b,
int p)
1103 SMFError(QString(
"Unexpected byte (%1) at %2").arg(b, 2, 16).arg(p));
1106 quint8 QSmf::lowerByte(quint16 x)
1111 quint8 QSmf::upperByte(quint16 x)
1113 return ((x >> 8) & 0xff);
1116 void QSmf::msgInit()
1118 d->m_MsgBuff.truncate(0);
1121 void QSmf::msgAdd(quint8 b)
1123 int s = d->m_MsgBuff.size();
1124 d->m_MsgBuff.resize(s + 1);
1125 d->m_MsgBuff[s] = b;
1136 return d->m_CurrTime;
1145 return d->m_CurrTempo;
1154 return d->m_RealTime;
1163 return d->m_Division;
1172 d->m_Division = division;
1190 d->m_Tracks = tracks;
1199 return d->m_fileFormat;
1208 d->m_fileFormat = fileFormat;
1217 return long(d->m_IOStream->device()->pos());
1250 return QStringLiteral(QT_STRINGIFY(VERSION));
void writeMetaEvent(long deltaTime, int type, const QByteArray &data)
Writes a variable length Meta Event.
void signalSMFSysex(const QByteArray &data)
Emitted after reading a System Exclusive message.
void writeTempo(long deltaTime, long tempo)
Writes a Tempo change message.
const quint8 text_event
SMF Text event.
void signalSMFPitchBend(int chan, int value)
Emitted after reading a Bender message.
const quint8 control_change
MIDI event Control change.
QSmf(QObject *parent=nullptr)
Constructor.
long getRealTime()
Gets the real time in seconds.
Q_DECL_DEPRECATED void signalSMFText(int typ, const QString &data)
Emitted after reading a SMF text message.
void signalSMFTrackStart()
Emitted after reading a track prefix.
void signalSMFendOfTrack()
Emitted after reading a End-Of-Track message.
const quint8 lyric
SMF Lyric.
void signalSMFforcedChannel(int channel)
Emitted after reading a Forced channel message.
void signalSMFSmpte(int b0, int b1, int b2, int b3, int b4)
Emitted after reading a SMPT offset message.
void writeKeySignature(long deltaTime, int tone, int mode)
Writes a key Signature message.
void signalSMFTimeSig(int b0, int b1, int b2, int b3)
Emitted after reading a SMF Time signature message.
void setDivision(int division)
Sets the resolution.
void writeSequenceNumber(long deltaTime, int seqnum)
Writes a MIDI Sequence number.
const quint8 key_signature
SMF Key signature.
void signalSMFWriteTempoTrack()
Emitted to request the user to prepare the tempo track.
void signalSMFWriteTrack(int track)
Emitted to request the user to write a track.
long getCurrentTempo()
Gets the current tempo.
void signalSMFChanPress(int chan, int press)
Emitted after reading a Channel Aftertouch message.
void signalSMFProgram(int chan, int patch)
Emitted after reading a Program change message.
const quint8 meta_event
SMF Meta Event prefix.
Q_DECL_DEPRECATED QTextCodec * getTextCodec()
Gets the text codec used for text meta-events I/O.
const quint8 midi_command_mask
Mask to extract the command from the status byte.
void signalSMFError(const QString &errorStr)
Emitted for a SMF read or write error.
void signalSMFSequenceNum(int seq)
Emitted after reading a Sequence number message.
The QObject class is the base class of all Qt objects.
const quint8 forced_channel
SMF Forced MIDI channel.
void signalSMFMetaUnregistered(int typ, const QByteArray &data)
Emitted after reading an unregistered SMF Meta message.
void signalSMFSeqSpecific(const QByteArray &data)
Emitted after reading a Sequencer specific message.
const quint8 note_on
MIDI event Note On.
const quint8 sequencer_specific
SMF Sequencer specific.
void writeTimeSignature(long deltaTime, int num, int den, int cc, int bb)
Writes a Time Signature message.
const quint8 instrument_name
SMF Instrument name.
const quint8 marker
SMF Marker.
const quint32 MThd
SMF Header prefix.
void readFromStream(QDataStream *stream)
Reads a SMF stream.
void writeMidiEvent(long deltaTime, int type, int chan, int b1)
Writes a MIDI message with a single parameter.
void setTracks(int tracks)
Sets the number of tracks.
QString drumstickLibraryVersion()
drumstickLibraryVersion provides the Drumstick version as an edited QString
void signalSMFNoteOn(int chan, int pitch, int vol)
Emitted after reading a Note On message.
const quint8 poly_aftertouch
MIDI event Polyphonic pressure.
void writeBpmTempo(long deltaTime, int tempo)
Writes a Tempo change message.
const quint8 sequence_name
SMF Sequence name.
const quint8 end_of_track
SMF End of track.
const quint8 set_tempo
SMF Tempo change.
void signalSMFTempo(int tempo)
Emitted after reading a Tempo Change message.
Q_DECL_DEPRECATED void setTextCodec(QTextCodec *codec)
Sets the text codec for text meta-events.
const quint8 program_chng
MIDI event Program change.
const quint8 midi_channel_mask
Mask to extract the channel from the status byte.
void signalSMFTrackEnd()
Emitted after a track has finished.
void setFileFormat(int fileFormat)
Sets the SMF file format.
long getFilePos()
Gets the position in the SMF stream.
const quint32 MTrk
SMF Track prefix.
const quint8 note_off
MIDI event Note Off.
void signalSMFMetaMisc(int typ, const QByteArray &data)
Emitted after reading any SMF Meta message.
const quint8 forced_port
SMF Forced MIDI port.
void readFromFile(const QString &fileName)
Reads a SMF stream from a disk file.
void signalSMFKeySig(int b0, int b1)
Emitted after reading a SMF Key Signature smessage.
void writeToFile(const QString &fileName)
Writes a SMF stream to a disk file.
const quint8 copyright_notice
SMF Copyright notice.
void signalSMFNoteOff(int chan, int pitch, int vol)
Emitted after reading a Note Off message.
long getCurrentTime()
Gets the current time in ticks.
void signalSMFText2(int typ, const QByteArray &data)
Emitted after reading a SMF text message.
const quint8 end_of_sysex
MIDI event System Exclusive end.
const quint8 smpte_offset
SMF SMPTE offset.
const quint8 pitch_wheel
MIDI event Bender.
void signalSMFforcedPort(int port)
Emitted after reading a Forced port message.
void signalSMFKeyPress(int chan, int pitch, int press)
Emitted after reading a Polyphonic Aftertouch message.
void signalSMFHeader(int format, int ntrks, int division)
Emitted after reading a SMF header.
void signalSMFCtlChange(int chan, int ctl, int value)
Emitted after reading a Control Change message.
const quint8 time_signature
SMF Time signature.
const quint8 sequence_number
SMF Sequence number.
const quint8 channel_aftertouch
MIDI event Channel after-touch.
const quint8 system_exclusive
MIDI event System Exclusive begin.
int getDivision()
Gets the resolution.
void writeToStream(QDataStream *stream)
Writes a SMF stream.
const quint8 cue_point
SMF Cue point.
int getFileFormat()
Gets the SMF file format.
Standard MIDI Files Input/Output.
virtual ~QSmf()
Destructor.
int getTracks()
Gets the number of tracks.