annotate audioio/AudioPortAudioTarget.cpp @ 180:84b2c1a4984a

* Offer the user a chance to answer our survey (only once, and only after several runs of the program, and only if the survey is known to be live on the website)
author Chris Cannam
date Thu, 27 Aug 2009 16:31:45 +0000
parents 7dae51741cc9
children 58b64dbb49c6
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@59 25 //#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1
Chris@59 26
Chris@59 27 AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) :
Chris@59 28 AudioCallbackPlayTarget(source),
Chris@59 29 m_stream(0),
Chris@59 30 m_bufferSize(0),
Chris@59 31 m_sampleRate(0),
Chris@70 32 m_latency(0),
Chris@70 33 m_done(false)
Chris@59 34 {
Chris@59 35 PaError err;
Chris@59 36
Chris@59 37 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@59 38 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
Chris@59 39 #endif
Chris@59 40
Chris@59 41 err = Pa_Initialize();
Chris@59 42 if (err != paNoError) {
Chris@59 43 std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 44 return;
Chris@59 45 }
Chris@59 46
Chris@80 47 m_bufferSize = 2048;
Chris@59 48 m_sampleRate = 44100;
Chris@59 49 if (m_source && (m_source->getSourceSampleRate() != 0)) {
Chris@59 50 m_sampleRate = m_source->getSourceSampleRate();
Chris@59 51 }
Chris@59 52
Chris@81 53 PaStreamParameters op;
Chris@96 54 op.device = Pa_GetDefaultOutputDevice();
Chris@81 55 op.channelCount = 2;
Chris@81 56 op.sampleFormat = paFloat32;
Chris@81 57 op.suggestedLatency = 0.2;
Chris@81 58 op.hostApiSpecificStreamInfo = 0;
Chris@91 59 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@91 60 paFramesPerBufferUnspecified,
Chris@81 61 paNoFlag, processStatic, this);
Chris@59 62
Chris@95 63 if (err != paNoError) {
Chris@95 64
Chris@95 65 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 66
Chris@95 67 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@95 68 1024,
Chris@95 69 paNoFlag, processStatic, this);
Chris@96 70 m_bufferSize = 1024;
Chris@95 71 }
Chris@95 72
Chris@59 73 if (err != paNoError) {
Chris@59 74 std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 75 m_stream = 0;
Chris@59 76 Pa_Terminate();
Chris@59 77 return;
Chris@59 78 }
Chris@59 79
Chris@59 80 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
Chris@59 81 m_latency = int(info->outputLatency * m_sampleRate + 0.001);
Chris@96 82 if (m_bufferSize < m_latency) m_bufferSize = m_latency;
Chris@59 83
Chris@59 84 std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
Chris@59 85
Chris@59 86 err = Pa_StartStream(m_stream);
Chris@59 87
Chris@59 88 if (err != paNoError) {
Chris@59 89 std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 90 Pa_CloseStream(m_stream);
Chris@59 91 m_stream = 0;
Chris@59 92 Pa_Terminate();
Chris@59 93 return;
Chris@59 94 }
Chris@59 95
Chris@59 96 if (m_source) {
Chris@59 97 std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
Chris@91 98 m_source->setTarget(this, m_bufferSize);
Chris@59 99 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 100 m_source->setTargetPlayLatency(m_latency);
Chris@59 101 }
Chris@59 102
Chris@59 103 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@59 104 std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
Chris@59 105 #endif
Chris@59 106 }
Chris@59 107
Chris@59 108 AudioPortAudioTarget::~AudioPortAudioTarget()
Chris@59 109 {
Chris@70 110 std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget()" << std::endl;
Chris@70 111
Chris@91 112 if (m_source) {
Chris@91 113 m_source->setTarget(0, m_bufferSize);
Chris@91 114 }
Chris@91 115
Chris@70 116 shutdown();
Chris@70 117
Chris@59 118 if (m_stream) {
Chris@70 119
Chris@70 120 std::cerr << "closing stream" << std::endl;
Chris@70 121
Chris@59 122 PaError err;
Chris@59 123 err = Pa_CloseStream(m_stream);
Chris@59 124 if (err != paNoError) {
Chris@59 125 std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 126 }
Chris@70 127
Chris@70 128 std::cerr << "terminating" << std::endl;
Chris@70 129
Chris@59 130 err = Pa_Terminate();
Chris@59 131 if (err != paNoError) {
Chris@59 132 std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 133 }
Chris@59 134 }
Chris@70 135
Chris@70 136 m_stream = 0;
Chris@70 137
Chris@70 138 std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget() done" << std::endl;
Chris@70 139 }
Chris@70 140
Chris@70 141 void
Chris@70 142 AudioPortAudioTarget::shutdown()
Chris@70 143 {
Chris@177 144 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@177 145 std::cerr << "AudioPortAudioTarget::shutdown" << std::endl;
Chris@177 146 #endif
Chris@70 147 m_done = true;
Chris@59 148 }
Chris@59 149
Chris@59 150 bool
Chris@59 151 AudioPortAudioTarget::isOK() const
Chris@59 152 {
Chris@59 153 return (m_stream != 0);
Chris@59 154 }
Chris@59 155
Chris@91 156 double
Chris@91 157 AudioPortAudioTarget::getCurrentTime() const
Chris@91 158 {
Chris@91 159 if (!m_stream) return 0.0;
Chris@91 160 else return Pa_GetStreamTime(m_stream);
Chris@91 161 }
Chris@91 162
Chris@59 163 int
Chris@59 164 AudioPortAudioTarget::processStatic(const void *input, void *output,
Chris@59 165 unsigned long nframes,
Chris@59 166 const PaStreamCallbackTimeInfo *timeInfo,
Chris@59 167 PaStreamCallbackFlags flags, void *data)
Chris@59 168 {
Chris@59 169 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 170 nframes, timeInfo,
Chris@59 171 flags);
Chris@59 172 }
Chris@59 173
Chris@59 174 void
Chris@59 175 AudioPortAudioTarget::sourceModelReplaced()
Chris@59 176 {
Chris@59 177 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 178 }
Chris@59 179
Chris@59 180 int
Chris@59 181 AudioPortAudioTarget::process(const void *, void *outputBuffer,
Chris@59 182 unsigned long nframes,
Chris@59 183 const PaStreamCallbackTimeInfo *,
Chris@59 184 PaStreamCallbackFlags)
Chris@59 185 {
Chris@59 186 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@177 187 std::cerr << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
Chris@59 188 #endif
Chris@59 189
Chris@177 190 if (!m_source || m_done) {
Chris@177 191 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@177 192 std::cerr << "AudioPortAudioTarget::process: Doing nothing, no source or application done" << std::endl;
Chris@177 193 #endif
Chris@177 194 return 0;
Chris@177 195 }
Chris@59 196
Chris@59 197 float *output = (float *)outputBuffer;
Chris@59 198
Chris@59 199 assert(nframes <= m_bufferSize);
Chris@59 200
Chris@59 201 static float **tmpbuf = 0;
Chris@59 202 static size_t tmpbufch = 0;
Chris@59 203 static size_t tmpbufsz = 0;
Chris@59 204
Chris@59 205 size_t sourceChannels = m_source->getSourceChannelCount();
Chris@59 206
Chris@59 207 // Because we offer pan, we always want at least 2 channels
Chris@59 208 if (sourceChannels < 2) sourceChannels = 2;
Chris@59 209
Chris@59 210 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
Chris@59 211
Chris@59 212 if (tmpbuf) {
Chris@59 213 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 214 delete[] tmpbuf[i];
Chris@59 215 }
Chris@59 216 delete[] tmpbuf;
Chris@59 217 }
Chris@59 218
Chris@59 219 tmpbufch = sourceChannels;
Chris@59 220 tmpbufsz = m_bufferSize;
Chris@59 221 tmpbuf = new float *[tmpbufch];
Chris@59 222
Chris@59 223 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 224 tmpbuf[i] = new float[tmpbufsz];
Chris@59 225 }
Chris@59 226 }
Chris@59 227
Chris@59 228 size_t received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@59 229
Chris@59 230 float peakLeft = 0.0, peakRight = 0.0;
Chris@59 231
Chris@59 232 for (size_t ch = 0; ch < 2; ++ch) {
Chris@59 233
Chris@59 234 float peak = 0.0;
Chris@59 235
Chris@59 236 if (ch < sourceChannels) {
Chris@59 237
Chris@59 238 // PortAudio samples are interleaved
Chris@59 239 for (size_t i = 0; i < nframes; ++i) {
Chris@59 240 if (i < received) {
Chris@59 241 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@59 242 float sample = fabsf(output[i * 2 + ch]);
Chris@59 243 if (sample > peak) peak = sample;
Chris@59 244 } else {
Chris@59 245 output[i * 2 + ch] = 0;
Chris@59 246 }
Chris@59 247 }
Chris@59 248
Chris@59 249 } else if (ch == 1 && sourceChannels == 1) {
Chris@59 250
Chris@59 251 for (size_t i = 0; i < nframes; ++i) {
Chris@59 252 if (i < received) {
Chris@59 253 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@59 254 float sample = fabsf(output[i * 2 + ch]);
Chris@59 255 if (sample > peak) peak = sample;
Chris@59 256 } else {
Chris@59 257 output[i * 2 + ch] = 0;
Chris@59 258 }
Chris@59 259 }
Chris@59 260
Chris@59 261 } else {
Chris@59 262 for (size_t i = 0; i < nframes; ++i) {
Chris@59 263 output[i * 2 + ch] = 0;
Chris@59 264 }
Chris@59 265 }
Chris@59 266
Chris@59 267 if (ch == 0) peakLeft = peak;
Chris@59 268 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@59 269 }
Chris@59 270
Chris@59 271 m_source->setOutputLevels(peakLeft, peakRight);
Chris@59 272
Chris@130 273 if (Pa_GetStreamCpuLoad(m_stream) > 0.7) {
Chris@130 274 if (m_source) m_source->audioProcessingOverload();
Chris@130 275 }
Chris@130 276
Chris@59 277 return 0;
Chris@59 278 }
Chris@59 279
Chris@59 280 #endif /* HAVE_PORTAUDIO */
Chris@59 281