Merge pull request #2339 from LMMS/audio-soundio
add libsoundio audio backend
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
sudo add-apt-repository ppa:kalakris/cmake -y;
|
||||
sudo add-apt-repository ppa:andrewrk/libgroove -y;
|
||||
if [ $QT5 ]
|
||||
then
|
||||
sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev
|
||||
libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev
|
||||
libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev
|
||||
libgig-dev"
|
||||
libgig-dev libsoundio-dev"
|
||||
|
||||
if [ $QT5 ]
|
||||
then
|
||||
|
||||
@@ -51,6 +51,7 @@ OPTION(WANT_JACK "Include JACK (Jack Audio Connection Kit) support" ON)
|
||||
OPTION(WANT_OGGVORBIS "Include OGG/Vorbis support" ON)
|
||||
OPTION(WANT_PULSEAUDIO "Include PulseAudio support" ON)
|
||||
OPTION(WANT_PORTAUDIO "Include PortAudio support" ON)
|
||||
OPTION(WANT_SOUNDIO "Include libsoundio support" ON)
|
||||
OPTION(WANT_SDL "Include SDL (Simple DirectMedia Layer) support" ON)
|
||||
OPTION(WANT_SF2 "Include SoundFont2 player plugin" ON)
|
||||
OPTION(WANT_GIG "Include GIG player plugin" ON)
|
||||
@@ -260,6 +261,20 @@ IF(WANT_PORTAUDIO)
|
||||
ENDIF(PORTAUDIO_FOUND)
|
||||
ENDIF(WANT_PORTAUDIO)
|
||||
|
||||
# check for libsoundio
|
||||
IF(WANT_SOUNDIO)
|
||||
FIND_PACKAGE(SoundIo)
|
||||
IF(SOUNDIO_FOUND)
|
||||
SET(LMMS_HAVE_SOUNDIO TRUE)
|
||||
SET(STATUS_SOUNDIO "OK")
|
||||
INCLUDE_DIRECTORIES("${SOUNDIO_INCLUDE_DIR}")
|
||||
ELSE(SOUNDIO_FOUND)
|
||||
SET(SOUNDIO_INCLUDE_DIR "")
|
||||
SET(STATUS_SOUNDIO "not found, please install libsoundio if you require libsoundio support")
|
||||
SET(SOUNDIO_LIBRARY "")
|
||||
ENDIF(SOUNDIO_FOUND)
|
||||
ENDIF(WANT_SOUNDIO)
|
||||
|
||||
|
||||
# check for PulseAudio
|
||||
IF(WANT_PULSEAUDIO)
|
||||
@@ -522,6 +537,7 @@ MESSAGE(
|
||||
"* JACK : ${STATUS_JACK}\n"
|
||||
"* OSS : ${STATUS_OSS}\n"
|
||||
"* PortAudio : ${STATUS_PORTAUDIO}\n"
|
||||
"* libsoundio : ${STATUS_SOUNDIO}\n"
|
||||
"* PulseAudio : ${STATUS_PULSEAUDIO}\n"
|
||||
"* SDL : ${STATUS_SDL}\n"
|
||||
)
|
||||
|
||||
16
cmake/modules/FindSoundIo.cmake
Normal file
16
cmake/modules/FindSoundIo.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2015 Andrew Kelley
|
||||
# This file is MIT licensed.
|
||||
# See http://opensource.org/licenses/MIT
|
||||
|
||||
# SOUNDIO_FOUND
|
||||
# SOUNDIO_INCLUDE_DIR
|
||||
# SOUNDIO_LIBRARY
|
||||
|
||||
find_path(SOUNDIO_INCLUDE_DIR NAMES soundio/soundio.h)
|
||||
|
||||
find_library(SOUNDIO_LIBRARY NAMES soundio)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SOUNDIO DEFAULT_MSG SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY)
|
||||
134
include/AudioSoundIo.h
Normal file
134
include/AudioSoundIo.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* AudioSoundIo.h - device-class that performs PCM-output via libsoundio
|
||||
*
|
||||
* Copyright (c) 2015 Andrew Kelley <superjoe30@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_SOUNDIO_H
|
||||
#define AUDIO_SOUNDIO_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
#include "ComboBoxModel.h"
|
||||
|
||||
#ifdef LMMS_HAVE_SOUNDIO
|
||||
|
||||
#include <soundio/soundio.h>
|
||||
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioDeviceSetupWidget.h"
|
||||
|
||||
class ComboBox;
|
||||
class LcdSpinBox;
|
||||
|
||||
// Exists only to work around "Error: Meta object features not supported for nested classes"
|
||||
class AudioSoundIoSetupUtil : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void *m_setupWidget;
|
||||
public slots:
|
||||
void updateDevices();
|
||||
void reconnectSoundIo();
|
||||
};
|
||||
|
||||
class AudioSoundIo : public AudioDevice
|
||||
{
|
||||
public:
|
||||
AudioSoundIo( bool & _success_ful, Mixer* mixer );
|
||||
virtual ~AudioSoundIo();
|
||||
|
||||
inline static QString name()
|
||||
{
|
||||
return QT_TRANSLATE_NOOP( "setupWidget", "soundio" );
|
||||
}
|
||||
|
||||
class setupWidget : public AudioDeviceSetupWidget
|
||||
{
|
||||
public:
|
||||
setupWidget( QWidget * _parent );
|
||||
virtual ~setupWidget();
|
||||
|
||||
virtual void saveSettings();
|
||||
|
||||
void updateDevices();
|
||||
void reconnectSoundIo();
|
||||
|
||||
private:
|
||||
|
||||
AudioSoundIoSetupUtil m_setupUtil;
|
||||
ComboBox * m_backend;
|
||||
ComboBox * m_device;
|
||||
|
||||
ComboBoxModel m_backendModel;
|
||||
ComboBoxModel m_deviceModel;
|
||||
|
||||
SoundIo * m_soundio;
|
||||
|
||||
struct DeviceId {
|
||||
QString id;
|
||||
bool is_raw;
|
||||
};
|
||||
QList<DeviceId> m_deviceList;
|
||||
|
||||
int m_defaultOutIndex;
|
||||
bool m_isFirst;
|
||||
|
||||
} ;
|
||||
|
||||
private:
|
||||
virtual void startProcessing();
|
||||
virtual void stopProcessing();
|
||||
|
||||
SoundIo *m_soundio;
|
||||
SoundIoOutStream *m_outstream;
|
||||
|
||||
surroundSampleFrame * m_outBuf;
|
||||
int m_outBufSize;
|
||||
fpp_t m_outBufFramesTotal;
|
||||
fpp_t m_outBufFrameIndex;
|
||||
|
||||
int m_disconnectErr;
|
||||
void onBackendDisconnect(int err);
|
||||
|
||||
void writeCallback(int frame_count_min, int frame_count_max);
|
||||
void errorCallback(int err);
|
||||
void underflowCallback();
|
||||
|
||||
static void staticWriteCallback(SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
|
||||
return ((AudioSoundIo *)outstream->userdata)->writeCallback(frame_count_min, frame_count_max);
|
||||
}
|
||||
static void staticErrorCallback(SoundIoOutStream *outstream, int err) {
|
||||
return ((AudioSoundIo *)outstream->userdata)->errorCallback(err);
|
||||
}
|
||||
static void staticUnderflowCallback(SoundIoOutStream *outstream) {
|
||||
return ((AudioSoundIo *)outstream->userdata)->underflowCallback();
|
||||
}
|
||||
static void staticOnBackendDisconnect(SoundIo *soundio, int err) {
|
||||
return ((AudioSoundIo *)soundio->userdata)->onBackendDisconnect(err);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -112,6 +112,7 @@ SET(LMMS_REQUIRED_LIBS
|
||||
${ASOUND_LIBRARY}
|
||||
${SDL_LIBRARY}
|
||||
${PORTAUDIO_LIBRARIES}
|
||||
${SOUNDIO_LIBRARY}
|
||||
${PULSEAUDIO_LIBRARIES}
|
||||
${JACK_LIBRARIES}
|
||||
${OGGVORBIS_LIBRARIES}
|
||||
@@ -182,6 +183,7 @@ IF(LMMS_BUILD_WIN32)
|
||||
"${MINGW_PREFIX}/bin/libfftw3f-3.dll"
|
||||
"${MINGW_PREFIX}/bin/libFLAC-8.dll"
|
||||
"${MINGW_PREFIX}/bin/libportaudio-2.dll"
|
||||
"${MINGW_PREFIX}/bin/libsoundio.dll"
|
||||
"${MINGW_PREFIX}/bin/libpng16-16.dll"
|
||||
"${MINGW_PREFIX}/bin/SDL.dll"
|
||||
"${MINGW_PREFIX}/bin/libglib-2.0-0.dll"
|
||||
|
||||
@@ -73,6 +73,7 @@ set(LMMS_SRCS
|
||||
core/audio/AudioOss.cpp
|
||||
core/audio/AudioPort.cpp
|
||||
core/audio/AudioPortAudio.cpp
|
||||
core/audio/AudioSoundIo.cpp
|
||||
core/audio/AudioPulseAudio.cpp
|
||||
core/audio/AudioSampleRecorder.cpp
|
||||
core/audio/AudioSdl.cpp
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "AudioJack.h"
|
||||
#include "AudioOss.h"
|
||||
#include "AudioPortAudio.h"
|
||||
#include "AudioSoundIo.h"
|
||||
#include "AudioPulseAudio.h"
|
||||
#include "AudioSdl.h"
|
||||
#include "AudioDummy.h"
|
||||
@@ -220,11 +221,10 @@ void Mixer::stopProcessing()
|
||||
if( m_fifoWriter != NULL )
|
||||
{
|
||||
m_fifoWriter->finish();
|
||||
m_audioDev->stopProcessing();
|
||||
m_fifoWriter->wait( 1000 );
|
||||
m_fifoWriter->terminate();
|
||||
m_fifoWriter->wait();
|
||||
delete m_fifoWriter;
|
||||
m_fifoWriter = NULL;
|
||||
m_audioDev->stopProcessing();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -831,6 +831,20 @@ AudioDevice * Mixer::tryAudioDevices()
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LMMS_HAVE_SOUNDIO
|
||||
if( dev_name == AudioSoundIo::name() || dev_name == "" )
|
||||
{
|
||||
dev = new AudioSoundIo( success_ful, this );
|
||||
if( success_ful )
|
||||
{
|
||||
m_audioDevName = AudioSoundIo::name();
|
||||
return dev;
|
||||
}
|
||||
delete dev;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// add more device-classes here...
|
||||
//dev = new audioXXXX( SAMPLE_RATES[m_qualityLevel], success_ful, this );
|
||||
//if( sucess_ful )
|
||||
|
||||
453
src/core/audio/AudioSoundIo.cpp
Normal file
453
src/core/audio/AudioSoundIo.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* AudioSoundIo.cpp - device-class that performs PCM-output via libsoundio
|
||||
*
|
||||
* Copyright (c) 2015 Andrew Kelley <superjoe30@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioSoundIo.h"
|
||||
|
||||
#ifdef LMMS_HAVE_SOUNDIO
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "debug.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "gui_templates.h"
|
||||
#include "templates.h"
|
||||
#include "ComboBox.h"
|
||||
#include "LcdSpinBox.h"
|
||||
|
||||
AudioSoundIo::AudioSoundIo( bool & outSuccessful, Mixer * _mixer ) :
|
||||
AudioDevice( tLimit<ch_cnt_t>(
|
||||
ConfigManager::inst()->value( "audiosoundio", "channels" ).toInt(), DEFAULT_CHANNELS, SURROUND_CHANNELS ),
|
||||
_mixer )
|
||||
{
|
||||
outSuccessful = false;
|
||||
m_soundio = NULL;
|
||||
m_outstream = NULL;
|
||||
m_disconnectErr = 0;
|
||||
m_outBufFrameIndex = 0;
|
||||
m_outBufFramesTotal = 0;
|
||||
|
||||
m_soundio = soundio_create();
|
||||
if (!m_soundio)
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
m_soundio->app_name = "LMMS";
|
||||
m_soundio->userdata = this;
|
||||
m_soundio->on_backend_disconnect = staticOnBackendDisconnect;
|
||||
|
||||
const QString& configBackend = ConfigManager::inst()->value( "audiosoundio", "backend" );
|
||||
const QString& configDeviceId = ConfigManager::inst()->value( "audiosoundio", "out_device_id" );
|
||||
const QString& configDeviceRaw = ConfigManager::inst()->value( "audiosoundio", "out_device_raw" );
|
||||
|
||||
int err;
|
||||
int outDeviceCount = 0;
|
||||
int backendCount = soundio_backend_count(m_soundio);
|
||||
for (int i = 0; i < backendCount; i += 1)
|
||||
{
|
||||
SoundIoBackend backend = soundio_get_backend(m_soundio, i);
|
||||
if (configBackend == soundio_backend_name(backend))
|
||||
{
|
||||
if ((err = soundio_connect_backend(m_soundio, backend)))
|
||||
{
|
||||
// error occurred, leave outDeviceCount 0
|
||||
}
|
||||
else
|
||||
{
|
||||
soundio_flush_events(m_soundio);
|
||||
if (m_disconnectErr)
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(m_disconnectErr));
|
||||
return;
|
||||
}
|
||||
outDeviceCount = soundio_output_device_count(m_soundio);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (outDeviceCount <= 0)
|
||||
{
|
||||
// try connecting to the default backend
|
||||
if ((err = soundio_connect(m_soundio)))
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
soundio_flush_events(m_soundio);
|
||||
if (m_disconnectErr)
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(m_disconnectErr));
|
||||
return;
|
||||
}
|
||||
|
||||
outDeviceCount = soundio_output_device_count(m_soundio);
|
||||
|
||||
if (outDeviceCount <= 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: no devices found\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int selected_device_index = soundio_default_output_device_index(m_soundio);
|
||||
|
||||
bool wantRaw = (configDeviceRaw == "yes");
|
||||
for (int i = 0; i < outDeviceCount; i += 1)
|
||||
{
|
||||
SoundIoDevice *device = soundio_get_output_device(m_soundio, i);
|
||||
bool isThisOne = (configDeviceId == device->id && wantRaw == device->is_raw);
|
||||
soundio_device_unref(device);
|
||||
if (isThisOne)
|
||||
{
|
||||
selected_device_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SoundIoDevice *device = soundio_get_output_device(m_soundio, selected_device_index);
|
||||
m_outstream = soundio_outstream_create(device);
|
||||
soundio_device_unref(device);
|
||||
|
||||
if (!m_outstream)
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int currentSampleRate = sampleRate();
|
||||
int closestSupportedSampleRate = -1;
|
||||
|
||||
for (int i = 0; i < device->sample_rate_count; i += 1)
|
||||
{
|
||||
SoundIoSampleRateRange *range = &device->sample_rates[i];
|
||||
if (range->min <= currentSampleRate && currentSampleRate <= range->max)
|
||||
{
|
||||
closestSupportedSampleRate = currentSampleRate;
|
||||
break;
|
||||
}
|
||||
if (closestSupportedSampleRate == -1 ||
|
||||
abs(range->max - currentSampleRate) < abs(closestSupportedSampleRate - currentSampleRate))
|
||||
{
|
||||
closestSupportedSampleRate = range->max;
|
||||
}
|
||||
}
|
||||
|
||||
if (closestSupportedSampleRate != currentSampleRate)
|
||||
{
|
||||
setSampleRate(closestSupportedSampleRate);
|
||||
currentSampleRate = closestSupportedSampleRate;
|
||||
}
|
||||
|
||||
m_outstream->name = "LMMS";
|
||||
m_outstream->software_latency = (double)mixer()->framesPerPeriod() / (double)currentSampleRate;
|
||||
m_outstream->userdata = this;
|
||||
m_outstream->write_callback = staticWriteCallback;
|
||||
m_outstream->error_callback = staticErrorCallback;
|
||||
m_outstream->underflow_callback = staticUnderflowCallback;
|
||||
m_outstream->sample_rate = currentSampleRate;
|
||||
m_outstream->layout = *soundio_channel_layout_get_default(channels());
|
||||
m_outstream->format = SoundIoFormatFloat32NE;
|
||||
|
||||
if ((err = soundio_outstream_open(m_outstream)))
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Output device: '%s' backend: '%s'\n",
|
||||
device->name, soundio_backend_name(m_soundio->current_backend));
|
||||
|
||||
outSuccessful = true;
|
||||
}
|
||||
|
||||
void AudioSoundIo::onBackendDisconnect(int err)
|
||||
{
|
||||
m_disconnectErr = err;
|
||||
}
|
||||
|
||||
AudioSoundIo::~AudioSoundIo()
|
||||
{
|
||||
stopProcessing();
|
||||
soundio_destroy(m_soundio);
|
||||
}
|
||||
|
||||
void AudioSoundIo::startProcessing()
|
||||
{
|
||||
m_outBufFrameIndex = 0;
|
||||
m_outBufFramesTotal = 0;
|
||||
m_outBufSize = mixer()->framesPerPeriod();
|
||||
|
||||
m_outBuf = new surroundSampleFrame[m_outBufSize];
|
||||
|
||||
int err;
|
||||
if ((err = soundio_outstream_start(m_outstream)))
|
||||
{
|
||||
fprintf(stderr, "soundio unable to start stream: %s\n", soundio_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSoundIo::stopProcessing()
|
||||
{
|
||||
soundio_outstream_destroy(m_outstream);
|
||||
m_outstream = NULL;
|
||||
|
||||
delete[] m_outBuf;
|
||||
m_outBuf = NULL;
|
||||
}
|
||||
|
||||
void AudioSoundIo::errorCallback(int err)
|
||||
{
|
||||
fprintf(stderr, "soundio: error streaming: %s\n", soundio_strerror(err));
|
||||
}
|
||||
|
||||
void AudioSoundIo::underflowCallback()
|
||||
{
|
||||
fprintf(stderr, "soundio: buffer underflow reported\n");
|
||||
}
|
||||
|
||||
void AudioSoundIo::writeCallback(int frameCountMin, int frameCountMax)
|
||||
{
|
||||
const struct SoundIoChannelLayout *layout = &m_outstream->layout;
|
||||
SoundIoChannelArea *areas;
|
||||
int bytesPerSample = m_outstream->bytes_per_sample;
|
||||
int err;
|
||||
|
||||
const float gain = mixer()->masterGain();
|
||||
|
||||
int framesLeft = frameCountMax;
|
||||
|
||||
while (framesLeft > 0)
|
||||
{
|
||||
int frameCount = framesLeft;
|
||||
if ((err = soundio_outstream_begin_write(m_outstream, &areas, &frameCount)))
|
||||
{
|
||||
errorCallback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!frameCount)
|
||||
break;
|
||||
|
||||
for (int frame = 0; frame < frameCount; frame += 1)
|
||||
{
|
||||
if (m_outBufFrameIndex >= m_outBufFramesTotal)
|
||||
{
|
||||
m_outBufFramesTotal = getNextBuffer(m_outBuf);
|
||||
m_outBufFrameIndex = 0;
|
||||
}
|
||||
|
||||
for (int channel = 0; channel < layout->channel_count; channel += 1)
|
||||
{
|
||||
float sample = gain * m_outBuf[m_outBufFrameIndex][channel];
|
||||
memcpy(areas[channel].ptr, &sample, bytesPerSample);
|
||||
areas[channel].ptr += areas[channel].step;
|
||||
}
|
||||
m_outBufFrameIndex += 1;
|
||||
}
|
||||
|
||||
if ((err = soundio_outstream_end_write(m_outstream)))
|
||||
{
|
||||
errorCallback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
framesLeft -= frameCount;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSoundIoSetupUtil::reconnectSoundIo()
|
||||
{
|
||||
((AudioSoundIo::setupWidget *)m_setupWidget)->reconnectSoundIo();
|
||||
}
|
||||
|
||||
void AudioSoundIoSetupUtil::updateDevices()
|
||||
{
|
||||
((AudioSoundIo::setupWidget *)m_setupWidget)->updateDevices();
|
||||
}
|
||||
|
||||
static void setupWidgetOnBackendDisconnect(SoundIo *soundio, int err)
|
||||
{
|
||||
AudioSoundIo::setupWidget *setupWidget = (AudioSoundIo::setupWidget *)soundio->userdata;
|
||||
setupWidget->reconnectSoundIo();
|
||||
}
|
||||
|
||||
static void setup_widget_on_devices_change(SoundIo *soundio)
|
||||
{
|
||||
AudioSoundIo::setupWidget *setupWidget = (AudioSoundIo::setupWidget *)soundio->userdata;
|
||||
setupWidget->updateDevices();
|
||||
}
|
||||
|
||||
void AudioSoundIo::setupWidget::reconnectSoundIo()
|
||||
{
|
||||
const QString& configBackend = m_isFirst ?
|
||||
ConfigManager::inst()->value( "audiosoundio", "backend" ) : m_backendModel.currentText();
|
||||
m_isFirst = false;
|
||||
|
||||
soundio_disconnect(m_soundio);
|
||||
|
||||
int err;
|
||||
int backend_index = m_backendModel.findText(configBackend);
|
||||
if (backend_index < 0)
|
||||
{
|
||||
if ((err = soundio_connect(m_soundio)))
|
||||
{
|
||||
fprintf(stderr, "soundio: unable to connect backend: %s\n", soundio_strerror(err));
|
||||
return;
|
||||
}
|
||||
backend_index = m_backendModel.findText(soundio_backend_name(m_soundio->current_backend));
|
||||
assert(backend_index >= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SoundIoBackend backend = soundio_get_backend(m_soundio, backend_index);
|
||||
if ((err = soundio_connect_backend(m_soundio, backend)))
|
||||
{
|
||||
fprintf(stderr, "soundio: unable to connect %s backend: %s\n",
|
||||
soundio_backend_name(backend), soundio_strerror(err));
|
||||
if ((err = soundio_connect(m_soundio)))
|
||||
{
|
||||
fprintf(stderr, "soundio: unable to connect backend: %s\n", soundio_strerror(err));
|
||||
return;
|
||||
}
|
||||
backend_index = m_backendModel.findText(soundio_backend_name(m_soundio->current_backend));
|
||||
assert(backend_index >= 0);
|
||||
}
|
||||
}
|
||||
m_backendModel.setValue(backend_index);
|
||||
|
||||
soundio_flush_events(m_soundio);
|
||||
|
||||
const QString& configDeviceId = ConfigManager::inst()->value( "audiosoundio", "out_device_id" );
|
||||
const QString& configDeviceRaw = ConfigManager::inst()->value( "audiosoundio", "out_device_raw" );
|
||||
|
||||
int deviceIndex = m_defaultOutIndex;
|
||||
bool wantRaw = (configDeviceRaw == "yes");
|
||||
for (int i = 0; i < m_deviceList.length(); i += 1)
|
||||
{
|
||||
const DeviceId *deviceId = &m_deviceList.at(i);
|
||||
if (deviceId->id == configDeviceId && deviceId->is_raw == wantRaw)
|
||||
{
|
||||
deviceIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_deviceModel.setValue(deviceIndex);
|
||||
}
|
||||
|
||||
void AudioSoundIo::setupWidget::updateDevices()
|
||||
{
|
||||
m_defaultOutIndex = soundio_default_output_device_index(m_soundio);
|
||||
|
||||
// get devices for selected backend
|
||||
m_deviceModel.clear();
|
||||
m_deviceList.clear();
|
||||
int outDeviceCount = soundio_output_device_count(m_soundio);
|
||||
for (int i = 0; i < outDeviceCount; i += 1)
|
||||
{
|
||||
SoundIoDevice *device = soundio_get_output_device(m_soundio, i);
|
||||
|
||||
QString raw_text = device->is_raw ? " (raw)" : "";
|
||||
QString default_text = (i == m_defaultOutIndex) ? " (default)" : "";
|
||||
|
||||
m_deviceModel.addItem(device->name + raw_text + default_text);
|
||||
m_deviceList.append({device->id, device->is_raw});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AudioSoundIo::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
AudioDeviceSetupWidget( AudioSoundIo::name(), _parent )
|
||||
{
|
||||
m_setupUtil.m_setupWidget = this;
|
||||
|
||||
m_backend = new ComboBox( this, "BACKEND" );
|
||||
m_backend->setGeometry( 64, 15, 260, 20 );
|
||||
|
||||
QLabel * backend_lbl = new QLabel( tr( "BACKEND" ), this );
|
||||
backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) );
|
||||
backend_lbl->move( 8, 18 );
|
||||
|
||||
m_device = new ComboBox( this, "DEVICE" );
|
||||
m_device->setGeometry( 64, 35, 260, 20 );
|
||||
|
||||
QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this );
|
||||
dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
|
||||
dev_lbl->move( 8, 38 );
|
||||
|
||||
// Setup models
|
||||
m_soundio = soundio_create();
|
||||
if (!m_soundio)
|
||||
{
|
||||
fprintf(stderr, "Unable to initialize soundio: out of memory\n");
|
||||
return;
|
||||
}
|
||||
m_soundio->userdata = this;
|
||||
m_soundio->on_backend_disconnect = setupWidgetOnBackendDisconnect;
|
||||
m_soundio->on_devices_change = setup_widget_on_devices_change;
|
||||
m_soundio->app_name = "LMMS";
|
||||
|
||||
int backendCount = soundio_backend_count(m_soundio);
|
||||
for (int i = 0; i < backendCount; i += 1)
|
||||
{
|
||||
SoundIoBackend backend = soundio_get_backend(m_soundio, i);
|
||||
m_backendModel.addItem(soundio_backend_name(backend));
|
||||
}
|
||||
|
||||
m_isFirst = true;
|
||||
|
||||
reconnectSoundIo();
|
||||
|
||||
bool ok = connect( &m_backendModel, SIGNAL( dataChanged() ), &m_setupUtil, SLOT( reconnectSoundIo() ) );
|
||||
assert(ok);
|
||||
|
||||
m_backend->setModel( &m_backendModel );
|
||||
m_device->setModel( &m_deviceModel );
|
||||
}
|
||||
|
||||
AudioSoundIo::setupWidget::~setupWidget()
|
||||
{
|
||||
bool ok = disconnect( &m_backendModel, SIGNAL( dataChanged() ), &m_setupUtil, SLOT( reconnectSoundIo() ) );
|
||||
assert(ok);
|
||||
soundio_destroy(m_soundio);
|
||||
}
|
||||
|
||||
void AudioSoundIo::setupWidget::saveSettings()
|
||||
{
|
||||
int deviceIndex = m_deviceModel.value();
|
||||
const DeviceId *deviceId = &m_deviceList.at(deviceIndex);
|
||||
|
||||
QString configDeviceRaw = deviceId->is_raw ? "yes" : "no";
|
||||
|
||||
ConfigManager::inst()->setValue( "audiosoundio", "backend", m_backendModel.currentText());
|
||||
ConfigManager::inst()->setValue( "audiosoundio", "out_device_id", deviceId->id);
|
||||
ConfigManager::inst()->setValue( "audiosoundio", "out_device_raw", configDeviceRaw);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "AudioJack.h"
|
||||
#include "AudioOss.h"
|
||||
#include "AudioPortAudio.h"
|
||||
#include "AudioSoundIo.h"
|
||||
#include "AudioPulseAudio.h"
|
||||
#include "AudioSdl.h"
|
||||
#include "AudioDummy.h"
|
||||
@@ -734,6 +735,11 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) :
|
||||
new AudioPortAudio::setupWidget( asw );
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_SOUNDIO
|
||||
m_audioIfaceSetupWidgets[AudioSoundIo::name()] =
|
||||
new AudioSoundIo::setupWidget( asw );
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_SDL
|
||||
m_audioIfaceSetupWidgets[AudioSdl::name()] =
|
||||
new AudioSdl::setupWidget( asw );
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#cmakedefine LMMS_HAVE_OGGVORBIS
|
||||
#cmakedefine LMMS_HAVE_OSS
|
||||
#cmakedefine LMMS_HAVE_PORTAUDIO
|
||||
#cmakedefine LMMS_HAVE_SOUNDIO
|
||||
#cmakedefine LMMS_HAVE_PULSEAUDIO
|
||||
#cmakedefine LMMS_HAVE_SDL
|
||||
#cmakedefine LMMS_HAVE_STK
|
||||
|
||||
Reference in New Issue
Block a user