diff options
| author | Sandro S. Andrade <sandroandrade@kde.org> | 2016-07-22 03:47:26 (GMT) |
|---|---|---|
| committer | Sandro S. Andrade <sandroandrade@kde.org> | 2016-07-22 03:47:26 (GMT) |
| commit | 751f969eb23f2c086290530e5ba3351a63ef88c7 (patch) | |
| tree | 25f2e4c67a9cdf0722c1ad0f7d682dc759d964c3 | |
| parent | 19b0de32d34c68966389ff887c81c7399d5b5ef7 (diff) | |
Improve fluidsynthsoundbackend
Remove old drumstick-based soundbackend
| -rw-r--r-- | README | 2 | ||||
| -rw-r--r-- | README.packagers | 3 | ||||
| -rw-r--r-- | src/app/qml/Main.qml | 2 | ||||
| -rw-r--r-- | src/interfaces/isoundbackend.cpp | 24 | ||||
| -rw-r--r-- | src/interfaces/isoundbackend.h | 22 | ||||
| -rw-r--r-- | src/plugins/CMakeLists.txt | 29 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/CMakeLists.txt | 20 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/drumsticksoundbackend.cpp | 339 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/drumsticksoundbackend.h | 93 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/drumsticksoundbackend.json | 12 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/midisequenceroutputthread.cpp | 182 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/midisequenceroutputthread.h | 72 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/song.cpp | 81 | ||||
| -rw-r--r-- | src/plugins/drumsticksoundbackend/song.h | 60 | ||||
| -rw-r--r-- | src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.cpp | 142 | ||||
| -rw-r--r-- | src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.h | 19 |
16 files changed, 152 insertions, 950 deletions
@@ -8,7 +8,7 @@ Minuet features a rich set of ear training's exercises and new ones can be seam # How to build Minuet - install KF5 and Drumstick + install KF5 and FluidSynth $ git clone git://anongit.kde.org/minuet $ cd minuet $ mkdir build diff --git a/README.packagers b/README.packagers index 8dafde7..c33bcf8 100644 --- a/README.packagers +++ b/README.packagers @@ -1,6 +1,5 @@ Please add: -- TiMidity++ (http://timidity.sourceforge.net/) -- Freepats (http://freepats.zenvoid.org/) +- FluidSynth (http://fluidsynth.elementsofsound.org/) as Minuet's installation dependencies diff --git a/src/app/qml/Main.qml b/src/app/qml/Main.qml index 1f9833b..1aa2765 100644 --- a/src/app/qml/Main.qml +++ b/src/app/qml/Main.qml @@ -42,7 +42,7 @@ Item { anchors { left: parent.left; top: parent.top } onBackPressed: { - core.soundBackend.stop() + core.soundBackend.reset() exerciseView.clearExerciseGrid() pianoView.clearAllMarks() } diff --git a/src/interfaces/isoundbackend.cpp b/src/interfaces/isoundbackend.cpp index 3830b01..c28d613 100644 --- a/src/interfaces/isoundbackend.cpp +++ b/src/interfaces/isoundbackend.cpp @@ -28,20 +28,18 @@ namespace Minuet { ISoundBackend::ISoundBackend(QObject *parent) - : IPlugin(parent), - m_state(StoppedState) + : IPlugin(parent) { + qRegisterMetaType<State>("State"); qmlRegisterInterface<ISoundBackend>("ISoundBackend"); qmlRegisterUncreatableType<ISoundBackend>("org.kde.minuet.isoundbackend", 1, 0, "ISoundBackend", "ISoundBackend cannot be instantiated"); -} -ISoundBackend::~ISoundBackend() -{ + setPlaybackLabel(QStringLiteral("00:00.00")); + setState(StoppedState); } -QString ISoundBackend::playbackLabel() const +ISoundBackend::~ISoundBackend() { - return m_playbackLabel; } ISoundBackend::State ISoundBackend::state() const @@ -49,17 +47,9 @@ ISoundBackend::State ISoundBackend::state() const return m_state; } -QString ISoundBackend::playMode() const -{ - return m_playMode; -} - -void ISoundBackend::setPlayMode(QString playMode) +QString ISoundBackend::playbackLabel() const { - if (m_playMode != playMode) { - m_playMode = playMode; - emit playModeChanged(m_playMode); - } + return m_playbackLabel; } void ISoundBackend::setPlaybackLabel(const QString &playbackLabel) diff --git a/src/interfaces/isoundbackend.h b/src/interfaces/isoundbackend.h index c581a21..efbacb7 100644 --- a/src/interfaces/isoundbackend.h +++ b/src/interfaces/isoundbackend.h @@ -36,33 +36,35 @@ class MINUETINTERFACES_EXPORT ISoundBackend : public IPlugin { Q_OBJECT + // Read-write properties with simple mutators + Q_PROPERTY(QString playMode MEMBER m_playMode NOTIFY playModeChanged) + + // Read-write properties with custom mutators Q_PROPERTY(qint8 pitch MEMBER m_pitch WRITE setPitch NOTIFY pitchChanged) Q_PROPERTY(quint8 volume MEMBER m_volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(quint8 tempo MEMBER m_tempo WRITE setTempo NOTIFY tempoChanged) - Q_PROPERTY(QString playbackLabel READ playbackLabel NOTIFY playbackLabelChanged) + + // Read-only properties Q_ENUMS(State) Q_PROPERTY(State state READ state NOTIFY stateChanged) - Q_PROPERTY(QString playMode READ playMode WRITE setPlayMode NOTIFY playModeChanged) + Q_PROPERTY(QString playbackLabel READ playbackLabel NOTIFY playbackLabelChanged) public: ~ISoundBackend() override; - QString playbackLabel() const; - enum State { StoppedState = 0, PlayingState, PausedState }; - ISoundBackend::State state() const; - QString playMode() const; + + QString playbackLabel() const; public Q_SLOTS: virtual void setPitch(qint8 pitch) = 0; virtual void setVolume(quint8 volume) = 0; virtual void setTempo(quint8 tempo) = 0; - void setPlayMode(QString playMode); virtual void prepareFromExerciseOptions(QJsonArray selectedExerciseOptions) = 0; virtual void prepareFromMidiFile(const QString &fileName) = 0; @@ -70,17 +72,19 @@ public Q_SLOTS: virtual void play() = 0; virtual void pause() = 0; virtual void stop() = 0; + virtual void reset() = 0; Q_SIGNALS: + void playModeChanged(QString newPlayMode); void pitchChanged(qint8 newPitch); void volumeChanged(quint8 newVolume); void tempoChanged(quint8 newTempo); - void playbackLabelChanged(QString newPlaybackLabel); void stateChanged(State newState); - void playModeChanged(QString newPlayMode); + void playbackLabelChanged(QString newPlaybackLabel); protected: explicit ISoundBackend(QObject *parent = 0); + void setPlaybackLabel(const QString &playbackLabel); void setState(State state); diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 86f70f5..0a5cb3c 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -5,35 +5,6 @@ else() message(FATAL_ERROR "Program pkg-config not found") endif() -#pkg_check_modules(ALSA REQUIRED alsa>=1.0.0) -#if(ALSA_FOUND) -# link_directories(${ALSA_LIBDIR}) -# include_directories(${ALSA_INCLUDEDIR}) -#else() -# message(FATAL_ERROR "Please install ALSA library and headers (libasound2)") -#endif() - -#pkg_check_modules(DRUMSTICK_ALSA REQUIRED drumstick-alsa>=1.0.1) -#pkg_check_modules(DRUMSTICK_FILE REQUIRED drumstick-file>=1.0.1) - -#if(DRUMSTICK_ALSA_FOUND) -# link_directories(${DRUMSTICK_ALSA_LIBDIR}) -# include_directories(${DRUMSTICK_ALSA_INCLUDEDIR}) -#endif() - -#if(DRUMSTICK_FILE_FOUND) -# link_directories(${DRUMSTICK_FILE_LIBDIR}) -# include_directories(${DRUMSTICK_FILE_INCLUDEDIR}) -#endif() - -#if(NOT DRUMSTICK_ALSA_FOUND OR NOT DRUMSTICK_FILE_FOUND) -# message(WARNING "Please install drumstick libraries and headers (package drumstick-devel) if you want to build drumstick soundbackend") -#endif() - -#if(ALSA_FOUND AND DRUMSTICK_ALSA_FOUND AND DRUMSTICK_FILE_FOUND) -# add_subdirectory(drumsticksoundbackend) -#endif() - pkg_check_modules(FLUIDSYNTH REQUIRED fluidsynth>=1.1.6) if(NOT FLUIDSYNTH_FOUND) diff --git a/src/plugins/drumsticksoundbackend/CMakeLists.txt b/src/plugins/drumsticksoundbackend/CMakeLists.txt deleted file mode 100644 index 56e69c5..0000000 --- a/src/plugins/drumsticksoundbackend/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -kde_enable_exceptions() - -set(drumsticksoundbackend_PLUGIN_SRCS - drumsticksoundbackend.cpp - midisequenceroutputthread.cpp - song.cpp -) - -add_library(minuetdrumsticksoundbackend MODULE ${drumsticksoundbackend_PLUGIN_SRCS}) - -target_link_libraries(minuetdrumsticksoundbackend - Qt5::Core - asound - drumstick-alsa - drumstick-file - Minuet::Interfaces - KF5::CoreAddons -) - -install(TARGETS minuetdrumsticksoundbackend DESTINATION ${PLUGIN_INSTALL_DIR}/minuet/) diff --git a/src/plugins/drumsticksoundbackend/drumsticksoundbackend.cpp b/src/plugins/drumsticksoundbackend/drumsticksoundbackend.cpp deleted file mode 100644 index 077c030..0000000 --- a/src/plugins/drumsticksoundbackend/drumsticksoundbackend.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 by Sandro S. Andrade <sandroandrade@kde.org> -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public License as -** published by the Free Software Foundation; either version 2 of -** the License or (at your option) version 3 or any later version -** accepted by the membership of KDE e.V. (or its successor approved -** by the membership of KDE e.V.), which shall act as a proxy -** defined in Section 14 of version 3 of the license. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see <http://www.gnu.org/licenses/>. -** -****************************************************************************/ - -#include "drumsticksoundbackend.h" - -#include "song.h" -#include "midisequenceroutputthread.h" - -#include <drumstick/alsaqueue.h> -#include <drumstick/alsaclient.h> - -#include <QTime> -#include <QtMath> -#include <QJsonObject> - -DrumstickSoundBackend::DrumstickSoundBackend(QObject *parent) - : Minuet::ISoundBackend(parent), - m_song(0) -{ - // MidiClient configuration - m_client = new drumstick::MidiClient(this); - try { - m_client->open(); - } catch (const drumstick::SequencerError &err) { -// KMessageBox::error(qobject_cast<QWidget*>(this->parent()), i18n("Fatal error from the ALSA sequencer: \"%1\". " -// "This usually happens when the kernel doesn't have ALSA support, " -// "or the device node (/dev/snd/seq) doesn't exists, " -// "or the kernel module (snd_seq) is not loaded, " -// "or the user isn't a member of audio group. " -// "Please check your ALSA/MIDI configuration." -// , err.qstrError()), -// i18n("Minuet startup")); -// m_eventSchedulingMode = DAMAGED; - return; - } - m_client->setClientName(QStringLiteral("MinuetSequencer")); - m_client->setPoolOutput(50); - - // Connection for events generated when playing MIDI - connect(m_client, &drumstick::MidiClient::eventReceived, this, &DrumstickSoundBackend::eventReceived, Qt::QueuedConnection); - m_client->setRealTimeInput(false); - m_client->startSequencerInput(); - - // Output port configuration (to TiMidity) - m_outputPort = new drumstick::MidiPort(this); - m_outputPort->attach(m_client); - m_outputPort->setPortName(QStringLiteral("Minuet Sequencer Output Port")); - m_outputPort->setCapability(SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ); - m_outputPort->setPortType(SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC); - m_outputPortId = m_outputPort->getPortId(); - - // Input port configuration (from ALSA) - m_inputPort = new drumstick::MidiPort(this); - m_inputPort->attach(m_client); - m_inputPort->setPortName(QStringLiteral("Minuet Sequencer Input Port")); - m_inputPort->setCapability(SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE); - m_inputPort->setPortType(SND_SEQ_PORT_TYPE_APPLICATION); - m_inputPortId = m_inputPort->getPortId(); - - // MidiQueue configuration - m_queue = m_client->createQueue(); - m_queueId = m_queue->getId(); - - // OutputThread - m_midiSequencerOutputThread = new MidiSequencerOutputThread(m_client, m_outputPortId); - connect(m_midiSequencerOutputThread, &MidiSequencerOutputThread::stopped, this, &DrumstickSoundBackend::outputThreadStopped); - connect(m_midiSequencerOutputThread, &MidiSequencerOutputThread::finished, this, [=]() { - setPlaybackLabel(QStringLiteral("00:00.00")); - setState(StoppedState); - }); - - // Subscribe to Minuet's virtual piano - try { - m_outputPort->subscribeTo(QStringLiteral("MinuetSequencer:1")); - } catch (const drumstick::SequencerError &err) { - qCDebug(MINUET) << "Subscribe error"; - throw err; - } - setPlaybackLabel(QStringLiteral("00:00.00")); - - startTimidity(); - m_outputPort->subscribeTo("TiMidity:0"); -} - -DrumstickSoundBackend::~DrumstickSoundBackend() -{ - m_client->stopSequencerInput(); - m_outputPort->detach(); - m_inputPort->detach(); - m_client->close(); - delete m_midiSequencerOutputThread; - m_timidityProcess.kill(); - qCDebug(MINUET) << "Stoping TiMidity++!"; - if (!m_timidityProcess.waitForFinished(-1)) - qCDebug(MINUET) << "Error when stoping TiMidity++:" << m_timidityProcess.errorString(); - else - qCDebug(MINUET) << "TiMidity++ stoped!"; -} - -void DrumstickSoundBackend::setPitch(qint8 pitch) -{ - if (m_midiSequencerOutputThread->pitch() != pitch) { - m_midiSequencerOutputThread->setPitch(pitch); - emit pitchChanged(pitch); - } -} - -void DrumstickSoundBackend::setVolume(quint8 volume) -{ - if (m_midiSequencerOutputThread->volume() != volume) { - m_midiSequencerOutputThread->setVolume(volume); - emit volumeChanged(volume); - } -} - -void DrumstickSoundBackend::setTempo (quint8 tempo) -{ - float tempoFactor = (tempo*tempo + 100.0*tempo + 20000.0) / 40000.0; - m_midiSequencerOutputThread->setTempoFactor(tempoFactor); - - drumstick::QueueTempo queueTempo = m_queue->getTempo(); - queueTempo.setTempoFactor(tempoFactor); - m_queue->setTempo(queueTempo); - m_client->drainOutput(); - - m_tempo = tempo; - emit tempoChanged(m_tempo); -} - -void DrumstickSoundBackend::prepareFromExerciseOptions(QJsonArray selectedExerciseOptions) -{ - Song *song = new Song; - song->setHeader(0, 1, 60); - song->setInitialTempo(600000); - m_song.reset(song); - - if (m_song->initialTempo() == 0) - m_song->setInitialTempo(600000); - appendEvent(new drumstick::TempoEvent(m_queueId, 600000), 0); - - unsigned int barStart = 0; - if (m_playMode == "rhythm") { - appendEvent(new drumstick::NoteOnEvent(9, 80, 120), 0); - appendEvent(new drumstick::NoteOnEvent(9, 80, 120), 60); - appendEvent(new drumstick::NoteOnEvent(9, 80, 120), 120); - appendEvent(new drumstick::NoteOnEvent(9, 80, 120), 180); - barStart = 240; - } - - for (int i = 0; i < selectedExerciseOptions.size(); ++i) { - QString sequence = selectedExerciseOptions[i].toObject()[QStringLiteral("sequence")].toString(); - - unsigned int chosenRootNote = selectedExerciseOptions[i].toObject()[QStringLiteral("rootNote")].toString().toInt(); - if (m_playMode != "rhythm") { - appendEvent(new drumstick::NoteOnEvent(1, chosenRootNote, 120), barStart); - appendEvent(new drumstick::NoteOffEvent(1, chosenRootNote, 120), barStart + 60); - - unsigned int j = 1; - drumstick::SequencerEvent *ev; - foreach(const QString &additionalNote, sequence.split(' ')) { - appendEvent(ev = new drumstick::NoteOnEvent(1, chosenRootNote + additionalNote.toInt(), 120), - (m_playMode == "scale") ? barStart+60*j:barStart); - ev->setTag(0); - appendEvent(ev = new drumstick::NoteOffEvent(1, chosenRootNote + additionalNote.toInt(), 120), - (m_playMode == "scale") ? barStart+60*(j+1):barStart+60); - ev->setTag(0); - ++j; - } - barStart += 60; - } - else { - appendEvent(new drumstick::NoteOnEvent(9, 80, 120), barStart); - foreach(QString additionalNote, sequence.split(' ')) { // krazy:exclude=foreach - appendEvent(new drumstick::NoteOnEvent(9, 37, 120), barStart); - float dotted = 1; - if (additionalNote.endsWith('.')) { - dotted = 1.5; - additionalNote.chop(1); - } - barStart += dotted*60*(4.0/additionalNote.toInt()); - } - } - } - if (m_playMode == "rhythm") - appendEvent(new drumstick::NoteOnEvent(9, 80, 120), barStart); -} - -void DrumstickSoundBackend::prepareFromMidiFile(const QString &fileName) -{ - Q_UNUSED(fileName) -} - -void DrumstickSoundBackend::play() -{ - if (m_song && !m_song->isEmpty() && !m_midiSequencerOutputThread->isRunning()) { - if(m_midiSequencerOutputThread->getInitialPosition() == 0 || !m_midiSequencerOutputThread->hasNext()) - m_midiSequencerOutputThread->setSong(m_song.data()); - m_midiSequencerOutputThread->start(); - setState(PlayingState); - } -} - -void DrumstickSoundBackend::pause() -{ - if (m_midiSequencerOutputThread->isRunning()) { - m_midiSequencerOutputThread->stop(); - m_midiSequencerOutputThread->setPosition(m_queue->getStatus().getTickTime()); - setState(PausedState); - } -} - -void DrumstickSoundBackend::stop() -{ - m_midiSequencerOutputThread->stop(); - m_midiSequencerOutputThread->resetPosition(); - setPlaybackLabel(QStringLiteral("00:00.00")); - setState(StoppedState); -} - -void DrumstickSoundBackend::eventReceived(drumstick::SequencerEvent *ev) -{ - static QChar fill('0'); - drumstick::KeyEvent *kev; - if (!(kev = static_cast<drumstick::KeyEvent*>(ev))) - return; -// if (kev->getSequencerType() == SND_SEQ_EVENT_NOTEON && kev->getTag() == 1) -// emit noteOn(kev->getChannel(), kev->getKey(), kev->getVelocity()); -// if (kev->getSequencerType() == SND_SEQ_EVENT_NOTEOFF && kev->getTag() == 1) -// emit noteOff(kev->getChannel(), kev->getKey(), kev->getVelocity()); - - if (m_tick != 0 && m_midiSequencerOutputThread->isRunning()) { - const snd_seq_real_time_t *rt = m_queue->getStatus().getRealtime(); - int mins = rt->tv_sec / 60; - int secs = rt->tv_sec % 60; - int cnts = qFloor( rt->tv_nsec / 1.0e7 ); - setPlaybackLabel(QStringLiteral("%1:%2.%3").arg(mins,2,10,fill).arg(secs,2,10,fill).arg(cnts,2,10,fill)); - } -} - -void DrumstickSoundBackend::outputThreadStopped() -{ - for (int channel = 0; channel < 16; ++channel) { - drumstick::ControllerEvent ev1(channel, MIDI_CTL_ALL_NOTES_OFF, 0); - ev1.setSource(m_outputPortId); - ev1.setSubscribers(); - ev1.setDirect(); - m_client->outputDirect(&ev1); - drumstick::ControllerEvent ev2(channel, MIDI_CTL_ALL_SOUNDS_OFF, 0); - ev2.setSource(m_outputPortId); - ev2.setSubscribers(); - ev2.setDirect(); - m_client->outputDirect(&ev2); - } - m_client->drainOutput(); -} - -void DrumstickSoundBackend::appendEvent(drumstick::SequencerEvent *ev, unsigned long tick) -{ - ev->setSource(m_outputPortId); - if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) - ev->setSubscribers(); - ev->scheduleTick(m_queueId, tick, false); - ev->setTag(1); - m_song->append(ev); - if (tick > m_tick) - m_tick = tick; -} - -void DrumstickSoundBackend::startTimidity() -{ - QString error; - if (!availableOutputPorts().contains(QStringLiteral("TiMidity:0"))) { -// qCDebug(MINUET) << "Starting TiMidity++ at" << MinuetSettings::timidityLocation().remove(QStringLiteral("file://")); -// m_timidityProcess.setProgram(MinuetSettings::timidityLocation().remove(QStringLiteral("file://")), QStringList() << MinuetSettings::timidityParameters()); - m_timidityProcess.setProgram("/usr/bin/timidity", QStringList() << "-iA"); - m_timidityProcess.start(); - if (!m_timidityProcess.waitForStarted(-1)) { - error = m_timidityProcess.errorString(); - } - else { - if (!waitForTimidityOutputPorts(3000)) -// error = i18n("error when waiting for TiMidity++ output ports!"); - error = "error when waiting for TiMidity++ output ports!"; - else - qCDebug(MINUET) << "TiMidity++ started!"; - } - } - else { - qCDebug(MINUET) << "TiMidity++ already running!"; - } -// if (!error.isEmpty()) -// KMessageBox::error(this, -// i18n("There was an error when starting TiMidity++: \"%1\". " -// "Is another application using the audio system? " -// "Also, please check Minuet settings!", error), -// i18n("Minuet startup")); -} - -bool DrumstickSoundBackend::waitForTimidityOutputPorts(int msecs) -{ - QTime time; - time.start(); - while (!availableOutputPorts().contains(QStringLiteral("TiMidity:0"))) - if (msecs != -1 && time.elapsed() > msecs) - return false; - return true; -} - -QStringList DrumstickSoundBackend::availableOutputPorts() const -{ - QStringList availableOutputPorts; - QListIterator<drumstick::PortInfo> it(m_client->getAvailableOutputs()); - while(it.hasNext()) { - drumstick::PortInfo p = it.next(); - availableOutputPorts << QStringLiteral("%1:%2").arg(p.getClientName()).arg(p.getPort()); - } - return availableOutputPorts; -} - -#include "moc_drumsticksoundbackend.cpp" diff --git a/src/plugins/drumsticksoundbackend/drumsticksoundbackend.h b/src/plugins/drumsticksoundbackend/drumsticksoundbackend.h deleted file mode 100644 index 01b7d4d..0000000 --- a/src/plugins/drumsticksoundbackend/drumsticksoundbackend.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 by Sandro S. Andrade <sandroandrade@kde.org> -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public License as -** published by the Free Software Foundation; either version 2 of -** the License or (at your option) version 3 or any later version -** accepted by the membership of KDE e.V. (or its successor approved -** by the membership of KDE e.V.), which shall act as a proxy -** defined in Section 14 of version 3 of the license. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see <http://www.gnu.org/licenses/>. -** -****************************************************************************/ - -#ifndef MINUET_DRUMSTICKSOUNDBACKEND_H -#define MINUET_DRUMSTICKSOUNDBACKEND_H - -#include <interfaces/isoundbackend.h> - -#include <KProcess> - -#include <QLoggingCategory> - -Q_DECLARE_LOGGING_CATEGORY(MINUET) -Q_LOGGING_CATEGORY(MINUET, "minuet") - -namespace drumstick { - class MidiClient; - class MidiPort; - class SequencerEvent; - class MidiQueue; -} - -class MidiSequencerOutputThread; -class Song; - -class DrumstickSoundBackend : public Minuet::ISoundBackend -{ - Q_OBJECT - - Q_PLUGIN_METADATA(IID "org.kde.minuet.IPlugin" FILE "drumsticksoundbackend.json") - Q_INTERFACES(Minuet::IPlugin) - Q_INTERFACES(Minuet::ISoundBackend) - -public: - explicit DrumstickSoundBackend(QObject *parent = 0); - virtual ~DrumstickSoundBackend() override; - -public Q_SLOTS: - virtual void setPitch(qint8 pitch); - virtual void setVolume(quint8 volume); - virtual void setTempo (quint8 tempo); - - virtual void prepareFromExerciseOptions(QJsonArray selectedExerciseOptions) override; - virtual void prepareFromMidiFile(const QString &fileName) override; - - virtual void play() override; - virtual void pause() override; - virtual void stop() override; - -private Q_SLOTS: - void eventReceived(drumstick::SequencerEvent *ev); - void outputThreadStopped(); - -private: - void appendEvent(drumstick::SequencerEvent *ev, unsigned long tick); - void startTimidity(); - bool waitForTimidityOutputPorts(int msecs); - QStringList availableOutputPorts() const; - - drumstick::MidiClient *m_client; - drumstick::MidiPort *m_outputPort; - int m_outputPortId; - drumstick::MidiPort *m_inputPort; - int m_inputPortId; - MidiSequencerOutputThread *m_midiSequencerOutputThread; - unsigned long m_tick; - drumstick::MidiQueue *m_queue; - int m_queueId; - QScopedPointer<Song> m_song; - - KProcess m_timidityProcess; -}; - -#endif diff --git a/src/plugins/drumsticksoundbackend/drumsticksoundbackend.json b/src/plugins/drumsticksoundbackend/drumsticksoundbackend.json deleted file mode 100644 index 4440171..0000000 --- a/src/plugins/drumsticksoundbackend/drumsticksoundbackend.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "KPlugin": { - "Category": "Sound Backends", - "Description": "This plugin provides Minuet with a Drumstick-based implementation of sound backend.", - "Icon": "utilities-terminal", - "Id": "minuetdrumsticksoundbackend", - "Name": "Drumstick-based Audio Backend", - "ServiceTypes": [ - "Minuet/Plugin" - ] - } -} diff --git a/src/plugins/drumsticksoundbackend/midisequenceroutputthread.cpp b/src/plugins/drumsticksoundbackend/midisequenceroutputthread.cpp deleted file mode 100644 index 8519a5b..0000000 --- a/src/plugins/drumsticksoundbackend/midisequenceroutputthread.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 by Sandro S. Andrade <sandroandrade@kde.org> -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public License as -** published by the Free Software Foundation; either version 2 of -** the License or (at your option) version 3 or any later version -** accepted by the membership of KDE e.V. (or its successor approved -** by the membership of KDE e.V.), which shall act as a proxy -** defined in Section 14 of version 3 of the license. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see <http://www.gnu.org/licenses/>. -** -****************************************************************************/ - -#include "midisequenceroutputthread.h" - -#include "song.h" - -#include <drumstick/alsaqueue.h> - -#include <cmath> - -MidiSequencerOutputThread::MidiSequencerOutputThread(drumstick::MidiClient *client, int portId) : - drumstick::SequencerOutputThread(client, portId), - m_client(client), - m_song(0), - m_songPosition(0), - m_lastEvent(0), - m_volume(100), - m_pitchShift(0), - m_tempoFactor(1.0), - m_songIterator(0) -{ - for (int chan = 0; chan < MIDI_CHANNELS; ++chan) - m_channelVolume[chan] = 100; -} - -MidiSequencerOutputThread::~MidiSequencerOutputThread() -{ - if (isRunning()) - stop(); - if (m_songIterator != 0) - delete m_songIterator; - if (m_lastEvent != 0) - delete m_lastEvent; -} - -bool MidiSequencerOutputThread::hasNext() -{ - return m_songIterator->hasNext(); -} - -drumstick::SequencerEvent *MidiSequencerOutputThread::nextEvent() -{ - if (m_lastEvent != 0) - delete m_lastEvent; - - m_lastEvent = m_songIterator->next()->clone(); - switch (m_lastEvent->getSequencerType()) { - case SND_SEQ_EVENT_NOTE: - case SND_SEQ_EVENT_NOTEON: - case SND_SEQ_EVENT_NOTEOFF: - case SND_SEQ_EVENT_KEYPRESS: { - drumstick::KeyEvent *kev = static_cast<drumstick::KeyEvent*>(m_lastEvent); - if (kev->getChannel() != MIDI_GM_DRUM_CHANNEL) - kev->setKey(kev->getKey() + m_pitchShift); - } - break; - case SND_SEQ_EVENT_CONTROLLER: { - drumstick::ControllerEvent *cev = static_cast<drumstick::ControllerEvent*>(m_lastEvent); - if (cev->getParam() == MIDI_CTL_MSB_MAIN_VOLUME) { - int chan = cev->getChannel(); - int value = cev->getValue(); - m_channelVolume[chan] = value; - value = floor(value * m_volume / 100.0); - if (value < 0) value = 0; - if (value > 127) value = 127; - cev->setValue(value); - } - } - break; - } - return m_lastEvent; -} - -void MidiSequencerOutputThread::setSong(Song *song) -{ - m_song = song; - if (m_songIterator) - delete m_songIterator; - m_songIterator = new QListIterator<drumstick::SequencerEvent *>(*song); - m_songPosition = 0; - drumstick::QueueTempo firstTempo = m_Queue->getTempo(); - firstTempo.setPPQ(m_song->division()); - firstTempo.setTempo(m_song->initialTempo()); - firstTempo.setTempoFactor(m_tempoFactor); - m_Queue->setTempo(firstTempo); -} - -void MidiSequencerOutputThread::setVolume(unsigned int volume) -{ - m_volume = volume; - for(int chan = 0; chan < MIDI_CHANNELS; ++chan) { - int value = m_channelVolume[chan]; - value = floor(value * m_volume / 100.0); - if (value < 0) value = 0; - if (value > 127) value = 127; - sendControllerEvent(chan, MIDI_CTL_MSB_MAIN_VOLUME, value); - } -} - -unsigned int MidiSequencerOutputThread::volume() const -{ - return m_volume; -} - -void MidiSequencerOutputThread::setPitch(qint8 value) -{ - bool playing = isRunning(); - if (playing) { - stop(); - unsigned int pos = m_Queue->getStatus().getTickTime(); - m_Queue->clear(); - mute(); - setPosition(pos); - } - m_pitchShift = value; - if (playing) - start(); -} - -qint8 MidiSequencerOutputThread::pitch() const -{ - return m_pitchShift; -} - -void MidiSequencerOutputThread::setTempoFactor(float value) -{ - m_tempoFactor = value; -} - -void MidiSequencerOutputThread::setPosition(unsigned int pos) -{ - m_songPosition = pos; - m_songIterator->toFront(); - while (m_songIterator->hasNext() && (m_songIterator->next()->getTick() < pos)) { }; - if (m_songIterator->hasPrevious()) - m_songIterator->previous(); -} - -void MidiSequencerOutputThread::resetPosition() -{ - if ((m_song != NULL) && (m_songIterator != NULL)) { - m_songIterator->toFront(); - m_songPosition = 0; - } -} - -void MidiSequencerOutputThread::mute() -{ - for(int chan = 0; chan < MIDI_CHANNELS; ++chan) { - sendControllerEvent(chan, MIDI_CTL_ALL_NOTES_OFF, 0); - sendControllerEvent(chan, MIDI_CTL_ALL_SOUNDS_OFF, 0); - } -} - -void MidiSequencerOutputThread::sendControllerEvent(int chan, int control, int value) -{ - drumstick::ControllerEvent ev(chan, control, value); - ev.setSource(m_PortId); - ev.setSubscribers(); - ev.setDirect(); - sendSongEvent(&ev); -} diff --git a/src/plugins/drumsticksoundbackend/midisequenceroutputthread.h b/src/plugins/drumsticksoundbackend/midisequenceroutputthread.h deleted file mode 100644 index 8632666..0000000 --- a/src/plugins/drumsticksoundbackend/midisequenceroutputthread.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 by Sandro S. Andrade <sandroandrade@kde.org> -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public License as -** published by the Free Software Foundation; either version 2 of -** the License or (at your option) version 3 or any later version -** accepted by the membership of KDE e.V. (or its successor approved -** by the membership of KDE e.V.), which shall act as a proxy -** defined in Section 14 of version 3 of the license. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see <http://www.gnu.org/licenses/>. -** -****************************************************************************/ - -#ifndef MIDISEQUENCEROUTPUTTHREAD_H -#define MIDISEQUENCEROUTPUTTHREAD_H - -#include <drumstick/playthread.h> - -namespace drumstick { - class MidiClient; -} - -class Song; - -class MidiSequencerOutputThread : public drumstick::SequencerOutputThread -{ - Q_OBJECT - -public: - MidiSequencerOutputThread(drumstick::MidiClient *client, int portId); - virtual ~MidiSequencerOutputThread(); - - // Virtual methods from drumstick::SequencerOutputThread - virtual bool hasNext(); - virtual drumstick::SequencerEvent *nextEvent(); - virtual unsigned int getInitialPosition() { return m_songPosition; } - - void setSong(Song *song); - void setPitch(qint8 value); - qint8 pitch() const; - void setVolume(unsigned int volume); - unsigned int volume() const; - void setPosition(unsigned int pos); - void setTempoFactor(float value); - void resetPosition(); - -private: - void mute(); - void sendControllerEvent(int chan, int control, int value); - -private: - drumstick::MidiClient *m_client; - Song *m_song; - unsigned int m_songPosition; - drumstick::SequencerEvent *m_lastEvent; - unsigned int m_volume; - int m_channelVolume[MIDI_CHANNELS]; - qint8 m_pitchShift; - float m_tempoFactor; - QListIterator<drumstick::SequencerEvent *>* m_songIterator; -}; - -#endif // MIDISEQUENCEROUTPUTTHREAD_H diff --git a/src/plugins/drumsticksoundbackend/song.cpp b/src/plugins/drumsticksoundbackend/song.cpp deleted file mode 100644 index 61d99ec..0000000 --- a/src/plugins/drumsticksoundbackend/song.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 by Sandro S. Andrade <sandroandrade@kde.org> -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public License as -** published by the Free Software Foundation; either version 2 of -** the License or (at your option) version 3 or any later version -** accepted by the membership of KDE e.V. (or its successor approved -** by the membership of KDE e.V.), which shall act as a proxy -** defined in Section 14 of version 3 of the license. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see <http://www.gnu.org/licenses/>. -** -****************************************************************************/ - -#include "song.h" - -#include <drumstick/alsaevent.h> - -static inline bool eventLessThan(const drumstick::SequencerEvent *s1, const drumstick::SequencerEvent *s2) -{ - return s1->getTick() < s2->getTick(); -} - -Song::Song() : - QList<drumstick::SequencerEvent *>(), - m_format(0), - m_ntrks(0), - m_division(0), - m_initialTempo(0) -{ -} - -Song::~Song() -{ - clear(); -} - -void Song::sort() -{ - qStableSort(begin(), end(), eventLessThan); -} - -void Song::clear() -{ - while (!isEmpty()) - delete takeFirst(); - m_fileName.clear(); - m_format = 0; - m_ntrks = 0; - m_division = 0; -} - -void Song::setHeader(int format, int ntrks, int division) -{ - m_format = format; - m_ntrks = ntrks; - m_division = division; -} - -void Song::setInitialTempo(int initialTempo) -{ - m_initialTempo = initialTempo; -} - -void Song::setDivision(int division) -{ - m_division = division; -} - -void Song::setFileName(const QString &fileName) -{ - m_fileName = fileName; -} diff --git a/src/plugins/drumsticksoundbackend/song.h b/src/plugins/drumsticksoundbackend/song.h deleted file mode 100644 index d11ba20..0000000 --- a/src/plugins/drumsticksoundbackend/song.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 by Sandro S. Andrade <sandroandrade@kde.org> -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public License as -** published by the Free Software Foundation; either version 2 of -** the License or (at your option) version 3 or any later version -** accepted by the membership of KDE e.V. (or its successor approved -** by the membership of KDE e.V.), which shall act as a proxy -** defined in Section 14 of version 3 of the license. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see <http://www.gnu.org/licenses/>. -** -****************************************************************************/ - -#ifndef SONG_H -#define SONG_H - -#include <QList> -#include <QString> - -namespace drumstick { - class SequencerEvent; -} - -class Song : public QList<drumstick::SequencerEvent *> -{ -public: - Song(); - virtual ~Song(); - - void clear(); - void sort(); - void setHeader(int format, int ntrks, int division); - void setInitialTempo(int initialTempo); - void setDivision(int division); - void setFileName(const QString &fileName); - - int format() const { return m_format; } - int tracks() const { return m_ntrks; } - int division() const { return m_division; } - int initialTempo() const { return m_initialTempo; } - QString fileName() const { return m_fileName; } - -private: - int m_format; - int m_ntrks; - int m_division; - int m_initialTempo; - QString m_fileName; -}; - -#endif // SONG_H diff --git a/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.cpp b/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.cpp index ea9e0fc..5a0cc67 100644 --- a/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.cpp +++ b/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.cpp @@ -22,40 +22,41 @@ #include "fluidsynthsoundbackend.h" +#include <QtMath> #include <QDebug> #include <QJsonObject> #include <QStandardPaths> +#include <functional> + +unsigned int FluidSynthSoundBackend::m_initialTime = 0; + FluidSynthSoundBackend::FluidSynthSoundBackend(QObject *parent) : Minuet::ISoundBackend(parent), + m_audioDriver(0), + m_sequencer(0), m_song(0) { - fluid_settings_t *settings; - settings = new_fluid_settings(); - fluid_settings_setstr(settings, "synth.reverb.active", "no"); - fluid_settings_setstr(settings, "synth.chorus.active", "no"); - - m_synth = new_fluid_synth(settings); - fluid_settings_setstr(settings, "audio.driver", "alsa"); - m_adriver = new_fluid_audio_driver(settings, m_synth); - m_sequencer = new_fluid_sequencer2(0); + m_tempo = 120; - // register synth as first destination - m_synthSeqID = fluid_sequencer_register_fluidsynth(m_sequencer, m_synth); + m_settings = new_fluid_settings(); + fluid_settings_setstr(m_settings, "synth.reverb.active", "no"); + fluid_settings_setstr(m_settings, "synth.chorus.active", "no"); + + m_synth = new_fluid_synth(m_settings); - // load soundfont int fluid_res = fluid_synth_sfload(m_synth, QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("soundfonts/GeneralUser-v1.47.sf2")).toLatin1(), 1); if (fluid_res == FLUID_FAILED) qDebug() << "Error when loading soundfont!"; - - m_tempo = 120; + + resetEngine(); } FluidSynthSoundBackend::~FluidSynthSoundBackend() { - delete_fluid_sequencer(m_sequencer); - delete_fluid_audio_driver(m_adriver); - delete_fluid_synth(m_synth); + deleteEngine(); + if (m_synth) delete_fluid_synth(m_synth); + if (m_settings) delete_fluid_settings(m_settings); } void FluidSynthSoundBackend::setPitch(qint8 pitch) @@ -89,10 +90,10 @@ void FluidSynthSoundBackend::prepareFromExerciseOptions(QJsonArray selectedExerc if (m_playMode != "rhythm") { appendEvent(1, chosenRootNote, 127, 1000*(60.0/m_tempo)); foreach(const QString &additionalNote, sequence.split(' ')) - appendEvent(1, chosenRootNote + additionalNote.toInt(), 127, 1000*(60.0/m_tempo)); + appendEvent(1, chosenRootNote + additionalNote.toInt(), 127, ((m_playMode == "scale") ? 1000:4000)*(60.0/m_tempo)); } else { - appendEvent(9, 80, 127, 1000*(60.0/m_tempo)); + //appendEvent(9, 80, 127, 1000*(60.0/m_tempo)); foreach(QString additionalNote, sequence.split(' ')) { // krazy:exclude=foreach float dotted = 1; if (additionalNote.endsWith('.')) { @@ -104,8 +105,13 @@ void FluidSynthSoundBackend::prepareFromExerciseOptions(QJsonArray selectedExerc } } } - if (m_playMode == "rhythm") - appendEvent(9, 80, 127, 1000*(60.0/m_tempo)); + //if (m_playMode == "rhythm") + // appendEvent(9, 80, 127, 1000*(60.0/m_tempo)); + + fluid_event_t *event = new_fluid_event(); + fluid_event_set_source(event, -1); + fluid_event_all_notes_off(event, 1); + m_song->append(event); } void FluidSynthSoundBackend::prepareFromMidiFile(const QString &fileName) @@ -115,11 +121,22 @@ void FluidSynthSoundBackend::prepareFromMidiFile(const QString &fileName) void FluidSynthSoundBackend::play() { - unsigned int now = fluid_sequencer_get_tick(m_sequencer); - foreach(fluid_event_t *event, *m_song.data()) { - fluid_sequencer_send_at(m_sequencer, event, now, 1); - now += (m_playMode == "rhythm") ? fluid_event_get_duration(event): - (m_playMode == "scale") ? 1000*(60.0/m_tempo):0; + if (!m_song.data()) + return; + + if (m_state != PlayingState) { + unsigned int now = fluid_sequencer_get_tick(m_sequencer); + foreach(fluid_event_t *event, *m_song.data()) { + if (fluid_event_get_type(event) != FLUID_SEQ_ALLNOTESOFF || m_playMode != "chord") { + fluid_event_set_dest(event, m_synthSeqID); + fluid_sequencer_send_at(m_sequencer, event, now, 1); + } + fluid_event_set_dest(event, m_callbackSeqID); + fluid_sequencer_send_at(m_sequencer, event, now, 1); + now += (m_playMode == "rhythm") ? fluid_event_get_duration(event): + (m_playMode == "scale") ? 1000*(60.0/m_tempo):0; + } + setState(PlayingState); } } @@ -129,15 +146,86 @@ void FluidSynthSoundBackend::pause() void FluidSynthSoundBackend::stop() { + if (m_state != StoppedState) { + fluid_event_t *event = new_fluid_event(); + fluid_event_set_source(event, -1); + fluid_event_all_notes_off(event, 1); + fluid_event_set_dest(event, m_synthSeqID); + fluid_sequencer_send_now(m_sequencer, event); + resetEngine(); + } +} + +void FluidSynthSoundBackend::reset() +{ + stop(); + m_song.reset(0); } void FluidSynthSoundBackend::appendEvent(int channel, short key, short velocity, unsigned int duration) { fluid_event_t *event = new_fluid_event(); fluid_event_set_source(event, -1); - fluid_event_set_dest(event, m_synthSeqID); fluid_event_note(event, channel, key, velocity, duration); m_song->append(event); } +void FluidSynthSoundBackend::sequencerCallback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) +{ + Q_UNUSED(seq); + + // This is safe! + FluidSynthSoundBackend *soundBackend = reinterpret_cast<FluidSynthSoundBackend *>(data); + + int eventType = fluid_event_get_type(event); + switch (eventType) { + case FLUID_SEQ_NOTE: { + if (m_initialTime == 0) + m_initialTime = time; + double adjustedTime = (time - m_initialTime)/1000.0; + int mins = adjustedTime / 60; + int secs = ((int)adjustedTime) % 60; + int cnts = 100*(adjustedTime-qFloor(adjustedTime)); + + static QChar fill('0'); + soundBackend->setPlaybackLabel(QStringLiteral("%1:%2.%3").arg(mins, 2, 10, fill).arg(secs, 2, 10, fill).arg(cnts, 2, 10, fill)); + break; + } + case FLUID_SEQ_ALLNOTESOFF: { + m_initialTime = 0; + soundBackend->setPlaybackLabel(QStringLiteral("00:00.00")); + soundBackend->setState(StoppedState); + break; + } + } +} + +void FluidSynthSoundBackend::resetEngine() +{ + deleteEngine(); + fluid_settings_setstr(m_settings, "audio.driver", "pulseaudio"); + m_audioDriver = new_fluid_audio_driver(m_settings, m_synth); + if (!m_audioDriver) { + fluid_settings_setstr(m_settings, "audio.driver", "alsa"); + m_audioDriver = new_fluid_audio_driver(m_settings, m_synth); + } + if (!m_audioDriver) { + qDebug() << "Couldn't start audio driver!"; + } + + m_sequencer = new_fluid_sequencer2(0); + m_synthSeqID = fluid_sequencer_register_fluidsynth(m_sequencer, m_synth); + m_callbackSeqID = fluid_sequencer_register_client (m_sequencer, "Minuet Fluidsynth Sound Backend", &FluidSynthSoundBackend::sequencerCallback, this); + + m_initialTime = 0; + setPlaybackLabel(QStringLiteral("00:00.00")); + setState(StoppedState); +} + +void FluidSynthSoundBackend::deleteEngine() +{ + if (m_sequencer) delete_fluid_sequencer(m_sequencer); + if (m_audioDriver) delete_fluid_audio_driver(m_audioDriver); +} + #include "moc_fluidsynthsoundbackend.cpp" diff --git a/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.h b/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.h index 51c9335..e6ada5a 100644 --- a/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.h +++ b/src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.h @@ -40,9 +40,9 @@ public: virtual ~FluidSynthSoundBackend() override; public Q_SLOTS: - virtual void setPitch(qint8 pitch); - virtual void setVolume(quint8 volume); - virtual void setTempo(quint8 tempo); + virtual void setPitch(qint8 pitch) override; + virtual void setVolume(quint8 volume) override; + virtual void setTempo(quint8 tempo) override; virtual void prepareFromExerciseOptions(QJsonArray selectedExerciseOptions) override; virtual void prepareFromMidiFile(const QString &fileName) override; @@ -50,15 +50,24 @@ public Q_SLOTS: virtual void play() override; virtual void pause() override; virtual void stop() override; + virtual void reset() override; private: void appendEvent(int channel, short key, short velocity, unsigned int duration); + static void sequencerCallback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data); + void resetEngine(); + void deleteEngine(); private: - fluid_synth_t *m_synth; - fluid_audio_driver_t *m_adriver; + fluid_settings_t *m_settings; + fluid_audio_driver_t *m_audioDriver; fluid_sequencer_t *m_sequencer; + fluid_synth_t *m_synth; + short m_synthSeqID; + short m_callbackSeqID; + static unsigned int m_initialTime; + QScopedPointer<QList<fluid_event_t *>> m_song; }; |
