commit 221bd7d5f94f271f044d875985d596c45d16ba80
parent 4de1f117af65e3c6a8d13f73e5794adb9d4b2ee1
Author: Hans Petter Selasky <hps@selasky.org>
Date: Wed, 29 Oct 2014 11:20:39 +0100
Add new OSS MULTI audio backend:
Add new OSS compatible backend which can output each part as a
separate stereo channel. Typically you will need audio hardware which
supports more than 2 channels. This is also useful for live mixing
and recording through virtual OSS devices.
Signed-off-by: Hans Petter Selasky <hps@selasky.org>
Diffstat:
4 files changed, 364 insertions(+), 1 deletion(-)
diff --git a/src/Nio/CMakeLists.txt b/src/Nio/CMakeLists.txt
@@ -43,7 +43,7 @@ if(AlsaEnable)
endif(AlsaEnable)
if(OssEnable)
- list(APPEND zynaddsubfx_nio_SRCS OssEngine.cpp)
+ list(APPEND zynaddsubfx_nio_SRCS OssEngine.cpp OssMultiEngine.cpp)
endif(OssEnable)
diff --git a/src/Nio/EngineMgr.cpp b/src/Nio/EngineMgr.cpp
@@ -9,6 +9,7 @@
#include "NulEngine.h"
#if OSS
#include "OssEngine.h"
+#include "OssMultiEngine.h"
#endif
#if ALSA
#include "AlsaEngine.h"
@@ -36,6 +37,7 @@ EngineMgr::EngineMgr()
engines.push_back(defaultEng);
#if OSS
engines.push_back(new OssEngine());
+ engines.push_back(new OssMultiEngine());
#endif
#if ALSA
engines.push_back(new AlsaEngine());
diff --git a/src/Nio/OssMultiEngine.cpp b/src/Nio/OssMultiEngine.cpp
@@ -0,0 +1,294 @@
+/*
+ ZynAddSubFX - a software synthesizer
+
+ OssMultiEngine.cpp - Multi channel audio output for Open Sound System
+ Copyright (C) 2014 Hans Petter Selasky
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License
+ as published by the Free Software Foundation.
+
+ 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 (version 2 or later) for more details.
+
+ You should have received a copy of the GNU General Public License (version 2)
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#include <cstring>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/soundcard.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <iostream>
+#include <signal.h>
+
+#include "Nio.h"
+#include "../Misc/Master.h"
+#include "../Misc/Part.h"
+#include "../Misc/MiddleWare.h"
+#include "../Misc/Util.h"
+
+#include "OssMultiEngine.h"
+
+extern MiddleWare *middleware;
+
+using namespace std;
+
+OssMultiEngine :: OssMultiEngine()
+{
+ /* setup variables */
+ name = "OSS-MULTI";
+ audioThread = 0;
+ handle = -1;
+ channels = 0;
+ en = false;
+ is32bit = false;
+ buffersize = 0;
+
+ /* compute worst case buffer size */
+ maxbuffersize = NUM_MIDI_PARTS * sizeof(int) * synth->buffersize * 2;
+ /* allocate buffer */
+ smps.ps32 = new int[maxbuffersize / sizeof(int)];
+ memset(smps.ps32, 0, maxbuffersize);
+}
+
+OssMultiEngine :: ~OssMultiEngine()
+{
+ Stop();
+ delete [] smps.ps32;
+}
+
+bool
+OssMultiEngine :: openAudio()
+{
+ int snd_samplerate;
+ int snd_format32;
+ int snd_format16;
+ int snd_fragment;
+ int x;
+
+ /* check if already open */
+ if (handle != -1)
+ return (true);
+
+ const char *device = getenv("DSP_DEVICE");
+ if(device == 0)
+ device = config.cfg.LinuxOSSWaveOutDev;
+
+ /* NOTE: PIPEs and FIFOs can block when opening them */
+ handle = open(device, O_WRONLY, O_NONBLOCK);
+ if (handle == -1) {
+ cerr << "ERROR - I can't open the "
+ << device << '.' << endl;
+ return (false);
+ }
+ ioctl(handle, SNDCTL_DSP_RESET, 0);
+
+ /* Figure out the correct format first */
+#if defined(AFMT_S16_NE) && defined(AFMT_S32_NE)
+ snd_format32 = AFMT_S32_NE;
+ snd_format16 = AFMT_S16_NE;
+#elif defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
+#if BYTE_ORDER == LITTLE_ENDIAN
+ snd_format32 = AFMT_S32_LE;
+ snd_format16 = AFMT_S16_LE;
+#else
+ snd_format32 = AFMT_S32_BE;
+ snd_format16 = AFMT_S16_BE;
+#endif
+#else
+ snd_format32 = AFMT_S32_LE;
+ snd_format16 = AFMT_S16_LE;
+#endif
+
+ if (ioctl(handle, SNDCTL_DSP_SETFMT, &snd_format32) == 0) {
+ is32bit = true;
+ } else if (ioctl(handle, SNDCTL_DSP_SETFMT, &snd_format16) == 0) {
+ is32bit = false;
+ } else {
+ cerr << "ERROR - Cannot set DSP format for "
+ << device << '.' << endl;
+ goto error;
+ }
+ for (x = NUM_MIDI_PARTS * 2; x >= 2; x -= 2) {
+ if (ioctl(handle, SNDCTL_DSP_CHANNELS, &x) == 0)
+ break;
+ }
+ if (x == 0) {
+ cerr << "ERROR - Cannot set DSP channels for "
+ << device << '.' << endl;
+ goto error;
+ }
+ channels = x;
+
+ snd_samplerate = synth->samplerate;
+
+ ioctl(handle, SNDCTL_DSP_SPEED, &snd_samplerate);
+
+ if (snd_samplerate != (int)synth->samplerate) {
+ cerr << "ERROR - Cannot set samplerate for "
+ << device << ". " << snd_samplerate
+ << " != " << synth->samplerate << endl;
+ goto error;
+ }
+
+ /* compute buffer size for 16-bit samples */
+ buffersize = 2 * synth->buffersize * channels;
+ if (is32bit)
+ buffersize *= 2;
+
+ for (x = 4; x < 20; x++) {
+ if ((1 << x) >= buffersize)
+ break;
+ }
+
+ snd_fragment = 0x20000 | x; /* 2x buffer */
+
+ ioctl(handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_create(&audioThread, &attr, _audioThreadCb, this);
+
+ return (true);
+
+error:
+ close(handle);
+ handle = -1;
+ return (false);
+}
+
+void
+OssMultiEngine :: stopAudio()
+{
+ int fd = handle;
+
+ /* check if already closed */
+ if (fd == -1)
+ return;
+ handle = -1;
+
+ /* close handle first, so that write() exits */
+ close(fd);
+
+ pthread_join(audioThread, 0);
+ audioThread = 0;
+}
+
+bool
+OssMultiEngine :: Start()
+{
+ return (openAudio());
+}
+
+void
+OssMultiEngine :: Stop()
+{
+ stopAudio();
+}
+
+void
+OssMultiEngine :: setAudioEn(bool enable)
+{
+ if (enable)
+ openAudio();
+ else
+ stopAudio();
+}
+
+bool
+OssMultiEngine :: getAudioEn() const
+{
+ return (handle != -1);
+}
+
+void *
+OssMultiEngine :: _audioThreadCb(void *arg)
+{
+ return (static_cast<OssMultiEngine *>(arg))->audioThreadCb();
+}
+
+void *
+OssMultiEngine :: audioThreadCb()
+{
+ /*
+ * In case the audio device is a PIPE/FIFO, we need to ignore
+ * any PIPE signals:
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ set_realtime();
+
+ while(getAudioEn()) {
+ int error;
+ float l;
+ float r;
+ int x;
+ int y;
+
+ /* get next buffer */
+ getNext();
+
+ /* extract audio from the "channels / 2" first parts */
+ for (x = 0; x != channels; x += 2) {
+ Part *part = middleware->spawnMaster()->part[x / 2];
+
+ if (is32bit) {
+ for (y = 0; y != synth->buffersize; y++) {
+ l = part->partoutl[y];
+ if (l < -1.0f)
+ l = -1.0f;
+ else if (l > 1.0f)
+ l = 1.0f;
+ smps.ps32[y * channels + x] = (int)(l * 2147483647.0f);
+ r = part->partoutr[y];
+ if (r < -1.0f)
+ r = -1.0f;
+ else if (r > 1.0f)
+ r = 1.0f;
+ smps.ps32[y * channels + x + 1] = (int)(r * 2147483647.0f);
+ }
+ } else {
+ for (y = 0; y != synth->buffersize; y++) {
+ l = part->partoutl[y];
+ if (l < -1.0f)
+ l = -1.0f;
+ else if (l > 1.0f)
+ l = 1.0f;
+ smps.ps16[y * channels + x] = (short int)(l * 32767.0f);
+ r = part->partoutr[y];
+ if (r < -1.0f)
+ r = -1.0f;
+ else if (r > 1.0f)
+ r = 1.0f;
+ smps.ps16[y * channels + x + 1] = (short int)(r * 32767.0f);
+ }
+ }
+ }
+
+ /* write audio buffer to DSP device */
+ do {
+ /* make a copy of handle, in case of OSS audio disable */
+ int fd = handle;
+ if (fd == -1)
+ goto done;
+ error = write(fd, smps.ps32, buffersize);
+ } while (error == -1 && errno == EINTR);
+
+ if(error == -1)
+ goto done;
+ }
+done:
+ pthread_exit(0);
+ return (0);
+}
diff --git a/src/Nio/OssMultiEngine.h b/src/Nio/OssMultiEngine.h
@@ -0,0 +1,67 @@
+/*
+ ZynAddSubFX - a software synthesizer
+
+ OssMultiEngine.h - Multi channel audio output for Open Sound System
+ Copyright (C) 2014 Hans Petter Selasky
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License
+ as published by the Free Software Foundation.
+
+ 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 (version 2 or later) for more details.
+
+ You should have received a copy of the GNU General Public License (version 2)
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef OSS_MULTI_ENGINE_H
+#define OSS_MULTI_ENGINE_H
+
+#include <sys/time.h>
+#include "../globals.h"
+#include "AudioOut.h"
+
+class OssMultiEngine : public AudioOut
+{
+public:
+ OssMultiEngine();
+ ~OssMultiEngine();
+
+ bool Start();
+ void Stop();
+
+ void setAudioEn(bool nval);
+ bool getAudioEn() const;
+
+protected:
+ void *audioThreadCb();
+ static void *_audioThreadCb(void *arg);
+
+private:
+ pthread_t audioThread;
+
+ /* Audio */
+ bool openAudio();
+ void stopAudio();
+
+ int handle;
+ int maxbuffersize;
+ int buffersize;
+ int channels;
+
+ union {
+ /* Samples to be sent to soundcard */
+ short int *ps16;
+ int *ps32;
+ } smps;
+
+ bool en;
+ bool is32bit;
+};
+
+#endif