Skip to content

Commit 7e87429

Browse files
committed
MIDI file playback using Sonivox library
1 parent 7691d33 commit 7e87429

File tree

15 files changed

+479
-32
lines changed

15 files changed

+479
-32
lines changed

cmdlnsynth/main.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,16 @@ int main(int argc, char *argv[])
4141
signal(SIGINT, signalHandler);
4242
signal(SIGTERM, signalHandler);
4343
synth = new SynthController();
44-
QObject::connect(synth, &SynthController::finished, &app, &QCoreApplication::quit);
44+
QObject::connect(synth->renderer(), &SynthRenderer::playbackStopped, &app, &QCoreApplication::quit);
4545
QObject::connect(&app, &QCoreApplication::aboutToQuit, synth, &QObject::deleteLater);
4646
synth->renderer()->initReverb(EAS_PARAM_REVERB_HALL);
47+
QStringList args = app.arguments();
48+
for(int i = 1; i < args.length(); ++i) {
49+
QFile argFile(args[i]);
50+
if (argFile.exists()) {
51+
synth->renderer()->playFile(argFile.fileName());
52+
}
53+
}
4754
synth->start();
4855
return app.exec();
4956
}

guisynth/guisynth.qrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<RCC>
22
<qresource prefix="/">
33
<file>icon.png</file>
4+
<file>open.png</file>
5+
<file>play.png</file>
6+
<file>stop.png</file>
47
</qresource>
58
</RCC>

guisynth/mainwindow.cpp

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
*/
1919

2020
#include <QCloseEvent>
21+
#include <QFileDialog>
2122
#include "mainwindow.h"
2223
#include "ui_mainwindow.h"
2324

2425
MainWindow::MainWindow(QWidget *parent) :
2526
QMainWindow(parent),
2627
ui(new Ui::MainWindow),
27-
m_synth(new SynthController(this))
28+
m_synth(new SynthController(this)),
29+
m_state(InitialState)
2830
{
2931
ui->setupUi(this);
3032
ui->combo_Reverb->addItem(QStringLiteral("Large Hall"), 0);
@@ -45,9 +47,16 @@ MainWindow::MainWindow(QWidget *parent) :
4547
connect(ui->combo_Chorus, SIGNAL(currentIndexChanged(int)), SLOT(chorusTypeChanged(int)));
4648
connect(ui->dial_Reverb, &QDial::valueChanged, this, &MainWindow::reverbChanged);
4749
connect(ui->dial_Chorus, &QDial::valueChanged, this, &MainWindow::chorusChanged);
50+
connect(ui->openButton, &QToolButton::clicked, this, &MainWindow::openFile);
51+
connect(ui->playButton, &QToolButton::clicked, this, &MainWindow::playSong);
52+
connect(ui->stopButton, &QToolButton::clicked, this, &MainWindow::stopSong);
53+
connect(m_synth->renderer(), &SynthRenderer::playbackStopped, this, &MainWindow::songStopped);
4854

4955
ui->combo_Reverb->setCurrentIndex(1);
5056
ui->dial_Reverb->setValue(25800);
57+
58+
m_songFile = QString();
59+
updateState(EmptyState);
5160
}
5261

5362
MainWindow::~MainWindow()
@@ -100,3 +109,74 @@ MainWindow::chorusChanged(int value)
100109
{
101110
m_synth->renderer()->setChorusLevel(value);
102111
}
112+
113+
void
114+
MainWindow::openFile()
115+
{
116+
m_songFile = QFileDialog::getOpenFileName(this,
117+
tr("Open MIDI file"), QDir::homePath(),
118+
tr("MIDI Files (*.mid *.midi *.kar)"));
119+
if (m_songFile.isEmpty()) {
120+
ui->lblSong->setText("[empty]");
121+
updateState(EmptyState);
122+
} else {
123+
QFileInfo f(m_songFile);
124+
ui->lblSong->setText(f.fileName());
125+
updateState(StoppedState);
126+
}
127+
}
128+
129+
void
130+
MainWindow::playSong()
131+
{
132+
if (m_state == StoppedState) {
133+
m_synth->renderer()->startPlayback(m_songFile);
134+
updateState(PlayingState);
135+
}
136+
}
137+
138+
void
139+
MainWindow::stopSong()
140+
{
141+
if (m_state == PlayingState) {
142+
m_synth->renderer()->stopPlayback();
143+
updateState(StoppedState);
144+
}
145+
}
146+
147+
void
148+
MainWindow::songStopped()
149+
{
150+
if (m_state != StoppedState) {
151+
updateState(StoppedState);
152+
}
153+
}
154+
155+
void
156+
MainWindow::updateState(PlayerState newState)
157+
{
158+
//qDebug() << Q_FUNC_INFO << newState;
159+
if (m_state != newState) {
160+
switch (newState) {
161+
case EmptyState:
162+
ui->playButton->setEnabled(false);
163+
ui->stopButton->setEnabled(false);
164+
ui->openButton->setEnabled(true);
165+
break;
166+
case PlayingState:
167+
ui->playButton->setEnabled(false);
168+
ui->stopButton->setEnabled(true);
169+
ui->openButton->setEnabled(false);
170+
break;
171+
case StoppedState:
172+
ui->stopButton->setEnabled(true);
173+
ui->playButton->setEnabled(true);
174+
ui->playButton->setChecked(false);
175+
ui->openButton->setEnabled(true);
176+
break;
177+
default:
178+
break;
179+
}
180+
m_state = newState;
181+
}
182+
}

