annotate audioio/AudioPortAudioTarget.cpp @ 60:7b71da2d0631

* Some work on correct alignment when moving panes during playback * Overhaul alignment for playback frame values (view manager now always refers to reference-timeline values, only the play source deals in playback model timeline values) * When making a selection, ensure the selection regions shown in other panes (and used for playback constraints if appropriate) are aligned correctly. This may be the coolest feature ever implemented in any program ever.
author Chris Cannam
date Thu, 22 Nov 2007 14:17:19 +0000
parents bf1a53489ccc
children 716e9d2f91c7 89a689720ee9
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@59 32 m_latency(0)
Chris@59 33 {
Chris@59 34 PaError err;
Chris@59 35
Chris@59 36 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@59 37 #ifdef HAVE_PORTAUDIO_V18
Chris@59 38 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v18" << std::endl;
Chris@59 39 #else
Chris@59 40 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
Chris@59 41 #endif
Chris@59 42 #endif
Chris@59 43
Chris@59 44 err = Pa_Initialize();
Chris@59 45 if (err != paNoError) {
Chris@59 46 std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 47 return;
Chris@59 48 }
Chris@59 49
Chris@59 50 m_bufferSize = 1024;
Chris@59 51 m_sampleRate = 44100;
Chris@59 52 if (m_source && (m_source->getSourceSampleRate() != 0)) {
Chris@59 53 m_sampleRate = m_source->getSourceSampleRate();
Chris@59 54 }
Chris@59 55
Chris@59 56 #ifdef HAVE_PORTAUDIO_V18
Chris@59 57 m_latency = Pa_GetMinNumBuffers(m_bufferSize, m_sampleRate) * m_bufferSize;
Chris@59 58 #endif
Chris@59 59
Chris@59 60 #ifdef HAVE_PORTAUDIO_V18
Chris@59 61 err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
Chris@59 62 m_sampleRate, m_bufferSize, 0,
Chris@59 63 processStatic, this);
Chris@59 64 #else
Chris@59 65 err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
Chris@59 66 m_sampleRate, m_bufferSize,
Chris@59 67 processStatic, this);
Chris@59 68 #endif
Chris@59 69
Chris@59 70 if (err != paNoError) {
Chris@59 71 std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 72 m_stream = 0;
Chris@59 73 Pa_Terminate();
Chris@59 74 return;
Chris@59 75 }
Chris@59 76
Chris@59 77 #ifndef HAVE_PORTAUDIO_V18
Chris@59 78 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
Chris@59 79 m_latency = int(info->outputLatency * m_sampleRate + 0.001);
Chris@59 80 #endif
Chris@59 81
Chris@59 82 std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
Chris@59 83
Chris@59 84 err = Pa_StartStream(m_stream);
Chris@59 85
Chris@59 86 if (err != paNoError) {
Chris@59 87 std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 88 Pa_CloseStream(m_stream);
Chris@59 89 m_stream = 0;
Chris@59 90 Pa_Terminate();
Chris@59 91 return;
Chris@59 92 }
Chris@59 93
Chris@59 94 if (m_source) {
Chris@59 95 std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
Chris@59 96 m_source->setTargetBlockSize(m_bufferSize);
Chris@59 97 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 98 m_source->setTargetPlayLatency(m_latency);
Chris@59 99 }
Chris@59 100
Chris@59 101 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@59 102 std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
Chris@59 103 #endif
Chris@59 104 }
Chris@59 105
Chris@59 106 AudioPortAudioTarget::~AudioPortAudioTarget()
Chris@59 107 {
Chris@59 108 if (m_stream) {
Chris@59 109 PaError err;
Chris@59 110 err = Pa_CloseStream(m_stream);
Chris@59 111 if (err != paNoError) {
Chris@59 112 std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 113 }
Chris@59 114 err = Pa_Terminate();
Chris@59 115 if (err != paNoError) {
Chris@59 116 std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 117 }
Chris@59 118 }
Chris@59 119 }
Chris@59 120
Chris@59 121 bool
Chris@59 122 AudioPortAudioTarget::isOK() const
Chris@59 123 {
Chris@59 124 return (m_stream != 0);
Chris@59 125 }
Chris@59 126
Chris@59 127 #ifdef HAVE_PORTAUDIO_V18
Chris@59 128 int
Chris@59 129 AudioPortAudioTarget::processStatic(void *input, void *output,
Chris@59 130 unsigned long nframes,
Chris@59 131 PaTimestamp outTime, void *data)
Chris@59 132 {
Chris@59 133 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 134 nframes, outTime);
Chris@59 135 }
Chris@59 136 #else
Chris@59 137 int
Chris@59 138 AudioPortAudioTarget::processStatic(const void *input, void *output,
Chris@59 139 unsigned long nframes,
Chris@59 140 const PaStreamCallbackTimeInfo *timeInfo,
Chris@59 141 PaStreamCallbackFlags flags, void *data)
Chris@59 142 {
Chris@59 143 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 144 nframes, timeInfo,
Chris@59 145 flags);
Chris@59 146 }
Chris@59 147 #endif
Chris@59 148
Chris@59 149 void
Chris@59 150 AudioPortAudioTarget::sourceModelReplaced()
Chris@59 151 {
Chris@59 152 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 153 }
Chris@59 154
Chris@59 155 #ifdef HAVE_PORTAUDIO_V18
Chris@59 156 int
Chris@59 157 AudioPortAudioTarget::process(void *inputBuffer, void *outputBuffer,
Chris@59 158 unsigned long nframes,
Chris@59 159 PaTimestamp)
Chris@59 160 #else
Chris@59 161 int
Chris@59 162 AudioPortAudioTarget::process(const void *, void *outputBuffer,
Chris@59 163 unsigned long nframes,
Chris@59 164 const PaStreamCallbackTimeInfo *,
Chris@59 165 PaStreamCallbackFlags)
Chris@59 166 #endif
Chris@59 167 {
Chris@59 168 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@59 169 std::cout << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
Chris@59 170 #endif
Chris@59 171
Chris@59 172 if (!m_source) return 0;
Chris@59 173
Chris@59 174 float *output = (float *)outputBuffer;
Chris@59 175
Chris@59 176 assert(nframes <= m_bufferSize);
Chris@59 177
Chris@59 178 static float **tmpbuf = 0;
Chris@59 179 static size_t tmpbufch = 0;
Chris@59 180 static size_t tmpbufsz = 0;
Chris@59 181
Chris@59 182 size_t sourceChannels = m_source->getSourceChannelCount();
Chris@59 183
Chris@59 184 // Because we offer pan, we always want at least 2 channels
Chris@59 185 if (sourceChannels < 2) sourceChannels = 2;
Chris@59 186
Chris@59 187 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
Chris@59 188
Chris@59 189 if (tmpbuf) {
Chris@59 190 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 191 delete[] tmpbuf[i];
Chris@59 192 }
Chris@59 193 delete[] tmpbuf;
Chris@59 194 }
Chris@59 195
Chris@59 196 tmpbufch = sourceChannels;
Chris@59 197 tmpbufsz = m_bufferSize;
Chris@59 198 tmpbuf = new float *[tmpbufch];
Chris@59 199
Chris@59 200 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 201 tmpbuf[i] = new float[tmpbufsz];
Chris@59 202 }
Chris@59 203 }
Chris@59 204
Chris@59 205 size_t received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@59 206
Chris@59 207 float peakLeft = 0.0, peakRight = 0.0;
Chris@59 208
Chris@59 209 for (size_t ch = 0; ch < 2; ++ch) {
Chris@59 210
Chris@59 211 float peak = 0.0;
Chris@59 212
Chris@59 213 if (ch < sourceChannels) {
Chris@59 214
Chris@59 215 // PortAudio samples are interleaved
Chris@59 216 for (size_t i = 0; i < nframes; ++i) {
Chris@59 217 if (i < received) {
Chris@59 218 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@59 219 float sample = fabsf(output[i * 2 + ch]);
Chris@59 220 if (sample > peak) peak = sample;
Chris@59 221 } else {
Chris@59 222 output[i * 2 + ch] = 0;
Chris@59 223 }
Chris@59 224 }
Chris@59 225
Chris@59 226 } else if (ch == 1 && sourceChannels == 1) {
Chris@59 227
Chris@59 228 for (size_t i = 0; i < nframes; ++i) {
Chris@59 229 if (i < received) {
Chris@59 230 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@59 231 float sample = fabsf(output[i * 2 + ch]);
Chris@59 232 if (sample > peak) peak = sample;
Chris@59 233 } else {
Chris@59 234 output[i * 2 + ch] = 0;
Chris@59 235 }
Chris@59 236 }
Chris@59 237
Chris@59 238 } else {
Chris@59 239 for (size_t i = 0; i < nframes; ++i) {
Chris@59 240 output[i * 2 + ch] = 0;
Chris@59 241 }
Chris@59 242 }
Chris@59 243
Chris@59 244 if (ch == 0) peakLeft = peak;
Chris@59 245 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@59 246 }
Chris@59 247
Chris@59 248 m_source->setOutputLevels(peakLeft, peakRight);
Chris@59 249
Chris@59 250 return 0;
Chris@59 251 }
Chris@59 252
Chris@59 253 #endif /* HAVE_PORTAUDIO */
Chris@59 254