diff --git a/.travis/linux..before_install.sh b/.travis/linux..before_install.sh index 90da27640..151db4d78 100644 --- a/.travis/linux..before_install.sh +++ b/.travis/linux..before_install.sh @@ -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 diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index 8bbaf24ad..1dfcd1615 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3001ada18..aae9ad075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" ) diff --git a/cmake/modules/FindSoundIo.cmake b/cmake/modules/FindSoundIo.cmake new file mode 100644 index 000000000..0d905ecf2 --- /dev/null +++ b/cmake/modules/FindSoundIo.cmake @@ -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) diff --git a/include/AudioSoundIo.h b/include/AudioSoundIo.h new file mode 100644 index 000000000..5816bc52b --- /dev/null +++ b/include/AudioSoundIo.h @@ -0,0 +1,134 @@ +/* + * AudioSoundIo.h - device-class that performs PCM-output via libsoundio + * + * Copyright (c) 2015 Andrew Kelley + * + * 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 + +#include "lmmsconfig.h" +#include "ComboBoxModel.h" + +#ifdef LMMS_HAVE_SOUNDIO + +#include + +#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 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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6a8776bf..b014f8307 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 082951d34..5634940f7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 948a4790f..5c85ff2f3 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.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 ) diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp new file mode 100644 index 000000000..89d21f0a9 --- /dev/null +++ b/src/core/audio/AudioSoundIo.cpp @@ -0,0 +1,453 @@ +/* + * AudioSoundIo.cpp - device-class that performs PCM-output via libsoundio + * + * Copyright (c) 2015 Andrew Kelley + * + * 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 +#include + +#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( + 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 diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index b43e5707f..9e0aef003 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -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 ); diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 7f774ff25..3d48d8372 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -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