guisynth/mainwindow.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
#include <QMainWindow>
2424
#include "synthcontroller.h"
2525

26+
enum PlayerState {
27+
InitialState,
28+
EmptyState,
29+
PlayingState,
30+
StoppedState
31+
};
32+
2633
namespace Ui {
2734
class MainWindow;
2835
}
@@ -34,6 +41,7 @@ class MainWindow : public QMainWindow
3441
public:
3542
explicit MainWindow(QWidget *parent = 0);
3643
~MainWindow();
44+
void updateState(PlayerState newState);
3745

3846
protected:
3947
virtual void showEvent(QShowEvent *ev);
@@ -44,10 +52,17 @@ private slots:
4452
void chorusTypeChanged(int index);
4553
void reverbChanged(int value);
4654
void chorusChanged(int value);
55+
void songStopped();
56+
57+
void openFile();
58+
void playSong();
59+
void stopSong();
4760

4861
private:
4962
Ui::MainWindow *ui;
5063
SynthController *m_synth;
64+
QString m_songFile;
65+
PlayerState m_state;
5166
};
5267

5368
#endif // MAINWINDOW_H

guisynth/mainwindow.ui

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>250</width>
10-
<height>167</height>
9+
<width>190</width>
10+
<height>208</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -19,7 +19,73 @@
1919
</property>
2020
<widget class="QWidget" name="centralWidget">
2121
<layout class="QGridLayout" name="gridLayout">
22-
<item row="0" column="0">
22+
<item row="0" column="0" colspan="3">
23+
<layout class="QHBoxLayout" name="horizontalLayout">
24+
<item>
25+
<widget class="QToolButton" name="playButton">
26+
<property name="text">
27+
<string>...</string>
28+
</property>
29+
<property name="icon">
30+
<iconset resource="guisynth.qrc">
31+
<normaloff>:/play.png</normaloff>:/play.png</iconset>
32+
</property>
33+
<property name="checkable">
34+
<bool>true</bool>
35+
</property>
36+
</widget>
37+
</item>
38+
<item>
39+
<widget class="QToolButton" name="stopButton">
40+
<property name="text">
41+
<string>...</string>
42+
</property>
43+
<property name="icon">
44+
<iconset resource="guisynth.qrc">
45+
<normaloff>:/stop.png</normaloff>:/stop.png</iconset>
46+
</property>
47+
</widget>
48+
</item>
49+
<item>
50+
<widget class="QToolButton" name="openButton">
51+
<property name="text">
52+
<string>...</string>
53+
</property>
54+
<property name="icon">
55+
<iconset resource="guisynth.qrc">
56+
<normaloff>:/open.png</normaloff>:/open.png</iconset>
57+
</property>
58+
<property name="checkable">
59+
<bool>false</bool>
60+
</property>
61+
<property name="checked">
62+
<bool>false</bool>
63+
</property>
64+
</widget>
65+
</item>
66+
<item>
67+
<widget class="QLabel" name="lblSong">
68+
<property name="text">
69+
<string>[empty]</string>
70+
</property>
71+
</widget>
72+
</item>
73+
</layout>
74+
</item>
75+
<item row="1" column="1" colspan="2">
76+
<spacer name="verticalSpacer">
77+
<property name="orientation">
78+
<enum>Qt::Vertical</enum>
79+
</property>
80+
<property name="sizeHint" stdset="0">
81+
<size>
82+
<width>20</width>
83+
<height>8</height>
84+
</size>
85+
</property>
86+
</spacer>
87+
</item>
88+
<item row="2" column="0">
2389
<widget class="QLabel" name="lblReverb">
2490
<property name="text">
2591
<string>Reverb</string>
@@ -29,7 +95,7 @@
2995
</property>
3096
</widget>
3197
</item>
32-
<item row="0" column="1">
98+
<item row="2" column="2">
3399
<widget class="QLabel" name="lblChorus">
34100
<property name="text">
35101
<string>Chorus</string>
@@ -39,24 +105,24 @@
39105
</property>
40106
</widget>
41107
</item>
42-
<item row="1" column="0">
108+
<item row="3" column="0" colspan="2">
43109
<widget class="QDial" name="dial_Reverb">
44110
<property name="maximum">
45111
<number>32765</number>
46112
</property>
47113
</widget>
48114
</item>
49-
<item row="1" column="1">
115+
<item row="3" column="2">
50116
<widget class="QDial" name="dial_Chorus">
51117
<property name="maximum">
52118
<number>32765</number>
53119
</property>
54120
</widget>
55121
</item>
56-
<item row="2" column="0">
122+
<item row="4" column="0" colspan="2">
57123
<widget class="QComboBox" name="combo_Reverb"/>
58124
</item>
59-
<item row="2" column="1">
125+
<item row="4" column="2">
60126
<widget class="QComboBox" name="combo_Chorus"/>
61127
</item>
62128
</layout>

guisynth/open.png

261 Bytes
Loading

guisynth/play.png

159 Bytes
Loading

guisynth/stop.png

174 Bytes
Loading

libsvoxeas/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ link_directories(
99
set( HEADERS
1010
synthcontroller.h
1111
synthrenderer.h
12-
)
12+
filewrapper.h
13+
)
1314

1415
set( SOURCES
1516
synthcontroller.cpp
1617
synthrenderer.cpp
17-
)
18+
filewrapper.cpp
19+
)
1820

1921
qt5_wrap_cpp( MOC_SRCS ${HEADERS} )
2022

libsvoxeas/filewrapper.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Sonivox EAS Synthesizer for Qt applications
3+
Copyright (C) 2016, Pedro Lopez-Cabanillas <plcl@users.sf.net>
4+
5+
This library is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation; either version 2 of the License, or
8+
(at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18+
*/
19+
20+
#include <QDebug>
21+
#include "filewrapper.h"
22+
23+
static int
24+
readAt(void *handle, void *buffer, int pos, int size) {
25+
return ((FileWrapper*)handle)->readAt(buffer, pos, size);
26+
}
27+
28+
static int
29+
size(void *handle) {
30+
return ((FileWrapper*)handle)->size();
31+
}
32+
33+
FileWrapper::FileWrapper(const QString path)
34+
{
35+
//qDebug() << Q_FUNC_INFO << path;
36+
m_file = new QFile(path);
37+
m_file->open(QIODevice::ReadOnly);
38+
m_Base = 0;
39+
m_Length = m_file->size();
40+
}
41+
42+
FileWrapper::FileWrapper(const char *path)
43+
{
44+
//qDebug("FileWrapper(path=%s)", path);
45+
m_file = new QFile(path);
46+
m_file->open(QIODevice::ReadOnly);
47+
m_Base = 0;
48+
m_Length = m_file->size();
49+
}
50+
51+
FileWrapper::~FileWrapper() {
52+
//qDebug("~FileWrapper");
53+
m_file->close();
54+
}
55+
56+
int
57+
FileWrapper::readAt(void *buffer, int offset, int size) {
58+
//qDebug("readAt(%p, %d, %d)", buffer, offset, size);
59+
m_file->seek(offset);
60+
if (offset + size > m_Length) {
61+
size = m_Length - offset;
62+
}
63+
return m_file->read((char *)buffer, size);
64+
}
65+
66+
int
67+
FileWrapper::size() {
68+
//qDebug("size() = %d", int(mLength));
69+
return m_Length;
70+
}
71+
72+
EAS_FILE_LOCATOR
73+
FileWrapper::getLocator() {
74+
m_easFile.handle = this;
75+
m_easFile.readAt = ::readAt;
76+
m_easFile.size = ::size;
77+
return &m_easFile;
78+
}

0 commit comments

Comments
 (0)