annotate audioio/AudioPortAudioTarget.cpp @ 43:3c5756fb6a68

* Move some things around to facilitate plundering libraries for other applications without needing to duplicate so much code. sv/osc -> data/osc sv/audioio -> audioio sv/transform -> plugin/transform sv/document -> document (will rename to framework in next commit)
author Chris Cannam
date Wed, 24 Oct 2007 16:34:31 +0000
parents
children bf1a53489ccc
rev   line source
Chris@43 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@43 2
Chris@43 3 /*
Chris@43 4 Sonic Visualiser
Chris@43 5 An audio file viewer and annotation editor.
Chris@43 6 Centre for Digital Music, Queen Mary, University of London.
Chris@43 7 This file copyright 2006 Chris Cannam.
Chris@43 8
Chris@43 9 This program is free software; you can redistribute it and/or
Chris@43 10 modify it under the terms of the GNU General Public License as
Chris@43 11 published by the Free Software Foundation; either version 2 of the
Chris@43 12 License, or (at your option) any later version. See the file
Chris@43 13 COPYING included with this distribution for more information.
Chris@43 14 */
Chris@43 15
Chris@43 16 #ifdef HAVE_PORTAUDIO
Chris@43 17
Chris@43 18 #include "AudioPortAudioTarget.h"
Chris@43 19 #include "AudioCallbackPlaySource.h"
Chris@43 20
Chris@43 21 #include <iostream>
Chris@43 22 #include <cassert>
Chris@43 23 #include <cmath>
Chris@43 24
Chris@43 25 //#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1
Chris@43 26
Chris@43 27 AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) :
Chris@43 28 AudioCallbackPlayTarget(source),
Chris@43 29 m_stream(0),
Chris@43 30 m_bufferSize(0),
Chris@43 31 m_sampleRate(0),
Chris@43 32 m_latency(0)
Chris@43 33 {
Chris@43 34 PaError err;
Chris@43 35
Chris@43 36 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@43 37 #ifdef HAVE_PORTAUDIO_V18
Chris@43 38 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v18" << std::endl;
Chris@43 39 #else
Chris@43 40 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
Chris@43 41 #endif
Chris@43 42 #endif
Chris@43 43
Chris@43 44 err = Pa_Initialize();
Chris@43 45 if (err != paNoError) {
Chris@43 46 std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@43 47 return;
Chris@43 48 }
Chris@43 49
Chris@43 50 m_bufferSize = 1024;
Chris@43 51 m_sampleRate = 44100;
Chris@43 52 if (m_source && (m_source->getSourceSampleRate() != 0)) {
Chris@43 53 m_sampleRate = m_source->getSourceSampleRate();
Chris@43 54 }
Chris@43 55
Chris@43 56 #ifdef HAVE_PORTAUDIO_V18
Chris@43 57 m_latency = Pa_GetMinNumBuffers(m_bufferSize, m_sampleRate) * m_bufferSize;
Chris@43 58 #endif
Chris@43 59
Chris@43 60 #ifdef HAVE_PORTAUDIO_V18
Chris@43 61 err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
Chris@43 62 m_sampleRate, m_bufferSize, 0,
Chris@43 63 processStatic, this);
Chris@43 64 #else
Chris@43 65 err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
Chris@43 66 m_sampleRate, m_bufferSize,
Chris@43 67 processStatic, this);
Chris@43 68 #endif
Chris@43 69
Chris@43 70 if (err != paNoError) {
Chris@43 71 std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@43 72 m_stream = 0;
Chris@43 73 Pa_Terminate();
Chris@43 74 return;
Chris@43 75 }
Chris@43 76
Chris@43 77 #ifndef HAVE_PORTAUDIO_V18
Chris@43 78 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
Chris@43 79 m_latency = int(info->outputLatency * m_sampleRate + 0.001);
Chris@43 80 #endif
Chris@43 81
Chris@43 82 std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
Chris@43 83
Chris@43 84 err = Pa_StartStream(m_stream);
Chris@43 85
Chris@43 86 if (err != paNoError) {
Chris@43 87 std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@43 88 Pa_CloseStream(m_stream);
Chris@43 89 m_stream = 0;
Chris@43 90 Pa_Terminate();
Chris@43 91 return;
Chris@43 92 }
Chris@43 93
Chris@43 94 if (m_source) {
Chris@43 95 std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
Chris@43 96 m_source->setTargetBlockSize(m_bufferSize);
Chris@43 97 m_source->setTargetSampleRate(m_sampleRate);
Chris@43 98 m_source->setTargetPlayLatency(m_latency);
Chris@43 99 }
Chris@43 100
Chris@43 101 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@43 102 std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
Chris@43 103 #endif
Chris@43 104 }
Chris@43 105
Chris@43 106 AudioPortAudioTarget::~AudioPortAudioTarget()
Chris@43 107 {
Chris@43 108 if (m_stream) {
Chris@43 109 PaError err;
Chris@43 110 err = Pa_CloseStream(m_stream);
Chris@43 111 if (err != paNoError) {
Chris@43 112 std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@43 113 }
Chris@43 114 err = Pa_Terminate();
Chris@43 115 if (err != paNoError) {
Chris@43 116 std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@43 117 }
Chris@43 118 }
Chris@43 119 }
Chris@43 120
Chris@43 121 bool
Chris@43 122 AudioPortAudioTarget::isOK() const
Chris@43 123 {
Chris@43 124 return (m_stream != 0);
Chris@43 125 }
Chris@43 126
Chris@43 127 #ifdef HAVE_PORTAUDIO_V18
Chris@43 128 int
Chris@43 129 AudioPortAudioTarget::processStatic(void *input, void *output,
Chris@43 130 unsigned long nframes,
Chris@43 131 PaTimestamp outTime, void *data)
Chris@43 132 {
Chris@43 133 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@43 134 nframes, outTime);
Chris@43 135 }
Chris@43 136 #else
Chris@43 137 int
Chris@43 138 AudioPortAudioTarget::processStatic(const void *input, void *output,
Chris@43 139 unsigned long nframes,
Chris@43 140 const PaStreamCallbackTimeInfo *timeInfo,
Chris@43 141 PaStreamCallbackFlags flags, void *data)
Chris@43 142 {
Chris@43 143 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@43 144 nframes, timeInfo,
Chris@43 145 flags);
Chris@43 146 }
Chris@43 147 #endif
Chris@43 148
Chris@43 149 void
Chris@43 150 AudioPortAudioTarget::sourceModelReplaced()
Chris@43 151 {
Chris@43 152 m_source->setTargetSampleRate(m_sampleRate);
Chris@43 153 }
Chris@43 154
Chris@43 155 #ifdef HAVE_PORTAUDIO_V18
Chris@43 156 int
Chris@43 157 AudioPortAudioTarget::process(void *inputBuffer, void *outputBuffer,
Chris@43 158 unsigned long nframes,
Chris@43 159 PaTimestamp)
Chris@43 160 #else
Chris@43 161 int
Chris@43 162 AudioPortAudioTarget::process(const void *, void *outputBuffer,
Chris@43 163 unsigned long nframes,
Chris@43 164 const PaStreamCallbackTimeInfo *,
Chris@43 165 PaStreamCallbackFlags)
Chris@43 166 #endif
Chris@43 167 {
Chris@43 168 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@43 169 std::cout << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
Chris@43 170 #endif
Chris@43 171
Chris@43 172 if (!m_source) return 0;
Chris@43 173
Chris@43 174 float *output = (float *)outputBuffer;
Chris@43 175
Chris@43 176 assert(nframes <= m_bufferSize);
Chris@43 177
Chris@43 178 static float **tmpbuf = 0;
Chris@43 179 static size_t tmpbufch = 0;
Chris@43 180 static size_t tmpbufsz = 0;
Chris@43 181
Chris@43 182 size_t sourceChannels = m_source->getSourceChannelCount();
Chris@43 183
Chris@43 184 // Because we offer pan, we always want at least 2 channels
Chris@43 185 if (sourceChannels < 2) sourceChannels = 2;
Chris@43 186
Chris@43 187 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
Chris@43 188
Chris@43 189 if (tmpbuf) {
Chris@43 190 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@43 191 delete[] tmpbuf[i];
Chris@43 192 }
Chris@43 193 delete[] tmpbuf;
Chris@43 194 }
Chris@43 195
Chris@43 196 tmpbufch = sourceChannels;
Chris@43 197 tmpbufsz = m_bufferSize;
Chris@43 198 tmpbuf = new float *[tmpbufch];
Chris@43 199
Chris@43 200 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@43 201 tmpbuf[i] = new float[tmpbufsz];
Chris@43 202 }
Chris@43 203 }
Chris@43 204
Chris@43 205 size_t received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@43 206
Chris@43 207 float peakLeft = 0.0, peakRight = 0.0;
Chris@43 208
Chris@43 209 for (size_t ch = 0; ch < 2; ++ch) {
Chris@43 210
Chris@43 211 float peak = 0.0;
Chris@43 212
Chris@43 213 if (ch < sourceChannels) {
Chris@43 214
Chris@43 215 // PortAudio samples are interleaved
Chris@43 216 for (size_t i = 0; i < nframes; ++i) {
Chris@43 217 if (i < received) {
Chris@43 218 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@43 219 float sample = fabsf(output[i * 2 + ch]);
Chris@43 220 if (sample > peak) peak = sample;
Chris@43 221 } else {
Chris@43 222 output[i * 2 + ch] = 0;
Chris@43 223 }
Chris@43 224 }
Chris@43 225
Chris@43 226 } else if (ch == 1 && sourceChannels == 1) {
Chris@43 227
Chris@43 228 for (size_t i = 0; i < nframes; ++i) {
Chris@43 229 if (i < received) {
Chris@43 230 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@43 231 float sample = fabsf(output[i * 2 + ch]);
Chris@43 232 if (sample > peak) peak = sample;
Chris@43 233 } else {
Chris@43 234 output[i * 2 + ch] = 0;
Chris@43 235 }
Chris@43 236 }
Chris@43 237
Chris@43 238 } else {
Chris@43 239 for (size_t i = 0; i < nframes; ++i) {
Chris@43 240 output[i * 2 + ch] = 0;
Chris@43 241 }
Chris@43 242 }
Chris@43 243
Chris@43 244 if (ch == 0) peakLeft = peak;
Chris@43 245 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@43 246 }
Chris@43 247
Chris@43 248 m_source->setOutputLevels(peakLeft, peakRight);
Chris@43 249
Chris@43 250 return 0;
Chris@43 251 }
Chris@43 252
Chris@43 253 #endif /* HAVE_PORTAUDIO */
Chris@43 254