AudioPortAudioTarget.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #ifdef HAVE_PORTAUDIO
00017 
00018 #include "AudioPortAudioTarget.h"
00019 #include "AudioCallbackPlaySource.h"
00020 
00021 #include <iostream>
00022 #include <cassert>
00023 #include <cmath>
00024 
00025 //#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1
00026 
00027 AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) :
00028     AudioCallbackPlayTarget(source),
00029     m_stream(0),
00030     m_bufferSize(0),
00031     m_sampleRate(0),
00032     m_latency(0),
00033     m_done(false)
00034 {
00035     PaError err;
00036 
00037 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
00038 #ifdef HAVE_PORTAUDIO_V18
00039     std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v18" << std::endl;
00040 #else
00041     std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
00042 #endif
00043 #endif
00044 
00045     err = Pa_Initialize();
00046     if (err != paNoError) {
00047         std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
00048         return;
00049     }
00050 
00051     m_bufferSize = 2048;
00052     m_sampleRate = 44100;
00053     if (m_source && (m_source->getSourceSampleRate() != 0)) {
00054         m_sampleRate = m_source->getSourceSampleRate();
00055     }
00056 
00057 #ifdef HAVE_PORTAUDIO_V18
00058     m_latency = Pa_GetMinNumBuffers(m_bufferSize, m_sampleRate) * m_bufferSize;
00059 #endif
00060 
00061 #ifdef HAVE_PORTAUDIO_V18
00062     err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
00063                                m_sampleRate, m_bufferSize, 0,
00064                                processStatic, this);
00065 #else
00066     PaStreamParameters op;
00067     op.device = Pa_GetDefaultOutputDevice();
00068     op.channelCount = 2;
00069     op.sampleFormat = paFloat32;
00070     op.suggestedLatency = 0.2;
00071     op.hostApiSpecificStreamInfo = 0;
00072     err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
00073                         paFramesPerBufferUnspecified,
00074                         paNoFlag, processStatic, this);
00075 #endif    
00076 
00077 #ifndef HAVE_PORTAUDIO_V18
00078     if (err != paNoError) {
00079 
00080         std::cerr << "WARNING: AudioPortAudioTarget: Failed to open PortAudio stream with default frames per buffer, trying again with fixed frames per buffer..." << std::endl;
00081         
00082         err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
00083                             1024,
00084                             paNoFlag, processStatic, this);
00085         m_bufferSize = 1024;
00086     }
00087 #endif
00088 
00089     if (err != paNoError) {
00090         std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
00091         m_stream = 0;
00092         Pa_Terminate();
00093         return;
00094     }
00095 
00096 #ifndef HAVE_PORTAUDIO_V18
00097     const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
00098     m_latency = int(info->outputLatency * m_sampleRate + 0.001);
00099     if (m_bufferSize < m_latency) m_bufferSize = m_latency;
00100 #endif
00101 
00102     std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
00103 
00104     err = Pa_StartStream(m_stream);
00105 
00106     if (err != paNoError) {
00107         std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
00108         Pa_CloseStream(m_stream);
00109         m_stream = 0;
00110         Pa_Terminate();
00111         return;
00112     }
00113 
00114     if (m_source) {
00115         std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
00116         m_source->setTarget(this, m_bufferSize);
00117         m_source->setTargetSampleRate(m_sampleRate);
00118         m_source->setTargetPlayLatency(m_latency);
00119     }
00120 
00121 #ifdef DEBUG_PORT_AUDIO_TARGET
00122     std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
00123 #endif
00124 }
00125 
00126 AudioPortAudioTarget::~AudioPortAudioTarget()
00127 {
00128     std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget()" << std::endl;
00129 
00130     if (m_source) {
00131         m_source->setTarget(0, m_bufferSize);
00132     }
00133 
00134     shutdown();
00135 
00136     if (m_stream) {
00137 
00138         std::cerr << "closing stream" << std::endl;
00139 
00140         PaError err;
00141         err = Pa_CloseStream(m_stream);
00142         if (err != paNoError) {
00143             std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
00144         }
00145 
00146         std::cerr << "terminating" << std::endl;
00147 
00148         err = Pa_Terminate();
00149         if (err != paNoError) {
00150             std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
00151         }   
00152     }
00153 
00154     m_stream = 0;
00155 
00156     std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget() done" << std::endl;
00157 }
00158 
00159 void 
00160 AudioPortAudioTarget::shutdown()
00161 {
00162     m_done = true;
00163 }
00164 
00165 bool
00166 AudioPortAudioTarget::isOK() const
00167 {
00168     return (m_stream != 0);
00169 }
00170 
00171 double
00172 AudioPortAudioTarget::getCurrentTime() const
00173 {
00174     if (!m_stream) return 0.0;
00175     else return Pa_GetStreamTime(m_stream);
00176 }
00177 
00178 #ifdef HAVE_PORTAUDIO_V18
00179 int
00180 AudioPortAudioTarget::processStatic(void *input, void *output,
00181                                     unsigned long nframes,
00182                                     PaTimestamp outTime, void *data)
00183 {
00184     return ((AudioPortAudioTarget *)data)->process(input, output,
00185                                                    nframes, outTime);
00186 }
00187 #else
00188 int
00189 AudioPortAudioTarget::processStatic(const void *input, void *output,
00190                                     unsigned long nframes,
00191                                     const PaStreamCallbackTimeInfo *timeInfo,
00192                                     PaStreamCallbackFlags flags, void *data)
00193 {
00194     return ((AudioPortAudioTarget *)data)->process(input, output,
00195                                                    nframes, timeInfo,
00196                                                    flags);
00197 }
00198 #endif
00199 
00200 void
00201 AudioPortAudioTarget::sourceModelReplaced()
00202 {
00203     m_source->setTargetSampleRate(m_sampleRate);
00204 }
00205 
00206 #ifdef HAVE_PORTAUDIO_V18
00207 int
00208 AudioPortAudioTarget::process(void *inputBuffer, void *outputBuffer,
00209                               unsigned long nframes,
00210                               PaTimestamp)
00211 #else
00212 int
00213 AudioPortAudioTarget::process(const void *, void *outputBuffer,
00214                               unsigned long nframes,
00215                               const PaStreamCallbackTimeInfo *,
00216                               PaStreamCallbackFlags)
00217 #endif
00218 {
00219 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET    
00220     std::cout << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
00221 #endif
00222 
00223     if (!m_source || m_done) return 0;
00224 
00225     float *output = (float *)outputBuffer;
00226 
00227     assert(nframes <= m_bufferSize);
00228 
00229     static float **tmpbuf = 0;
00230     static size_t tmpbufch = 0;
00231     static size_t tmpbufsz = 0;
00232 
00233     size_t sourceChannels = m_source->getSourceChannelCount();
00234 
00235     // Because we offer pan, we always want at least 2 channels
00236     if (sourceChannels < 2) sourceChannels = 2;
00237 
00238     if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
00239 
00240         if (tmpbuf) {
00241             for (size_t i = 0; i < tmpbufch; ++i) {
00242                 delete[] tmpbuf[i];
00243             }
00244             delete[] tmpbuf;
00245         }
00246 
00247         tmpbufch = sourceChannels;
00248         tmpbufsz = m_bufferSize;
00249         tmpbuf = new float *[tmpbufch];
00250 
00251         for (size_t i = 0; i < tmpbufch; ++i) {
00252             tmpbuf[i] = new float[tmpbufsz];
00253         }
00254     }
00255         
00256     size_t received = m_source->getSourceSamples(nframes, tmpbuf);
00257 
00258     float peakLeft = 0.0, peakRight = 0.0;
00259 
00260     for (size_t ch = 0; ch < 2; ++ch) {
00261         
00262         float peak = 0.0;
00263 
00264         if (ch < sourceChannels) {
00265 
00266             // PortAudio samples are interleaved
00267             for (size_t i = 0; i < nframes; ++i) {
00268                 if (i < received) {
00269                     output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
00270                     float sample = fabsf(output[i * 2 + ch]);
00271                     if (sample > peak) peak = sample;
00272                 } else {
00273                     output[i * 2 + ch] = 0;
00274                 }
00275             }
00276 
00277         } else if (ch == 1 && sourceChannels == 1) {
00278 
00279             for (size_t i = 0; i < nframes; ++i) {
00280                 if (i < received) {
00281                     output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
00282                     float sample = fabsf(output[i * 2 + ch]);
00283                     if (sample > peak) peak = sample;
00284                 } else {
00285                     output[i * 2 + ch] = 0;
00286                 }
00287             }
00288 
00289         } else {
00290             for (size_t i = 0; i < nframes; ++i) {
00291                 output[i * 2 + ch] = 0;
00292             }
00293         }
00294 
00295         if (ch == 0) peakLeft = peak;
00296         if (ch > 0 || sourceChannels == 1) peakRight = peak;
00297     }
00298 
00299     m_source->setOutputLevels(peakLeft, peakRight);
00300 
00301     return 0;
00302 }
00303 
00304 #endif /* HAVE_PORTAUDIO */
00305 

Generated on Wed Feb 20 15:45:25 2008 for SonicVisualiser by  doxygen 1.5.1