30DISABLE_WARNING_DEPRECATED_DECLARATIONS
52class 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();
122 return d->m_IOStream->atEnd();
129quint8 QSmf::getByte()
144void QSmf::putByte(quint8 value)
146 *d->m_IOStream << value;
147 d->m_NumBytesWritten++;
155void QSmf::addTempo(quint64 tempo, quint64 time)
157 QSmfRecTempo tempoRec;
158 tempoRec.tempo = tempo;
159 tempoRec.time = time;
160 d->m_TempoList.append(tempoRec);
166void 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");
204void 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));
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);
490void QSmf::writeHeaderChunk(
int format,
int ntracks,
int division)
494 write16bit(quint16(format));
495 write16bit(quint16(ntracks));
496 write16bit(quint16(division));
503void 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);
813void QSmf::writeVarLen(quint64 value)
817 buffer = value & 0x7f;
818 while ((value >>= 7) > 0)
822 buffer += (value & 0x7f);
826 putByte(buffer & 0xff);
836void QSmf::write32bit(quint32 data)
838 putByte((data >> 24) & 0xff);
839 putByte((data >> 16) & 0xff);
840 putByte((data >> 8) & 0xff);
841 putByte(data & 0xff);
844void QSmf::write16bit(quint16 data)
846 putByte((data >> 8) & 0xff);
847 putByte(data & 0xff);
850quint16 QSmf::to16bit(quint8 c1, quint8 c2)
853 value = quint16(c1 << 8);
858quint32 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);
868quint16 QSmf::read16bit()
873 return to16bit(c1, c2);
876quint32 QSmf::read32bit()
878 quint8 c1, c2, c3, c4;
883 return to32bit(c1, c2, c3, c4);
886long QSmf::readVarLen()
899 value = (value << 7) + (c & 0x7f);
900 }
while ((c & 0x80) != 0);
905void 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()));
920quint64 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;
957double 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
977void QSmf::SMFError(
const QString& s)
982void 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));
1027void 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);
1101void QSmf::badByte(quint8 b,
int p)
1103 SMFError(QString(
"Unexpected byte (%1) at %2").arg(b, 2, 16).arg(p));
1106quint8 QSmf::lowerByte(quint16 x)
1111quint8 QSmf::upperByte(quint16 x)
1113 return ((x >> 8) & 0xff);
1118 d->m_MsgBuff.truncate(0);
1121void 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));
The QObject class is the base class of all Qt objects.
int getTracks()
Gets the number of tracks.
void signalSMFKeyPress(int chan, int pitch, int press)
Emitted after reading a Polyphonic Aftertouch message.
Q_DECL_DEPRECATED void setTextCodec(QTextCodec *codec)
Sets the text codec for text meta-events.
long getRealTime()
Gets the real time in seconds.
long getCurrentTempo()
Gets the current tempo.
void signalSMFforcedChannel(int channel)
Emitted after reading a Forced channel message.
void setDivision(int division)
Sets the resolution.
long getFilePos()
Gets the position in the SMF stream.
void signalSMFforcedPort(int port)
Emitted after reading a Forced port message.
void signalSMFTimeSig(int b0, int b1, int b2, int b3)
Emitted after reading a SMF Time signature message.
void signalSMFKeySig(int b0, int b1)
Emitted after reading a SMF Key Signature smessage.
long getCurrentTime()
Gets the current time in ticks.
void signalSMFChanPress(int chan, int press)
Emitted after reading a Channel Aftertouch message.
void setTracks(int tracks)
Sets the number of tracks.
void writeTimeSignature(long deltaTime, int num, int den, int cc, int bb)
Writes a Time Signature message.
void signalSMFTrackEnd()
Emitted after a track has finished.
void signalSMFNoteOn(int chan, int pitch, int vol)
Emitted after reading a Note On message.
void signalSMFTempo(int tempo)
Emitted after reading a Tempo Change message.
void signalSMFWriteTrack(int track)
Emitted to request the user to write a track.
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 signalSMFError(const QString &errorStr)
Emitted for a SMF read or write error.
int getDivision()
Gets the resolution.
QSmf(QObject *parent=nullptr)
Constructor.
void writeToFile(const QString &fileName)
Writes a SMF stream to a disk file.
void signalSMFSeqSpecific(const QByteArray &data)
Emitted after reading a Sequencer specific message.
void signalSMFWriteTempoTrack()
Emitted to request the user to prepare the tempo track.
void signalSMFendOfTrack()
Emitted after reading a End-Of-Track message.
void writeSequenceNumber(long deltaTime, int seqnum)
Writes a MIDI Sequence number.
void signalSMFHeader(int format, int ntrks, int division)
Emitted after reading a SMF header.
void readFromStream(QDataStream *stream)
Reads a SMF stream.
void signalSMFProgram(int chan, int patch)
Emitted after reading a Program change message.
void signalSMFText2(int typ, const QByteArray &data)
Emitted after reading a SMF text message.
void signalSMFNoteOff(int chan, int pitch, int vol)
Emitted after reading a Note Off message.
void writeMetaEvent(long deltaTime, int type, const QByteArray &data)
Writes a variable length Meta Event.
void signalSMFSequenceNum(int seq)
Emitted after reading a Sequence number message.
void signalSMFMetaMisc(int typ, const QByteArray &data)
Emitted after reading any SMF Meta message.
Q_DECL_DEPRECATED QTextCodec * getTextCodec()
Gets the text codec used for text meta-events I/O.
virtual ~QSmf()
Destructor.
void writeBpmTempo(long deltaTime, int tempo)
Writes a Tempo change message.
void readFromFile(const QString &fileName)
Reads a SMF stream from a disk file.
void signalSMFSysex(const QByteArray &data)
Emitted after reading a System Exclusive message.
void writeToStream(QDataStream *stream)
Writes a SMF stream.
void writeTempo(long deltaTime, long tempo)
Writes a Tempo change message.
void signalSMFCtlChange(int chan, int ctl, int value)
Emitted after reading a Control Change message.
void writeKeySignature(long deltaTime, int tone, int mode)
Writes a key Signature message.
void setFileFormat(int fileFormat)
Sets the SMF file format.
void signalSMFPitchBend(int chan, int value)
Emitted after reading a Bender message.
int getFileFormat()
Gets the SMF file format.
void writeMidiEvent(long deltaTime, int type, int chan, int b1)
Writes a MIDI message with a single parameter.
void signalSMFMetaUnregistered(int typ, const QByteArray &data)
Emitted after reading an unregistered SMF Meta message.
void signalSMFSmpte(int b0, int b1, int b2, int b3, int b4)
Emitted after reading a SMPT offset message.
const quint8 cue_point
SMF Cue point.
const quint8 system_exclusive
MIDI event System Exclusive begin.
const quint8 sequencer_specific
SMF Sequencer specific.
const quint32 MTrk
SMF Track prefix.
const quint8 forced_channel
SMF Forced MIDI channel.
const quint8 midi_channel_mask
Mask to extract the channel from the status byte.
QString drumstickLibraryVersion()
drumstickLibraryVersion provides the Drumstick version as an edited QString
const quint8 note_on
MIDI event Note On.
const quint8 control_change
MIDI event Control change.
const quint8 note_off
MIDI event Note Off.
const quint8 smpte_offset
SMF SMPTE offset.
const quint8 sequence_number
SMF Sequence number.
const quint8 sequence_name
SMF Sequence name.
const quint8 pitch_wheel
MIDI event Bender.
const quint8 meta_event
SMF Meta Event prefix.
const quint8 time_signature
SMF Time signature.
const quint8 end_of_track
SMF End of track.
const quint32 MThd
SMF Header prefix.
const quint8 poly_aftertouch
MIDI event Polyphonic pressure.
const quint8 key_signature
SMF Key signature.
const quint8 text_event
SMF Text event.
const quint8 channel_aftertouch
MIDI event Channel after-touch.
const quint8 instrument_name
SMF Instrument name.
const quint8 marker
SMF Marker.
const quint8 forced_port
SMF Forced MIDI port.
const quint8 copyright_notice
SMF Copyright notice.
const quint8 program_chng
MIDI event Program change.
const quint8 midi_command_mask
Mask to extract the command from the status byte.
const quint8 end_of_sysex
MIDI event System Exclusive end.
const quint8 lyric
SMF Lyric.
const quint8 set_tempo
SMF Tempo change.
Standard MIDI Files Input/Output.