svapp
1.9
|
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, ¶m)) { 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