annotate audioio/AudioPortAudioTarget.cpp @ 191:3bd87e04f060

* Move query for latency and other stream attributes from contextStateChanged to streamStateChanged (they did not work previously, as they were trying to query the stream too soon)
author Chris Cannam
date Tue, 15 Jun 2010 11:36:02 +0000
parents 58b64dbb49c6
children d9c21e7bff21
rev   line source
Chris@59 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@59 2
Chris@59 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@59 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@59 14 */
Chris@59 15
Chris@114 16 #ifdef HAVE_PORTAUDIO_2_0
Chris@59 17
Chris@59 18 #include "AudioPortAudioTarget.h"
Chris@59 19 #include "AudioCallbackPlaySource.h"
Chris@59 20
Chris@59 21 #include <iostream>
Chris@59 22 #include <cassert>
Chris@59 23 #include <cmath>
Chris@59 24
Chris@182 25 #ifndef _WIN32
Chris@182 26 #include <pthread.h>
Chris@182 27 #endif
Chris@182 28
Chris@59 29 //#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1
Chris@59 30
Chris@59 31 AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) :
Chris@59 32 AudioCallbackPlayTarget(source),
Chris@59 33 m_stream(0),
Chris@59 34 m_bufferSize(0),
Chris@59 35 m_sampleRate(0),
Chris@70 36 m_latency(0),
Chris@182 37 m_prioritySet(false),
Chris@70 38 m_done(false)
Chris@59 39 {
Chris@59 40 PaError err;
Chris@59 41
Chris@59 42 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@59 43 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
Chris@59 44 #endif
Chris@59 45
Chris@59 46 err = Pa_Initialize();
Chris@59 47 if (err != paNoError) {
Chris@59 48 std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 49 return;
Chris@59 50 }
Chris@59 51
Chris@80 52 m_bufferSize = 2048;
Chris@59 53 m_sampleRate = 44100;
Chris@59 54 if (m_source && (m_source->getSourceSampleRate() != 0)) {
Chris@59 55 m_sampleRate = m_source->getSourceSampleRate();
Chris@59 56 }
Chris@59 57
Chris@81 58 PaStreamParameters op;
Chris@96 59 op.device = Pa_GetDefaultOutputDevice();
Chris@81 60 op.channelCount = 2;
Chris@81 61 op.sampleFormat = paFloat32;
Chris@182 62 op.suggestedLatency = 1.0;
Chris@81 63 op.hostApiSpecificStreamInfo = 0;
Chris@91 64 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@91 65 paFramesPerBufferUnspecified,
Chris@81 66 paNoFlag, processStatic, this);
Chris@59 67
Chris@95 68 if (err != paNoError) {
Chris@95 69
Chris@95 70 std::cerr << "WARNING: AudioPortAudioTarget: Failed to open PortAudio stream with default frames per buffer, trying again with fixed frames per buffer..." << std::endl;
Chris@95 71
Chris@95 72 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@95 73 1024,
Chris@95 74 paNoFlag, processStatic, this);
Chris@96 75 m_bufferSize = 1024;
Chris@95 76 }
Chris@95 77
Chris@59 78 if (err != paNoError) {
Chris@59 79 std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 80 m_stream = 0;
Chris@59 81 Pa_Terminate();
Chris@59 82 return;
Chris@59 83 }
Chris@59 84
Chris@59 85 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
Chris@59 86 m_latency = int(info->outputLatency * m_sampleRate + 0.001);
Chris@96 87 if (m_bufferSize < m_latency) m_bufferSize = m_latency;
Chris@59 88
Chris@59 89 std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
Chris@59 90
Chris@59 91 err = Pa_StartStream(m_stream);
Chris@59 92
Chris@59 93 if (err != paNoError) {
Chris@59 94 std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 95 Pa_CloseStream(m_stream);
Chris@59 96 m_stream = 0;
Chris@59 97 Pa_Terminate();
Chris@59 98 return;
Chris@59 99 }
Chris@59 100
Chris@59 101 if (m_source) {
Chris@59 102 std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
Chris@91 103 m_source->setTarget(this, m_bufferSize);
Chris@59 104 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 105 m_source->setTargetPlayLatency(m_latency);
Chris@59 106 }
Chris@59 107
Chris@59 108 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@59 109 std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
Chris@59 110 #endif
Chris@59 111 }
Chris@59 112
Chris@59 113 AudioPortAudioTarget::~AudioPortAudioTarget()
Chris@59 114 {
Chris@70 115 std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget()" << std::endl;
Chris@70 116
Chris@91 117 if (m_source) {
Chris@91 118 m_source->setTarget(0, m_bufferSize);
Chris@91 119 }
Chris@91 120
Chris@70 121 shutdown();
Chris@70 122
Chris@59 123 if (m_stream) {
Chris@70 124
Chris@70 125 std::cerr << "closing stream" << std::endl;
Chris@70 126
Chris@59 127 PaError err;
Chris@59 128 err = Pa_CloseStream(m_stream);
Chris@59 129 if (err != paNoError) {
Chris@59 130 std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 131 }
Chris@70 132
Chris@70 133 std::cerr << "terminating" << std::endl;
Chris@70 134
Chris@59 135 err = Pa_Terminate();
Chris@59 136 if (err != paNoError) {
Chris@59 137 std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 138 }
Chris@59 139 }
Chris@70 140
Chris@70 141 m_stream = 0;
Chris@70 142
Chris@70 143 std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget() done" << std::endl;
Chris@70 144 }
Chris@70 145
Chris@70 146 void
Chris@70 147 AudioPortAudioTarget::shutdown()
Chris@70 148 {
Chris@177 149 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@177 150 std::cerr << "AudioPortAudioTarget::shutdown" << std::endl;
Chris@177 151 #endif
Chris@70 152 m_done = true;
Chris@59 153 }
Chris@59 154
Chris@59 155 bool
Chris@59 156 AudioPortAudioTarget::isOK() const
Chris@59 157 {
Chris@59 158 return (m_stream != 0);
Chris@59 159 }
Chris@59 160
Chris@91 161 double
Chris@91 162 AudioPortAudioTarget::getCurrentTime() const
Chris@91 163 {
Chris@91 164 if (!m_stream) return 0.0;
Chris@91 165 else return Pa_GetStreamTime(m_stream);
Chris@91 166 }
Chris@91 167
Chris@59 168 int
Chris@59 169 AudioPortAudioTarget::processStatic(const void *input, void *output,
Chris@59 170 unsigned long nframes,
Chris@59 171 const PaStreamCallbackTimeInfo *timeInfo,
Chris@59 172 PaStreamCallbackFlags flags, void *data)
Chris@59 173 {
Chris@59 174 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 175 nframes, timeInfo,
Chris@59 176 flags);
Chris@59 177 }
Chris@59 178
Chris@59 179 void
Chris@59 180 AudioPortAudioTarget::sourceModelReplaced()
Chris@59 181 {
Chris@59 182 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 183 }
Chris@59 184
Chris@59 185 int
Chris@59 186 AudioPortAudioTarget::process(const void *, void *outputBuffer,
Chris@59 187 unsigned long nframes,
Chris@59 188 const PaStreamCallbackTimeInfo *,
Chris@59 189 PaStreamCallbackFlags)
Chris@59 190 {
Chris@59 191 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@177 192 std::cerr << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
Chris@59 193 #endif
Chris@59 194
Chris@177 195 if (!m_source || m_done) {
Chris@177 196 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@177 197 std::cerr << "AudioPortAudioTarget::process: Doing nothing, no source or application done" << std::endl;
Chris@177 198 #endif
Chris@177 199 return 0;
Chris@177 200 }
Chris@59 201
Chris@182 202 if (!m_prioritySet) {
Chris@182 203 #ifndef _WIN32
Chris@182 204 sched_param param;
Chris@182 205 param.sched_priority = 20;
Chris@182 206 if (pthread_setschedparam(pthread_self(), SCHED_RR, &param)) {
Chris@182 207 std::cerr << "AudioPortAudioTarget: NOTE: couldn't set RT scheduling class" << std::endl;
Chris@182 208 } else {
Chris@182 209 std::cerr << "AudioPortAudioTarget: NOTE: successfully set RT scheduling class" << std::endl;
Chris@182 210 }
Chris@182 211 #endif
Chris@182 212 m_prioritySet = true;
Chris@182 213 }
Chris@182 214
Chris@59 215 float *output = (float *)outputBuffer;
Chris@59 216
Chris@59 217 assert(nframes <= m_bufferSize);
Chris@59 218
Chris@59 219 static float **tmpbuf = 0;
Chris@59 220 static size_t tmpbufch = 0;
Chris@59 221 static size_t tmpbufsz = 0;
Chris@59 222
Chris@59 223 size_t sourceChannels = m_source->getSourceChannelCount();
Chris@59 224
Chris@59 225 // Because we offer pan, we always want at least 2 channels
Chris@59 226 if (sourceChannels < 2) sourceChannels = 2;
Chris@59 227
Chris@59 228 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
Chris@59 229
Chris@59 230 if (tmpbuf) {
Chris@59 231 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 232 delete[] tmpbuf[i];
Chris@59 233 }
Chris@59 234 delete[] tmpbuf;
Chris@59 235 }
Chris@59 236
Chris@59 237 tmpbufch = sourceChannels;
Chris@59 238 tmpbufsz = m_bufferSize;
Chris@59 239 tmpbuf = new float *[tmpbufch];
Chris@59 240
Chris@59 241 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 242 tmpbuf[i] = new float[tmpbufsz];
Chris@59 243 }
Chris@59 244 }
Chris@59 245
Chris@59 246 size_t received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@59 247
Chris@59 248 float peakLeft = 0.0, peakRight = 0.0;
Chris@59 249
Chris@59 250 for (size_t ch = 0; ch < 2; ++ch) {
Chris@59 251
Chris@59 252 float peak = 0.0;
Chris@59 253
Chris@59 254 if (ch < sourceChannels) {
Chris@59 255
Chris@59 256 // PortAudio samples are interleaved
Chris@59 257 for (size_t i = 0; i < nframes; ++i) {
Chris@59 258 if (i < received) {
Chris@59 259 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@59 260 float sample = fabsf(output[i * 2 + ch]);
Chris@59 261 if (sample > peak) peak = sample;
Chris@59 262 } else {
Chris@59 263 output[i * 2 + ch] = 0;
Chris@59 264 }
Chris@59 265 }
Chris@59 266
Chris@59 267 } else if (ch == 1 && sourceChannels == 1) {
Chris@59 268
Chris@59 269 for (size_t i = 0; i < nframes; ++i) {
Chris@59 270 if (i < received) {
Chris@59 271 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@59 272 float sample = fabsf(output[i * 2 + ch]);
Chris@59 273 if (sample > peak) peak = sample;
Chris@59 274 } else {
Chris@59 275 output[i * 2 + ch] = 0;
Chris@59 276 }
Chris@59 277 }
Chris@59 278
Chris@59 279 } else {
Chris@59 280 for (size_t i = 0; i < nframes; ++i) {
Chris@59 281 output[i * 2 + ch] = 0;
Chris@59 282 }
Chris@59 283 }
Chris@59 284
Chris@59 285 if (ch == 0) peakLeft = peak;
Chris@59 286 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@59 287 }
Chris@59 288
Chris@59 289 m_source->setOutputLevels(peakLeft, peakRight);
Chris@59 290
Chris@130 291 if (Pa_GetStreamCpuLoad(m_stream) > 0.7) {
Chris@130 292 if (m_source) m_source->audioProcessingOverload();
Chris@130 293 }
Chris@130 294
Chris@59 295 return 0;
Chris@59 296 }
Chris@59 297
Chris@59 298 #endif /* HAVE_PORTAUDIO */
Chris@59 299