コード例 #1
0
ファイル: SMFDriver.cpp プロジェクト: albundy122/munt
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;
}
コード例 #2
0
ファイル: SMFDriver.cpp プロジェクト: albundy122/munt
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);
}
コード例 #3
0
ファイル: midi_in.cpp プロジェクト: EQ4/linthesia
MidiEventList MidiIn::readAllNotes() {
    MidiEventList allNotes;
    //TODO: should be possible to transform it into
    // a do while
    MidiEvent event = read();
    while(
        event.get_type() == MidiEventType_NoteOff ||
        event.get_type() == MidiEventType_NoteOn
    ) {
        allNotes.push_back(event);
        event = read();
    }
    return allNotes;
}
コード例 #4
0
ファイル: MidiTrack.cpp プロジェクト: johndpope/pianogame
MidiEventList MidiTrack::Update(microseconds_t delta_microseconds)
{
   m_running_microseconds += delta_microseconds;

   MidiEventList evs;
   for (size_t i = m_last_event + 1; i < m_events.size(); ++i)
   {
      if (m_event_usecs[i] <= m_running_microseconds)
      {
         evs.push_back(m_events[i]);
         m_last_event = static_cast<long>(i);

         if (m_events[i].Type() == MidiEventType_NoteOn &&
            m_events[i].NoteVelocity() > 0) m_notes_remaining--;
      }
      else break;
   }

   return evs;
}
コード例 #5
0
void MidiPlayer::run()
{
    // Workaround to fix errors with the Microsoft GS Wavetable Synth on
    // Windows 10 - see http://stackoverflow.com/a/32553208/586978
#ifdef _WIN32
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    BOOST_SCOPE_EXIT(this_) {
        CoUninitialize();
    } BOOST_SCOPE_EXIT_END
#endif

    boost::signals2::scoped_connection connection(
        mySettingsManager.subscribeToChanges([&]() {
            auto settings = mySettingsManager.getReadHandle();
            myMetronomeEnabled = settings->get(Settings::MetronomeEnabled);
        }));

    setIsPlaying(true);

    MidiFile::LoadOptions options;
    options.myEnableMetronome = true;
    options.myRecordPositionChanges = true;

    // Load MIDI settings.
    int api;
    int port;
    {
        auto settings = mySettingsManager.getReadHandle();
        myMetronomeEnabled = settings->get(Settings::MetronomeEnabled);

        api = settings->get(Settings::MidiApi);
        port = settings->get(Settings::MidiPort);

        options.myMetronomePreset = settings->get(Settings::MetronomePreset) +
                                    Midi::MIDI_PERCUSSION_PRESET_OFFSET;
        options.myStrongAccentVel =
            settings->get(Settings::MetronomeStrongAccent);
        options.myWeakAccentVel = settings->get(Settings::MetronomeWeakAccent);
        options.myVibratoStrength = settings->get(Settings::MidiVibratoLevel);
        options.myWideVibratoStrength =
            settings->get(Settings::MidiWideVibratoLevel);
    }

    MidiFile file;
    file.load(myScore, options);

    const int ticks_per_beat = file.getTicksPerBeat();

    // Merge the MIDI evvents for each track.
    MidiEventList events;
    for (MidiEventList &track : file.getTracks())
    {
        track.convertToAbsoluteTicks();
        events.concat(track);
    }

    // TODO - since each track is already sorted, an n-way merge should be faster.
    std::stable_sort(events.begin(), events.end());
    events.convertToDeltaTicks();

    // Initialize RtMidi and set the port.
    MidiOutputDevice device;
    if (!device.initialize(api, port))
    {
        emit error(tr("Error initializing MIDI output device."));
        return;
    }

    bool started = false;
    int beat_duration = Midi::BEAT_DURATION_120_BPM;
    const SystemLocation start_location(myStartLocation.getSystemIndex(),
                                        myStartLocation.getPositionIndex());
    SystemLocation current_location = start_location;

    for (auto event = events.begin(); event != events.end(); ++event)
    {
        if (!isPlaying())
            break;

        if (event->isTempoChange())
            beat_duration = event->getTempo();

        // Skip events before the start location, except for events such as
        // instrument changes. Tempo changes are tracked above.
        if (!started)
        {
            if (event->getLocation() < start_location)
            {
                if (event->isProgramChange())
                    device.sendMessage(event->getData());

                continue;
            }
            else
            {
                performCountIn(device, event->getLocation(), beat_duration);

                started = true;
            }
        }

        const int delta = event->getTicks();
        assert(delta >= 0);

        const int duration_us = boost::rational_cast<int>(
            boost::rational<int>(delta, ticks_per_beat) * beat_duration);

        usleep(duration_us * (100.0 / myPlaybackSpeed));

        // Don't play metronome events if the metronome is disabled.
        if (event->isNoteOnOff() && event->getChannel() == METRONOME_CHANNEL &&
            !myMetronomeEnabled)
        {
            continue;
        }

        device.sendMessage(event->getData());

        // Notify listeners of the current playback position.
        if (event->getLocation() != current_location)
        {
            const SystemLocation &new_location = event->getLocation();

            // Don't move backwards unless a repeat occurred.
            if (new_location < current_location && !event->isPositionChange())
                    continue;

            if (new_location.getSystem() != current_location.getSystem())
                emit playbackSystemChanged(new_location.getSystem());

            emit playbackPositionChanged(new_location.getPosition());

            current_location = new_location;
        }
    }
}
コード例 #6
0
ファイル: MidiParser.cpp プロジェクト: belaamg/munt
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;
}
コード例 #7
0
ファイル: AudioFileWriter.cpp プロジェクト: albundy122/munt
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();
}