summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSandro S. Andrade <sandroandrade@kde.org>2016-07-22 03:47:26 (GMT)
committerSandro S. Andrade <sandroandrade@kde.org>2016-07-22 03:47:26 (GMT)
commit751f969eb23f2c086290530e5ba3351a63ef88c7 (patch)
tree25f2e4c67a9cdf0722c1ad0f7d682dc759d964c3
parent19b0de32d34c68966389ff887c81c7399d5b5ef7 (diff)
Improve fluidsynthsoundbackend
Remove old drumstick-based soundbackend
-rw-r--r--README2
-rw-r--r--README.packagers3
-rw-r--r--src/app/qml/Main.qml2
-rw-r--r--src/interfaces/isoundbackend.cpp24
-rw-r--r--src/interfaces/isoundbackend.h22
-rw-r--r--src/plugins/CMakeLists.txt29
-rw-r--r--src/plugins/drumsticksoundbackend/CMakeLists.txt20
-rw-r--r--src/plugins/drumsticksoundbackend/drumsticksoundbackend.cpp339
-rw-r--r--src/plugins/drumsticksoundbackend/drumsticksoundbackend.h93
-rw-r--r--src/plugins/drumsticksoundbackend/drumsticksoundbackend.json12
-rw-r--r--src/plugins/drumsticksoundbackend/midisequenceroutputthread.cpp182
-rw-r--r--src/plugins/drumsticksoundbackend/midisequenceroutputthread.h72
-rw-r--r--src/plugins/drumsticksoundbackend/song.cpp81
-rw-r--r--src/plugins/drumsticksoundbackend/song.h60
-rw-r--r--src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.cpp142
-rw-r--r--src/plugins/fluidsynthsoundbackend/fluidsynthsoundbackend.h19
16 files changed, 152 insertions, 950 deletions
diff --git a/README b/README
index 016cdea..4614d1c 100644
--- a/README
+++ b/README
@@ -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;
};