/* ZynAddSubFX - a software synthesizer OSSaudiooutput.C - Audio output for Open Sound System Copyright (C) 2002-2005 Nasca Octavian Paul Author: Nasca Octavian Paul 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 "OssEngine.h" #include "../Misc/Util.h" #include "../globals.h" #include #include #include #include #include #include #include #include #include #include "InMgr.h" using namespace std; OssEngine::OssEngine() :AudioOut(), engThread(NULL) { name = "OSS"; midi.handle = -1; audio.handle = -1; audio.smps = new short[synth->buffersize * 2]; memset(audio.smps, 0, synth->bufferbytes); } OssEngine::~OssEngine() { Stop(); delete [] audio.smps; } bool OssEngine::openAudio() { if(audio.handle != -1) return true; //already open int snd_bitsize = 16; int snd_fragment = 0x00080009; //fragment size (?); int snd_stereo = 1; //stereo; int snd_format = AFMT_S16_LE; int snd_samplerate = synth->samplerate; audio.handle = open(config.cfg.LinuxOSSWaveOutDev, O_WRONLY, 0); if(audio.handle == -1) { cerr << "ERROR - I can't open the " << config.cfg.LinuxOSSWaveOutDev << '.' << endl; return false; } ioctl(audio.handle, SNDCTL_DSP_RESET, NULL); ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format); ioctl(audio.handle, SNDCTL_DSP_STEREO, &snd_stereo); ioctl(audio.handle, SNDCTL_DSP_SPEED, &snd_samplerate); ioctl(audio.handle, SNDCTL_DSP_SAMPLESIZE, &snd_bitsize); ioctl(audio.handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment); if(!getMidiEn()) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); engThread = new pthread_t; pthread_create(engThread, &attr, _thread, this); } return true; } void OssEngine::stopAudio() { int handle = audio.handle; if(handle == -1) //already closed return; audio.handle = -1; if(!getMidiEn() && engThread) pthread_join(*engThread, NULL); delete engThread; engThread = NULL; close(handle); } bool OssEngine::Start() { bool good = true; if(!openAudio()) { cerr << "Failed to open OSS audio" << endl; good = false; } if(!openMidi()) { cerr << "Failed to open OSS midi" << endl; good = false; } return good; } void OssEngine::Stop() { stopAudio(); stopMidi(); } void OssEngine::setMidiEn(bool nval) { if(nval) openMidi(); else stopMidi(); } bool OssEngine::getMidiEn() const { return midi.handle != -1; } void OssEngine::setAudioEn(bool nval) { if(nval) openAudio(); else stopAudio(); } bool OssEngine::getAudioEn() const { return audio.handle != -1; } bool OssEngine::openMidi() { int handle = midi.handle; if(handle != -1) return true; //already open handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0); if(-1 == handle) return false; midi.handle = handle; if(!getAudioEn()) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); engThread = new pthread_t; pthread_create(engThread, &attr, _thread, this); } return true; } void OssEngine::stopMidi() { int handle = midi.handle; if(handle == -1) //already closed return; midi.handle = -1; if(!getAudioEn() && engThread) { pthread_join(*engThread, NULL); delete engThread; engThread = NULL; } close(handle); } void *OssEngine::_thread(void *arg) { return (static_cast(arg))->thread(); } void *OssEngine::thread() { unsigned char tmp[4] = {0, 0, 0, 0}; set_realtime(); while(getAudioEn() || getMidiEn()) { if(getAudioEn()) { const Stereo smps = getNext(); float l, r; for(int i = 0; i < synth->buffersize; ++i) { l = smps.l[i]; r = smps.r[i]; if(l < -1.0f) l = -1.0f; else if(l > 1.0f) l = 1.0f; if(r < -1.0f) r = -1.0f; else if(r > 1.0f) r = 1.0f; audio.smps[i * 2] = (short int) (l * 32767.0f); audio.smps[i * 2 + 1] = (short int) (r * 32767.0f); } int handle = audio.handle; if(handle != -1) write(handle, audio.smps, synth->buffersize * 4); // *2 because is 16 bit, again * 2 because is stereo else break; } //Collect up to 30 midi events for(int k = 0; k < 30 && getMidiEn(); ++k) { static char escaped; memset(tmp, 0, 4); if(escaped) { tmp[0] = escaped; escaped = 0; } else { getMidi(tmp); if(!(tmp[0] & 0x80)) continue; } getMidi(tmp + 1); if(tmp[1] & 0x80) { escaped = tmp[1]; tmp[1] = 0; } else { getMidi(tmp + 2); if(tmp[2] & 0x80) { escaped = tmp[2]; tmp[2] = 0; } else { getMidi(tmp + 3); if(tmp[3] & 0x80) { escaped = tmp[3]; tmp[3] = 0; } } } midiProcess(tmp[0], tmp[1], tmp[2]); } } pthread_exit(NULL); return NULL; } void OssEngine::getMidi(unsigned char *midiPtr) { read(midi.handle, midiPtr, 1); }