|
- /*
- 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 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.
- */
-
- #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"
- #include "Compressor.h"
-
- using namespace std;
-
- extern zyncarla::MiddleWare *middleware;
-
- namespace zyncarla {
-
- OssMultiEngine :: OssMultiEngine(const SYNTH_T &synth,
- const oss_devs_t &oss_devs)
- :AudioOut(synth),
- linux_oss_wave_out_dev(oss_devs.linux_wave_out)
- {
- /* 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);
-
- /* setup compressor */
- unsigned peaksize = NUM_MIDI_PARTS * sizeof(float);
- peaks = new float[peaksize / sizeof(float)];
- memset(peaks, 0, peaksize);
- }
-
- OssMultiEngine :: ~OssMultiEngine()
- {
- Stop();
- delete [] smps.ps32;
- delete [] peaks;
- }
-
- bool
- OssMultiEngine :: openAudio()
- {
- int snd_samplerate;
- int snd_fragment;
- int x;
-
- /* check if already open */
- if (handle != -1)
- return (true);
-
- const char *device = getenv("DSP_DEVICE");
- if(device == 0)
- device = linux_oss_wave_out_dev;
-
- /* 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 */
- int snd_format16 = AFMT_S16_NE;
-
- #ifdef AFMT_S32_NE
- int snd_format32 = AFMT_S32_NE;
- if (ioctl(handle, SNDCTL_DSP_SETFMT, &snd_format32) == 0) {
- is32bit = true;
- } else
- #endif
- 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;
- 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++) {
- float l = part->partoutl[y];
- float r = part->partoutr[y];
- stereoCompressor(synth.samplerate, peaks[x/2], l, r);
- smps.ps32[y * channels + x] = (int)(l * 2147483647.0f);
- smps.ps32[y * channels + x + 1] = (int)(r * 2147483647.0f);
- }
- } else {
- for (y = 0; y != synth.buffersize; y++) {
- float l = part->partoutl[y];
- float r = part->partoutr[y];
- stereoCompressor(synth.samplerate, peaks[x/2], l, r);
- smps.ps16[y * channels + x] = (short int)(l * 32767.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);
- }
-
- }
|