annotate audioio/AudioPortAudioTarget.cpp @ 154:386b02c926bf

* Merge from one-fftdataserver-per-fftmodel branch. This bit of reworking (which is not described very accurately by the title of the branch) turns the MatrixFile object into something that either reads or writes, but not both, and separates the FFT file cache reader and writer implementations separately. This allows the FFT data server to have a single thread owning writers and one reader per "customer" thread, and for all locking to be vastly simplified and concentrated in the data server alone (because none of the classes it makes use of is used in more than one thread at a time). The result is faster and more trustworthy code.
author Chris Cannam
date Tue, 27 Jan 2009 13:25:10 +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