annotate audioio/AudioPortAudioTarget.cpp @ 141:9a8c73ffdce0

* Make it possible to import an entire session from an RDF document. However, at the moment the timings of events appear to be constrained by how far the audio decoder has got through its audio file at the time the event is queried -- need to investigate.
author Chris Cannam
date Fri, 21 Nov 2008 18:03:14 +0000
parents 4c9c04645685
children 7dae51741cc9
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@70 144 m_done = true;
Chris@59 145 }
Chris@59 146
Chris@59 147 bool
Chris@59 148 AudioPortAudioTarget::isOK() const
Chris@59 149 {
Chris@59 150 return (m_stream != 0);
Chris@59 151 }
Chris@59 152
Chris@91 153 double
Chris@91 154 AudioPortAudioTarget::getCurrentTime() const
Chris@91 155 {
Chris@91 156 if (!m_stream) return 0.0;
Chris@91 157 else return Pa_GetStreamTime(m_stream);
Chris@91 158 }
Chris@91 159
Chris@59 160 int
Chris@59 161 AudioPortAudioTarget::processStatic(const void *input, void *output,
Chris@59 162 unsigned long nframes,
Chris@59 163 const PaStreamCallbackTimeInfo *timeInfo,
Chris@59 164 PaStreamCallbackFlags flags, void *data)
Chris@59 165 {
Chris@59 166 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 167 nframes, timeInfo,
Chris@59 168 flags);
Chris@59 169 }
Chris@59 170
Chris@59 171 void
Chris@59 172 AudioPortAudioTarget::sourceModelReplaced()
Chris@59 173 {
Chris@59 174 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 175 }
Chris@59 176
Chris@59 177 int
Chris@59 178 AudioPortAudioTarget::process(const void *, void *outputBuffer,
Chris@59 179 unsigned long nframes,
Chris@59 180 const PaStreamCallbackTimeInfo *,
Chris@59 181 PaStreamCallbackFlags)
Chris@59 182 {
Chris@59 183 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@59 184 std::cout << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
Chris@59 185 #endif
Chris@59 186
Chris@70 187 if (!m_source || m_done) return 0;
Chris@59 188
Chris@59 189 float *output = (float *)outputBuffer;
Chris@59 190
Chris@59 191 assert(nframes <= m_bufferSize);
Chris@59 192
Chris@59 193 static float **tmpbuf = 0;
Chris@59 194 static size_t tmpbufch = 0;
Chris@59 195 static size_t tmpbufsz = 0;
Chris@59 196
Chris@59 197 size_t sourceChannels = m_source->getSourceChannelCount();
Chris@59 198
Chris@59 199 // Because we offer pan, we always want at least 2 channels
Chris@59 200 if (sourceChannels < 2) sourceChannels = 2;
Chris@59 201
Chris@59 202 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
Chris@59 203
Chris@59 204 if (tmpbuf) {
Chris@59 205 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 206 delete[] tmpbuf[i];
Chris@59 207 }
Chris@59 208 delete[] tmpbuf;
Chris@59 209 }
Chris@59 210
Chris@59 211 tmpbufch = sourceChannels;
Chris@59 212 tmpbufsz = m_bufferSize;
Chris@59 213 tmpbuf = new float *[tmpbufch];
Chris@59 214
Chris@59 215 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 216 tmpbuf[i] = new float[tmpbufsz];
Chris@59 217 }
Chris@59 218 }
Chris@59 219
Chris@59 220 size_t received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@59 221
Chris@59 222 float peakLeft = 0.0, peakRight = 0.0;
Chris@59 223
Chris@59 224 for (size_t ch = 0; ch < 2; ++ch) {
Chris@59 225
Chris@59 226 float peak = 0.0;
Chris@59 227
Chris@59 228 if (ch < sourceChannels) {
Chris@59 229
Chris@59 230 // PortAudio samples are interleaved
Chris@59 231 for (size_t i = 0; i < nframes; ++i) {
Chris@59 232 if (i < received) {
Chris@59 233 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@59 234 float sample = fabsf(output[i * 2 + ch]);
Chris@59 235 if (sample > peak) peak = sample;
Chris@59 236 } else {
Chris@59 237 output[i * 2 + ch] = 0;
Chris@59 238 }
Chris@59 239 }
Chris@59 240
Chris@59 241 } else if (ch == 1 && sourceChannels == 1) {
Chris@59 242
Chris@59 243 for (size_t i = 0; i < nframes; ++i) {
Chris@59 244 if (i < received) {
Chris@59 245 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@59 246 float sample = fabsf(output[i * 2 + ch]);
Chris@59 247 if (sample > peak) peak = sample;
Chris@59 248 } else {
Chris@59 249 output[i * 2 + ch] = 0;
Chris@59 250 }
Chris@59 251 }
Chris@59 252
Chris@59 253 } else {
Chris@59 254 for (size_t i = 0; i < nframes; ++i) {
Chris@59 255 output[i * 2 + ch] = 0;
Chris@59 256 }
Chris@59 257 }
Chris@59 258
Chris@59 259 if (ch == 0) peakLeft = peak;
Chris@59 260 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@59 261 }
Chris@59 262
Chris@59 263 m_source->setOutputLevels(peakLeft, peakRight);
Chris@59 264
Chris@130 265 if (Pa_GetStreamCpuLoad(m_stream) > 0.7) {
Chris@130 266 if (m_source) m_source->audioProcessingOverload();
Chris@130 267 }
Chris@130 268
Chris@59 269 return 0;
Chris@59 270 }
Chris@59 271
Chris@59 272 #endif /* HAVE_PORTAUDIO */
Chris@59 273