annotate audioio/AudioPortAudioTarget.cpp @ 105:907e44e4ecf0

* juggle some files around in order to free audioio, base, and system libraries from dependency on QtGui
author Chris Cannam
date Wed, 12 Mar 2008 17:42:56 +0000
parents e177e6ee7c12
children ccdc5b30e54c
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@59 16 #ifdef HAVE_PORTAUDIO
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 #ifdef HAVE_PORTAUDIO_V18
Chris@59 39 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v18" << std::endl;
Chris@59 40 #else
Chris@59 41 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
Chris@59 42 #endif
Chris@59 43 #endif
Chris@59 44
Chris@59 45 err = Pa_Initialize();
Chris@59 46 if (err != paNoError) {
Chris@59 47 std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 48 return;
Chris@59 49 }
Chris@59 50
Chris@80 51 m_bufferSize = 2048;
Chris@59 52 m_sampleRate = 44100;
Chris@59 53 if (m_source && (m_source->getSourceSampleRate() != 0)) {
Chris@59 54 m_sampleRate = m_source->getSourceSampleRate();
Chris@59 55 }
Chris@59 56
Chris@59 57 #ifdef HAVE_PORTAUDIO_V18
Chris@59 58 m_latency = Pa_GetMinNumBuffers(m_bufferSize, m_sampleRate) * m_bufferSize;
Chris@59 59 #endif
Chris@59 60
Chris@59 61 #ifdef HAVE_PORTAUDIO_V18
Chris@59 62 err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
Chris@59 63 m_sampleRate, m_bufferSize, 0,
Chris@59 64 processStatic, this);
Chris@59 65 #else
Chris@81 66 PaStreamParameters op;
Chris@96 67 op.device = Pa_GetDefaultOutputDevice();
Chris@81 68 op.channelCount = 2;
Chris@81 69 op.sampleFormat = paFloat32;
Chris@81 70 op.suggestedLatency = 0.2;
Chris@81 71 op.hostApiSpecificStreamInfo = 0;
Chris@91 72 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@91 73 paFramesPerBufferUnspecified,
Chris@81 74 paNoFlag, processStatic, this);
Chris@59 75 #endif
Chris@59 76
Chris@95 77 #ifndef HAVE_PORTAUDIO_V18
Chris@95 78 if (err != paNoError) {
Chris@95 79
Chris@95 80 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 81
Chris@95 82 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@95 83 1024,
Chris@95 84 paNoFlag, processStatic, this);
Chris@96 85 m_bufferSize = 1024;
Chris@95 86 }
Chris@95 87 #endif
Chris@95 88
Chris@59 89 if (err != paNoError) {
Chris@59 90 std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 91 m_stream = 0;
Chris@59 92 Pa_Terminate();
Chris@59 93 return;
Chris@59 94 }
Chris@59 95
Chris@59 96 #ifndef HAVE_PORTAUDIO_V18
Chris@59 97 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
Chris@59 98 m_latency = int(info->outputLatency * m_sampleRate + 0.001);
Chris@96 99 if (m_bufferSize < m_latency) m_bufferSize = m_latency;
Chris@59 100 #endif
Chris@59 101
Chris@59 102 std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
Chris@59 103
Chris@59 104 err = Pa_StartStream(m_stream);
Chris@59 105
Chris@59 106 if (err != paNoError) {
Chris@59 107 std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 108 Pa_CloseStream(m_stream);
Chris@59 109 m_stream = 0;
Chris@59 110 Pa_Terminate();
Chris@59 111 return;
Chris@59 112 }
Chris@59 113
Chris@59 114 if (m_source) {
Chris@59 115 std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
Chris@91 116 m_source->setTarget(this, m_bufferSize);
Chris@59 117 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 118 m_source->setTargetPlayLatency(m_latency);
Chris@59 119 }
Chris@59 120
Chris@59 121 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@59 122 std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
Chris@59 123 #endif
Chris@59 124 }
Chris@59 125
Chris@59 126 AudioPortAudioTarget::~AudioPortAudioTarget()
Chris@59 127 {
Chris@70 128 std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget()" << std::endl;
Chris@70 129
Chris@91 130 if (m_source) {
Chris@91 131 m_source->setTarget(0, m_bufferSize);
Chris@91 132 }
Chris@91 133
Chris@70 134 shutdown();
Chris@70 135
Chris@59 136 if (m_stream) {
Chris@70 137
Chris@70 138 std::cerr << "closing stream" << std::endl;
Chris@70 139
Chris@59 140 PaError err;
Chris@59 141 err = Pa_CloseStream(m_stream);
Chris@59 142 if (err != paNoError) {
Chris@59 143 std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 144 }
Chris@70 145
Chris@70 146 std::cerr << "terminating" << std::endl;
Chris@70 147
Chris@59 148 err = Pa_Terminate();
Chris@59 149 if (err != paNoError) {
Chris@59 150 std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 151 }
Chris@59 152 }
Chris@70 153
Chris@70 154 m_stream = 0;
Chris@70 155
Chris@70 156 std::cerr << "AudioPortAudioTarget::~AudioPortAudioTarget() done" << std::endl;
Chris@70 157 }
Chris@70 158
Chris@70 159 void
Chris@70 160 AudioPortAudioTarget::shutdown()
Chris@70 161 {
Chris@70 162 m_done = true;
Chris@59 163 }
Chris@59 164
Chris@59 165 bool
Chris@59 166 AudioPortAudioTarget::isOK() const
Chris@59 167 {
Chris@59 168 return (m_stream != 0);
Chris@59 169 }
Chris@59 170
Chris@91 171 double
Chris@91 172 AudioPortAudioTarget::getCurrentTime() const
Chris@91 173 {
Chris@91 174 if (!m_stream) return 0.0;
Chris@91 175 else return Pa_GetStreamTime(m_stream);
Chris@91 176 }
Chris@91 177
Chris@59 178 #ifdef HAVE_PORTAUDIO_V18
Chris@59 179 int
Chris@59 180 AudioPortAudioTarget::processStatic(void *input, void *output,
Chris@59 181 unsigned long nframes,
Chris@59 182 PaTimestamp outTime, void *data)
Chris@59 183 {
Chris@59 184 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 185 nframes, outTime);
Chris@59 186 }
Chris@59 187 #else
Chris@59 188 int
Chris@59 189 AudioPortAudioTarget::processStatic(const void *input, void *output,
Chris@59 190 unsigned long nframes,
Chris@59 191 const PaStreamCallbackTimeInfo *timeInfo,
Chris@59 192 PaStreamCallbackFlags flags, void *data)
Chris@59 193 {
Chris@59 194 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 195 nframes, timeInfo,
Chris@59 196 flags);
Chris@59 197 }
Chris@59 198 #endif
Chris@59 199
Chris@59 200 void
Chris@59 201 AudioPortAudioTarget::sourceModelReplaced()
Chris@59 202 {
Chris@59 203 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 204 }
Chris@59 205
Chris@59 206 #ifdef HAVE_PORTAUDIO_V18
Chris@59 207 int
Chris@59 208 AudioPortAudioTarget::process(void *inputBuffer, void *outputBuffer,
Chris@59 209 unsigned long nframes,
Chris@59 210 PaTimestamp)
Chris@59 211 #else
Chris@59 212 int
Chris@59 213 AudioPortAudioTarget::process(const void *, void *outputBuffer,
Chris@59 214 unsigned long nframes,
Chris@59 215 const PaStreamCallbackTimeInfo *,
Chris@59 216 PaStreamCallbackFlags)
Chris@59 217 #endif
Chris@59 218 {
Chris@59 219 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@59 220 std::cout << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
Chris@59 221 #endif
Chris@59 222
Chris@70 223 if (!m_source || m_done) return 0;
Chris@59 224
Chris@59 225 float *output = (float *)outputBuffer;
Chris@59 226
Chris@59 227 assert(nframes <= m_bufferSize);
Chris@59 228
Chris@59 229 static float **tmpbuf = 0;
Chris@59 230 static size_t tmpbufch = 0;
Chris@59 231 static size_t tmpbufsz = 0;
Chris@59 232
Chris@59 233 size_t sourceChannels = m_source->getSourceChannelCount();
Chris@59 234
Chris@59 235 // Because we offer pan, we always want at least 2 channels
Chris@59 236 if (sourceChannels < 2) sourceChannels = 2;
Chris@59 237
Chris@59 238 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
Chris@59 239
Chris@59 240 if (tmpbuf) {
Chris@59 241 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 242 delete[] tmpbuf[i];
Chris@59 243 }
Chris@59 244 delete[] tmpbuf;
Chris@59 245 }
Chris@59 246
Chris@59 247 tmpbufch = sourceChannels;
Chris@59 248 tmpbufsz = m_bufferSize;
Chris@59 249 tmpbuf = new float *[tmpbufch];
Chris@59 250
Chris@59 251 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 252 tmpbuf[i] = new float[tmpbufsz];
Chris@59 253 }
Chris@59 254 }
Chris@59 255
Chris@59 256 size_t received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@59 257
Chris@59 258 float peakLeft = 0.0, peakRight = 0.0;
Chris@59 259
Chris@59 260 for (size_t ch = 0; ch < 2; ++ch) {
Chris@59 261
Chris@59 262 float peak = 0.0;
Chris@59 263
Chris@59 264 if (ch < sourceChannels) {
Chris@59 265
Chris@59 266 // PortAudio samples are interleaved
Chris@59 267 for (size_t i = 0; i < nframes; ++i) {
Chris@59 268 if (i < received) {
Chris@59 269 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@59 270 float sample = fabsf(output[i * 2 + ch]);
Chris@59 271 if (sample > peak) peak = sample;
Chris@59 272 } else {
Chris@59 273 output[i * 2 + ch] = 0;
Chris@59 274 }
Chris@59 275 }
Chris@59 276
Chris@59 277 } else if (ch == 1 && sourceChannels == 1) {
Chris@59 278
Chris@59 279 for (size_t i = 0; i < nframes; ++i) {
Chris@59 280 if (i < received) {
Chris@59 281 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@59 282 float sample = fabsf(output[i * 2 + ch]);
Chris@59 283 if (sample > peak) peak = sample;
Chris@59 284 } else {
Chris@59 285 output[i * 2 + ch] = 0;
Chris@59 286 }
Chris@59 287 }
Chris@59 288
Chris@59 289 } else {
Chris@59 290 for (size_t i = 0; i < nframes; ++i) {
Chris@59 291 output[i * 2 + ch] = 0;
Chris@59 292 }
Chris@59 293 }
Chris@59 294
Chris@59 295 if (ch == 0) peakLeft = peak;
Chris@59 296 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@59 297 }
Chris@59 298
Chris@59 299 m_source->setOutputLevels(peakLeft, peakRight);
Chris@59 300
Chris@59 301 return 0;
Chris@59 302 }
Chris@59 303
Chris@59 304 #endif /* HAVE_PORTAUDIO */
Chris@59 305