int SMFProcessor::seek(SynthRoute *synthRoute, const MidiEventList &midiEvents, int currentEventIx, MasterClockNanos seekNanos, MasterClockNanos currentEventNanos) { MasterClockNanos nanosNow = MasterClock::getClockNanos(); while (!stopProcessing && currentEventNanos < seekNanos && currentEventIx < midiEvents.size()) { const MidiEvent &e = midiEvents.at(currentEventIx); while (!stopProcessing && synthRoute->getState() == SynthRouteState_OPEN) { bool res = true; switch (e.getType()) { case SHORT_MESSAGE: { quint32 msg = e.getShortMessage(); if ((msg & 0xE0) != 0x80) res = synthRoute->pushMIDIShortMessage(msg, nanosNow); break; } case SYSEX: res = synthRoute->pushMIDISysex(e.getSysexData(), e.getSysexLen(), nanosNow); break; case SET_TEMPO: { uint tempo = e.getShortMessage(); midiTick = parser.getMidiTick(tempo); emit driver->tempoUpdated(MidiParser::MICROSECONDS_PER_MINUTE / tempo); break; } default: break; } if (res) break; qDebug() << "SMFProcessor: MIDI buffer became full while seeking, taking a nap"; usleep(MAX_SLEEP_TIME / MasterClock::NANOS_PER_MICROSECOND); nanosNow = MasterClock::getClockNanos(); } currentEventIx++; currentEventNanos += e.getTimestamp() * midiTick; } return currentEventIx; }
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); }
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(); }