svapp  1.9
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_2_0
00017 
00018 #include "AudioPortAudioTarget.h"
00019 #include "AudioCallbackPlaySource.h"
00020 
00021 #include <iostream>
00022 #include <cassert>
00023 #include <cmath>
00024 
00025 #ifndef _WIN32
00026 #include <pthread.h>
00027 #endif
00028 
00029 //#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1
00030 
00031 AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) :
00032     AudioCallbackPlayTarget(source),
00033     m_stream(0),
00034     m_bufferSize(0),
00035     m_sampleRate(0),
00036     m_latency(0),
00037     m_prioritySet(false),
00038     m_done(false)
00039 {
00040     PaError err;
00041 
00042 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
00043     cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << endl;
00044 #endif
00045 
00046     err = Pa_Initialize();
00047     if (err != paNoError) {
00048         cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << endl;
00049         return;
00050     }
00051 
00052     m_bufferSize = 2048;
00053     m_sampleRate = 44100;
00054     if (m_source && (m_source->getSourceSampleRate() != 0)) {
00055         m_sampleRate = m_source->getSourceSampleRate();
00056     }
00057 
00058     PaStreamParameters op;
00059     op.device = Pa_GetDefaultOutputDevice();
00060     op.channelCount = 2;
00061     op.sampleFormat = paFloat32;
00062     op.suggestedLatency = 0.2;
00063     op.hostApiSpecificStreamInfo = 0;
00064     err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
00065                         paFramesPerBufferUnspecified,
00066                         paNoFlag, processStatic, this);
00067 
00068     if (err != paNoError) {
00069 
00070         cerr << "WARNING: AudioPortAudioTarget: Failed to open PortAudio stream with default frames per buffer, trying again with fixed frames per buffer..." << endl;
00071         
00072         err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
00073                             1024,
00074                             paNoFlag, processStatic, this);
00075         m_bufferSize = 1024;
00076     }
00077 
00078     if (err != paNoError) {
00079         cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << endl;
00080         cerr << "Note: device ID was " << op.device << endl;
00081         m_stream = 0;
00082         Pa_Terminate();
00083         return;
00084     }
00085 
00086     const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
00087     m_latency = int(info->outputLatency * m_sampleRate + 0.001);
00088     if (m_bufferSize < m_latency) m_bufferSize = m_latency;
00089 
00090     cerr << "PortAudio latency = " << m_latency << " frames" << endl;
00091 
00092     err = Pa_StartStream(m_stream);
00093 
00094     if (err != paNoError) {
00095         cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << endl;
00096         Pa_CloseStream(m_stream);
00097         m_stream = 0;
00098         Pa_Terminate();
00099         return;
00100     }
00101 
00102     if (m_source) {
00103         cerr << "AudioPortAudioTarget: block size " << m_bufferSize << endl;
00104         m_source->setTarget(this, m_bufferSize);
00105         m_source->setTargetSampleRate(m_sampleRate);
00106         m_source->setTargetPlayLatency(m_latency);
00107     }
00108 
00109 #ifdef DEBUG_PORT_AUDIO_TARGET
00110     cerr << "AudioPortAudioTarget: initialised OK" << endl;
00111 #endif
00112 }
00113 
00114 AudioPortAudioTarget::~AudioPortAudioTarget()
00115 {
00116     SVDEBUG << "AudioPortAudioTarget::~AudioPortAudioTarget()" << endl;
00117 
00118     if (m_source) {
00119         m_source->setTarget(0, m_bufferSize);
00120     }
00121 
00122     shutdown();
00123 
00124     if (m_stream) {
00125 
00126         SVDEBUG << "closing stream" << endl;
00127 
00128         PaError err;
00129         err = Pa_CloseStream(m_stream);
00130         if (err != paNoError) {
00131             cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << endl;
00132         }
00133 
00134         cerr << "terminating" << endl;
00135 
00136         err = Pa_Terminate();
00137         if (err != paNoError) {
00138             cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << endl;
00139         }   
00140     }
00141 
00142     m_stream = 0;
00143 
00144     SVDEBUG << "AudioPortAudioTarget::~AudioPortAudioTarget() done" << endl;
00145 }
00146 
00147 void 
00148 AudioPortAudioTarget::shutdown()
00149 {
00150 #ifdef DEBUG_PORT_AUDIO_TARGET
00151     SVDEBUG << "AudioPortAudioTarget::shutdown" << endl;
00152 #endif
00153     m_done = true;
00154 }
00155 
00156 bool
00157 AudioPortAudioTarget::isOK() const
00158 {
00159     return (m_stream != 0);
00160 }
00161 
00162 double
00163 AudioPortAudioTarget::getCurrentTime() const
00164 {
00165     if (!m_stream) return 0.0;
00166     else return Pa_GetStreamTime(m_stream);
00167 }
00168 
00169 int
00170 AudioPortAudioTarget::processStatic(const void *input, void *output,
00171                                     unsigned long nframes,
00172                                     const PaStreamCallbackTimeInfo *timeInfo,
00173                                     PaStreamCallbackFlags flags, void *data)
00174 {
00175     return ((AudioPortAudioTarget *)data)->process(input, output,
00176                                                    nframes, timeInfo,
00177                                                    flags);
00178 }
00179 
00180 void
00181 AudioPortAudioTarget::sourceModelReplaced()
00182 {
00183     m_source->setTargetSampleRate(m_sampleRate);
00184 }
00185 
00186 int
00187 AudioPortAudioTarget::process(const void *, void *outputBuffer,
00188                               int nframes,
00189                               const PaStreamCallbackTimeInfo *,
00190                               PaStreamCallbackFlags)
00191 {
00192 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET    
00193     SVDEBUG << "AudioPortAudioTarget::process(" << nframes << ")" << endl;
00194 #endif
00195 
00196     if (!m_source || m_done) {
00197 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
00198         SVDEBUG << "AudioPortAudioTarget::process: Doing nothing, no source or application done" << endl;
00199 #endif
00200         return 0;
00201     }
00202 
00203     if (!m_prioritySet) {
00204 #ifndef _WIN32
00205         sched_param param;
00206         param.sched_priority = 20;
00207         if (pthread_setschedparam(pthread_self(), SCHED_RR, &param)) {
00208             SVDEBUG << "AudioPortAudioTarget: NOTE: couldn't set RT scheduling class" << endl;
00209         } else {
00210             SVDEBUG << "AudioPortAudioTarget: NOTE: successfully set RT scheduling class" << endl;
00211         }
00212 #endif
00213         m_prioritySet = true;
00214     }
00215 
00216     float *output = (float *)outputBuffer;
00217 
00218     assert(nframes <= m_bufferSize);
00219 
00220     static float **tmpbuf = 0;
00221     static int tmpbufch = 0;
00222     static int tmpbufsz = 0;
00223 
00224     int sourceChannels = m_source->getSourceChannelCount();
00225 
00226     // Because we offer pan, we always want at least 2 channels
00227     if (sourceChannels < 2) sourceChannels = 2;
00228 
00229     if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
00230 
00231         if (tmpbuf) {
00232             for (int i = 0; i < tmpbufch; ++i) {
00233                 delete[] tmpbuf[i];
00234             }
00235             delete[] tmpbuf;
00236         }
00237 
00238         tmpbufch = sourceChannels;
00239         tmpbufsz = m_bufferSize;
00240         tmpbuf = new float *[tmpbufch];
00241 
00242         for (int i = 0; i < tmpbufch; ++i) {
00243             tmpbuf[i] = new float[tmpbufsz];
00244         }
00245     }
00246         
00247     int received = m_source->getSourceSamples(nframes, tmpbuf);
00248 
00249     float peakLeft = 0.0, peakRight = 0.0;
00250 
00251     for (int ch = 0; ch < 2; ++ch) {
00252         
00253         float peak = 0.0;
00254 
00255         if (ch < sourceChannels) {
00256 
00257             // PortAudio samples are interleaved
00258             for (int i = 0; i < nframes; ++i) {
00259                 if (i < received) {
00260                     output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
00261                     float sample = fabsf(output[i * 2 + ch]);
00262                     if (sample > peak) peak = sample;
00263                 } else {
00264                     output[i * 2 + ch] = 0;
00265                 }
00266             }
00267 
00268         } else if (ch == 1 && sourceChannels == 1) {
00269 
00270             for (int i = 0; i < nframes; ++i) {
00271                 if (i < received) {
00272                     output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
00273                     float sample = fabsf(output[i * 2 + ch]);
00274                     if (sample > peak) peak = sample;
00275                 } else {
00276                     output[i * 2 + ch] = 0;
00277                 }
00278             }
00279 
00280         } else {
00281             for (int i = 0; i < nframes; ++i) {
00282                 output[i * 2 + ch] = 0;
00283             }
00284         }
00285 
00286         if (ch == 0) peakLeft = peak;
00287         if (ch > 0 || sourceChannels == 1) peakRight = peak;
00288     }
00289 
00290     m_source->setOutputLevels(peakLeft, peakRight);
00291 
00292     if (Pa_GetStreamCpuLoad(m_stream) > 0.7) {
00293         if (m_source) m_source->audioProcessingOverload();
00294     }
00295 
00296     return 0;
00297 }
00298 
00299 #endif /* HAVE_PORTAUDIO */
00300