📄 audiolayer.cpp
字号:
/* * Copyright (C) 2005 Savoir-Faire Linux inc. * Author: Yan Morin <yan.morin@savoirfairelinux.com> * Author: Jerome Oufella <jerome.oufella@savoirfairelinux.com> * * 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; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <stdio.h>#include <stdlib.h>#include "audiolayer.h"#include "../global.h"#include "../manager.h"//#define SFL_TEST//#define SFL_TEST_SINE#ifdef SFL_TEST_SINE#include <cmath>#endifAudioLayer::AudioLayer(ManagerImpl* manager) : _urgentRingBuffer(SIZEBUF) , _mainSndRingBuffer(SIZEBUF) , _micRingBuffer(SIZEBUF) , _stream(NULL) , _errorMessage("") , _manager(manager){ _sampleRate = 8000; _inChannel = 1; // don't put in stereo _outChannel = 1; // don't put in stereo _echoTesting = false; try { portaudio::AutoSystem autoSys; portaudio::System::initialize(); } catch (const portaudio::PaException &e) { setErrorMessage(e.paErrorText()); } catch (const portaudio::PaCppException &e) { setErrorMessage(e.what()); } // std::runtime_error &e (e.what()) catch (...) { setErrorMessage("Unknown type error in portaudio initialization"); }#ifdef SFL_TEST_SINE leftPhase_ = 0; tableSize_ = 200; const double PI = 3.14159265; table_ = new float[tableSize_]; for (int i = 0; i < tableSize_; ++i) { table_[i] = 0.125f * (float)sin(((double)i/(double)tableSize_)*PI*2.); _debug("%9.8f\n", table_[i]); }#endif}// DestructorAudioLayer::~AudioLayer (void) { stopStream(); closeStream(); try { portaudio::System::terminate(); } catch (const portaudio::PaException &e) { _debug("! AL: Catch an exception when portaudio tried to terminate\n"); }#ifdef SFL_TEST_SINE delete [] table_;#endif}voidAudioLayer::closeStream (void) { ost::MutexLock guard(_mutex); if(_stream) { _stream->close(); delete _stream; _stream = NULL; }}boolAudioLayer::hasStream(void) { ost::MutexLock guard(_mutex); return (_stream!=0 ? true : false); }voidAudioLayer::openDevice (int indexIn, int indexOut, int sampleRate) { closeStream(); _sampleRate = sampleRate; int portaudioFramePerBuffer = FRAME_PER_BUFFER; //=FRAME_PER_BUFFER; //= paFramesPerBufferUnspecified; int nbDevice = getDeviceCount(); if (nbDevice == 0) { _debug("Portaudio detect no sound card."); return; } else { if (indexIn >= nbDevice) { _debug(" Portaudio auto-select device #0 for input because device #%02d is not found\n", indexIn); indexIn = 0; } if (indexOut >= nbDevice) { _debug(" Portaudio auto-select device #0 for output because device #%02d is not found\n", indexOut); indexOut = 0; } _debug(" Setting audiolayer: device in=%2d, out=%2d\n", indexIn, indexOut); _debug(" : nb channel in=%2d, out=%2d\n", _inChannel, _outChannel); _debug(" : sample rate=%5d, format=%s\n", _sampleRate, SFLPortaudioFormatString); _debug(" : frame per buffer=%d\n", portaudioFramePerBuffer); } try { // Set up the parameters required to open a (Callback)Stream: portaudio::DirectionSpecificStreamParameters inParams(portaudio::System::instance().deviceByIndex(indexIn), _inChannel, SFLPortaudioFormat, true, portaudio::System::instance().deviceByIndex(indexIn).defaultLowInputLatency(), NULL); portaudio::DirectionSpecificStreamParameters outParams( portaudio::System::instance().deviceByIndex(indexOut), _outChannel, SFLPortaudioFormat, true, portaudio::System::instance().deviceByIndex(indexOut).defaultLowOutputLatency(), NULL); // like audacity // DON'T USE paFramesPerBufferUnspecified, it's 32, instead of 160 for FRAME_PER_BUFFER // DON'T USE paDitherOff or paClipOff, // FRAME_PER_BUFFER | paFramesPerBufferUnspecified // paNoFlag | paClipOff || paDitherOff | paPrimeOutputBuffersUsingStreamCallback | paNeverDropInput portaudio::StreamParameters const params( inParams, outParams, _sampleRate, portaudioFramePerBuffer, paClipOff); // Create (and open) a new Stream, using the AudioLayer::audioCallback ost::MutexLock guard(_mutex);#ifdef SFL_TEST _stream = new portaudio::MemFunCallbackStream<AudioLayer>(params, *this, &AudioLayer::miniAudioCallback);#else _stream = new portaudio::MemFunCallbackStream<AudioLayer>(params,*this, &AudioLayer::audioCallback);#endif } catch (const portaudio::PaException &e) { setErrorMessage(e.paErrorText()); _debug("Portaudio openDevice error: %s\n", e.paErrorText()); } catch (const portaudio::PaCppException &e) { setErrorMessage(e.what()); _debug("Portaudio openDevice error: %s\n", e.what()); } // std::runtime_error &e (e.what()) catch (...) { setErrorMessage("Unknown type error in portaudio openDevice"); _debug("Portaudio openDevice: unknown error\n"); }}intAudioLayer::getDeviceCount(){ return portaudio::System::instance().deviceCount();}AudioDevice*AudioLayer::getAudioDeviceInfo(int index, int ioDeviceMask){ try { portaudio::System& sys = portaudio::System::instance(); portaudio::Device& device = sys.deviceByIndex(index); int deviceIsSupported = false; if (ioDeviceMask == InputDevice && !device.isOutputOnlyDevice()) { deviceIsSupported = true; } else if (ioDeviceMask == OutputDevice && !device.isInputOnlyDevice()) { deviceIsSupported = true; } else if (device.isFullDuplexDevice()) { deviceIsSupported = true; } if (deviceIsSupported) { AudioDevice* audiodevice = new AudioDevice(index, device.hostApi().name(), device.name()); if (audiodevice) { audiodevice->setRate(device.defaultSampleRate()); } return audiodevice; } } catch (...) { return 0; } return 0;}voidAudioLayer::startStream(void) { try { ost::MutexLock guard(_mutex); if (_stream && !_stream->isActive()) { _debug("- AL Action: Starting sound stream\n"); _stream->start(); } else { _debug ("* AL Info: Stream doesn't exist or is already active\n"); } } catch (const portaudio::PaException &e) { _debugException("! AL: Portaudio error: error on starting audiolayer stream"); throw; } catch(...) { _debugException("! AL: Stream start error"); throw; }} voidAudioLayer::stopStream(void) { _debug("- AL Action: Stopping sound stream\n"); try { ost::MutexLock guard(_mutex); if (_stream && !_stream->isStopped()) { _stream->stop(); _mainSndRingBuffer.flush(); _urgentRingBuffer.flush(); _micRingBuffer.flush(); } } catch (const portaudio::PaException &e) { _debugException("! AL: Portaudio error: stoping audiolayer stream failed"); throw; } catch(...) { _debugException("! AL: Stream stop error"); throw; }}voidAudioLayer::sleep(int msec) { if (_stream) { portaudio::System::instance().sleep(msec); }}boolAudioLayer::isStreamActive (void) { ost::MutexLock guard(_mutex); try { if(_stream && _stream->isActive()) { return true; } } catch (const portaudio::PaException &e) { _debugException("! AL: Portaudio error: isActive returned an error"); } return false;}int AudioLayer::putMain(void* buffer, int toCopy){ ost::MutexLock guard(_mutex); if (_stream) { int a = _mainSndRingBuffer.AvailForPut(); if ( a >= toCopy ) { return _mainSndRingBuffer.Put(buffer, toCopy); } else { return _mainSndRingBuffer.Put(buffer, a); } } return 0;}voidAudioLayer::flushMain(){ ost::MutexLock guard(_mutex); _mainSndRingBuffer.flush();}intAudioLayer::putUrgent(void* buffer, int toCopy){ ost::MutexLock guard(_mutex); if (_stream) { int a = _urgentRingBuffer.AvailForPut(); if ( a >= toCopy ) { return _urgentRingBuffer.Put(buffer, toCopy); } else { return _urgentRingBuffer.Put(buffer, a); } } return 0;}intAudioLayer::canGetMic(){ if (_stream) { return _micRingBuffer.AvailForGet(); } else { return 0; }}int AudioLayer::getMic(void *buffer, int toCopy){ if(_stream) { return _micRingBuffer.Get(buffer, toCopy, 100); } else { return 0; }}voidAudioLayer::flushMic(){ _micRingBuffer.flush();}boolAudioLayer::isStreamStopped (void) { ost::MutexLock guard(_mutex); try { if(_stream && _stream->isStopped()) { return true; } } catch (const portaudio::PaException &e) { _debugException("! AL: Portaudio error: isStopped returned an exception"); } return false;}voidAudioLayer::toggleEchoTesting() { ost::MutexLock guard(_mutex); _echoTesting = (_echoTesting == true) ? false : true;}int AudioLayer::audioCallback (const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) { (void) timeInfo; (void) statusFlags; SFLDataFormat *in = (SFLDataFormat *) inputBuffer; SFLDataFormat *out = (SFLDataFormat *) outputBuffer; if (_echoTesting) { memcpy(out, in, framesPerBuffer*sizeof(SFLDataFormat)); return paContinue; } int toGet; int toPut; int urgentAvail; // number of data right and data left int normalAvail; // number of data right and data left int micAvailPut; unsigned short spkrVolume = _manager->getSpkrVolume(); unsigned short micVolume = _manager->getMicVolume(); // AvailForGet tell the number of chars inside the buffer // framePerBuffer are the number of data for one channel (left) urgentAvail = _urgentRingBuffer.AvailForGet(); if (urgentAvail > 0) { // Urgent data (dtmf, incoming call signal) come first. toGet = (urgentAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBuffer * sizeof(SFLDataFormat); _urgentRingBuffer.Get(out, toGet, spkrVolume); // Consume the regular one as well (same amount of bytes) _mainSndRingBuffer.Discard(toGet); } else { AudioLoop* tone = _manager->getTelephoneTone(); if ( tone != 0) { tone->getNext(out, framesPerBuffer, spkrVolume); } else if ( (tone=_manager->getTelephoneFile()) != 0 ) { tone->getNext(out, framesPerBuffer, spkrVolume); } else { // If nothing urgent, play the regular sound samples normalAvail = _mainSndRingBuffer.AvailForGet(); toGet = (normalAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? normalAvail : framesPerBuffer * sizeof(SFLDataFormat); if (toGet) { _mainSndRingBuffer.Get(out, toGet, spkrVolume); } else { bzero(out, framesPerBuffer * sizeof(SFLDataFormat)); } } } // Additionally handle the mic's audio stream micAvailPut = _micRingBuffer.AvailForPut(); toPut = (micAvailPut <= (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? micAvailPut : framesPerBuffer * sizeof(SFLDataFormat); //_debug("AL: Nb sample: %d char, [0]=%f [1]=%f [2]=%f\n", toPut, in[0], in[1], in[2]); _micRingBuffer.Put(in, toPut, micVolume); return paContinue;}int AudioLayer::miniAudioCallback (const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) { (void) timeInfo; (void) statusFlags; _debug("mini audio callback!!\n");#ifdef SFL_TEST_SINE assert(outputBuffer != NULL); float *out = static_cast<float *>(outputBuffer); for (unsigned int i = 0; i < framesPerBuffer; ++i) { out[i] = table_[leftPhase_]; leftPhase_ += 1; if (leftPhase_ >= tableSize_) { leftPhase_ -= tableSize_; } }#else SFLDataFormat *out = (SFLDataFormat *) outputBuffer; AudioLoop* tone = _manager->getTelephoneTone(); if ( tone != 0) { //_debug("Frames Per Buffer: %d\n", framesPerBuffer); tone->getNext(out, framesPerBuffer, 100); }#endif return paContinue;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -