示例#1
0
quint32 SMFProcessor::estimateRemainingTime(const MidiEventList &midiEvents, int currentEventIx) {
	MasterClockNanos tick = midiTick;
	MasterClockNanos totalNanos = 0;
	for (int i = currentEventIx; i < midiEvents.count(); i++) {
		const MidiEvent &e = midiEvents.at(i);
		totalNanos += e.getTimestamp() * tick;
		if (e.getType() == SET_TEMPO) tick = parser.getMidiTick(e.getShortMessage());
	}
	return quint32(totalNanos / MasterClock::NANOS_PER_SECOND);
}
示例#2
0
bool MidiParser::parseTrack(MidiEventList &midiEventList) {
	char header[8];
	forever {
		if (!readFile(header, 8)) return false;
		if (memcmp(header, trackID, 4) == 0) {
			break;
		} else {
			qDebug() << "MidiParser: Wrong MIDI track signature, skipping unknown data chunk";
			quint32 dataLen = qFromBigEndian<quint32>((uchar *)&header[4]);
			if (file.seek(file.pos() + dataLen)) {
				qDebug() << "MidiParser: Error in data chunk";
				return false;
			}
		}
	}
	quint32 trackLen = qFromBigEndian<quint32>((uchar *)&header[4]);
	char *trackData = new char[trackLen];
	if (!readFile(trackData, trackLen)) {
		delete trackData;
		return false;
	}

	// Reserve memory for MIDI events, approx. 3 bytes per event
	midiEventList.reserve(trackLen / 3);
	qDebug() << "MidiParser: Memory reservation" << trackLen / 3;

	// Parsing actual MIDI events
	unsigned int runningStatus = 0;
	uchar *data = (uchar *)trackData;
	uchar sysexBuffer[MAX_SYSEX_LENGTH];
	while(data < (uchar *)trackData + trackLen) {
		SynthTimestamp time = parseVarLenInt(data);
		quint32 message = 0;
		if (*data & 0x80) {
			// It's normal status byte
			if ((*data & 0xF0) == 0xF0) {
				// It's a special event
				if (*data == 0xF0) {
					// It's a sysex event
					sysexBuffer[0] = *(data++);
					quint32 sysexLength = parseVarLenInt(data);
					if (MAX_SYSEX_LENGTH <= sysexLength) {
						qDebug() << "MidiParser: too long sysex encountered:" << sysexLength;
						data += sysexLength;
						continue;
					}
					memcpy(&sysexBuffer[1], data, sysexLength);
					data += sysexLength;
					midiEventList.newMidiEvent().assignSysex(time, sysexBuffer, sysexLength + 1);
					continue;
				} else if (*data == 0xF7) {
					qDebug() << "MidiParser: Fragmented sysex, unsupported";
					data++;
					quint32 len = parseVarLenInt(data);
					data += len;
				} else if (*(data++) == 0xFF) {
					uint metaType = *(data++);
					quint32 len = parseVarLenInt(data);
					if (metaType == 0x2F) {
						qDebug() << "MidiParser: End-of-track Meta-event";
						runningStatus = 0x2F;
						break;
					} else if (metaType == 0x51) {
						uint newTempo = qFromBigEndian<quint32>(data) >> 8;
						midiEventList.newMidiEvent().assignSetTempoMessage(time, newTempo);
						qDebug() << "MidiParser: Meta-event: Set tempo:" << newTempo;
						data += len;
						continue;
					} else {
						qDebug() << "MidiParser: Meta-event code" << metaType << "unsupported";
					}
					data += len;
				} else {
					qDebug() << "MidiParser: Unsupported event" << *(data++);
				}
				if (time > 0) {
					// The event is unsupported. Nevertheless, assign a special marker event to retain timing information
					qDebug() << "MidiParser: Adding sync event for" << time << "divisions";
					midiEventList.newMidiEvent().assignSyncMessage(time);
				}
				continue;
			} else if ((*data & 0xE0) == 0xC0) {
				// It's a short message with one data byte
				message = qFromLittleEndian<quint16>(data);
				data += 2;
			} else {
				// It's a short message with two data bytes
				message = qFromLittleEndian<quint32>(data) & 0xFFFFFF;
				data += 3;
			}
			runningStatus = message & 0xFF;
		} else {
			// Handle running status
			if ((runningStatus & 0x80) == 0) {
				qDebug() << "MidiParser: First MIDI event must has status byte";
				data++;
				continue;
			}
			if ((runningStatus & 0xE0) == 0xC0) {
				// It's a short message with one data byte
				message = runningStatus | ((quint32)*data << 8);
				data++;
			} else {
				// It's a short message with two data bytes
				message = runningStatus | ((quint32)qFromLittleEndian<quint16>(data) << 8);
				data += 2;
			}
		}
		midiEventList.newMidiEvent().assignShortMessage(time, message);
	}
	if (runningStatus != 0x2F) {
		qDebug() << "MidiParser: End-of-track Meta-event isn't the last event, file is probably corrupted.";
	}
	delete trackData;
	qDebug() << "MidiParser: Parsed" << midiEventList.count() << "MIDI events";
	return true;
}
示例#3
0
void AudioFileWriter::run() {
	QFile file(outFileName);
	bool waveMode = false;
	if (outFileName.endsWith(".wav")) waveMode = true;
	if (!file.open(QIODevice::WriteOnly)) {
		qDebug() << "AudioFileWriter: Can't open file for writing:" << outFileName;
		synth->close();
		return;
	}
	if (waveMode) file.seek(44);
	MasterClockNanos startNanos = MasterClock::getClockNanos();
	MasterClockNanos firstSampleNanos = 0;
	MasterClockNanos midiTick = 0;
	MasterClockNanos midiNanos = 0;
	MidiEventList midiEvents;
	int midiEventIx = 0;
	uint parserIx = 0;
	if (realtimeMode) {
		firstSampleNanos = startNanos;
	} else {
		midiEvents = parsers[parserIx].getMIDIEvents();
		midiTick = parsers[parserIx].getMidiTick();
	}
	qDebug() << "AudioFileWriter: Rendering started";
	while (!stopProcessing) {
		unsigned int frameCount = 0;
		if (realtimeMode) {
			frameCount = sampleRate * (MasterClock::getClockNanos() - firstSampleNanos) / MasterClock::NANOS_PER_SECOND;
			if (frameCount < bufferSize) {
				usleep(1000000 * (bufferSize - frameCount) / sampleRate);
				continue;
			} else {
				frameCount = bufferSize;
			}
		} else {
			while (midiEventIx < midiEvents.count()) {
				const MidiEvent &e = midiEvents.at(midiEventIx);
				bool eventPushed = true;
				MasterClockNanos nextEventNanos = midiNanos + e.getTimestamp() * midiTick;
				frameCount = (uint)(sampleRate * (nextEventNanos - firstSampleNanos) / MasterClock::NANOS_PER_SECOND);
				if (bufferSize < frameCount) {
					frameCount = bufferSize;
					break;
				}
				switch (e.getType()) {
					case SHORT_MESSAGE:
						eventPushed = synth->pushMIDIShortMessage(e.getShortMessage(), nextEventNanos);
						break;
					case SYSEX:
						eventPushed = synth->pushMIDISysex(e.getSysexData(), e.getSysexLen(), nextEventNanos);
						break;
					case SET_TEMPO:
						midiTick = parsers[parserIx].getMidiTick(e.getShortMessage());
						break;
					default:
						break;
				}
				if (!eventPushed) {
					qDebug() << "AudioFileWriter: MIDI buffer overflow, midiNanos:" << midiNanos;
					break;
				}
				midiNanos = nextEventNanos;
				midiEventIx++;
				emit midiEventProcessed(midiEventIx, midiEvents.count());
			}
			if (midiEvents.count() <= midiEventIx) {
				if (parserIx < parsersCount - 1) {
					++parserIx;
					midiEventIx = 0;
					midiEvents = parsers[parserIx].getMIDIEvents();
					midiTick = parsers[parserIx].getMidiTick();
					continue;
				}
				if (!synth->isActive()) break;
				frameCount += bufferSize;
				qDebug() << "AudioFileWriter: Rendering after the end of MIDI file, time:" << (double)midiNanos / MasterClock::NANOS_PER_SECOND;
			}
		}
		while (frameCount > 0) {
			unsigned int framesToRender = qMin(bufferSize, frameCount);
			unsigned int framesRendered = synth->render(buffer, framesToRender, firstSampleNanos - latency, sampleRate);
			qint64 bytesToWrite = framesRendered * FRAME_SIZE;
			char *bufferPos = (char *)buffer;
			while (bytesToWrite > 0) {
				qint64 bytesWritten = file.write(bufferPos, bytesToWrite);
				if (bytesWritten == -1) {
					qDebug() << "AudioFileWriter: error writing into the audio file:" << file.errorString();
					file.close();
					return;
				}
				bytesToWrite -= bytesWritten;
				bufferPos += bytesWritten;
			}
			firstSampleNanos += MasterClock::NANOS_PER_SECOND * framesRendered / sampleRate;
			frameCount -= framesRendered;
			if (!realtimeMode) qDebug() << "AudioFileWriter: Rendering time:" << (double)firstSampleNanos / MasterClock::NANOS_PER_SECOND;
		}
	}
	qDebug() << "AudioFileWriter: Rendering finished";
	if (!realtimeMode) qDebug() << "AudioFileWriter: Elapsed seconds: " << 1e-9 * (MasterClock::getClockNanos() - startNanos);
	if (waveMode) {
		unsigned char *charBuffer = (unsigned char *)buffer;
		memcpy(charBuffer, WAVE_HEADER, 44);
		quint32 fileSize = (quint32)file.size();
		qToLittleEndian(fileSize - 8, charBuffer + 4);
		qToLittleEndian(fileSize - 44, charBuffer + 40);
		qToLittleEndian(sampleRate, charBuffer + 24);
		qToLittleEndian(sampleRate * FRAME_SIZE, charBuffer + 28);
		file.seek(0);
		file.write((char *)charBuffer, 44);
	}
	file.close();
	synth->close();
	if (!stopProcessing) emit conversionFinished();
}