annotate audioio/AudioPortAudioTarget.cpp @ 256:f3f9e3d647c1

Give a dedicated key to toggling the centre line, and move it out of the overlay level setting -- reducing number of overlay levels to 3. Introduce two distinct vertical scale types (so that we can hide the spectrogram colour scale part easily)
author Chris Cannam
date Mon, 30 Jan 2012 16:02:14 +0000
parents a9f1850b873d
children 0136555495ae 068235cf5bf7
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@182 25 #ifndef _WIN32
Chris@182 26 #include <pthread.h>
Chris@182 27 #endif
Chris@182 28
Chris@59 29 //#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1
Chris@59 30
Chris@59 31 AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) :
Chris@59 32 AudioCallbackPlayTarget(source),
Chris@59 33 m_stream(0),
Chris@59 34 m_bufferSize(0),
Chris@59 35 m_sampleRate(0),
Chris@70 36 m_latency(0),
Chris@182 37 m_prioritySet(false),
Chris@70 38 m_done(false)
Chris@59 39 {
Chris@59 40 PaError err;
Chris@59 41
Chris@59 42 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@59 43 std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
Chris@59 44 #endif
Chris@59 45
Chris@59 46 err = Pa_Initialize();
Chris@59 47 if (err != paNoError) {
Chris@59 48 std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 49 return;
Chris@59 50 }
Chris@59 51
Chris@80 52 m_bufferSize = 2048;
Chris@59 53 m_sampleRate = 44100;
Chris@59 54 if (m_source && (m_source->getSourceSampleRate() != 0)) {
Chris@59 55 m_sampleRate = m_source->getSourceSampleRate();
Chris@59 56 }
Chris@59 57
Chris@81 58 PaStreamParameters op;
Chris@96 59 op.device = Pa_GetDefaultOutputDevice();
Chris@81 60 op.channelCount = 2;
Chris@81 61 op.sampleFormat = paFloat32;
Chris@245 62 op.suggestedLatency = 0.2;
Chris@81 63 op.hostApiSpecificStreamInfo = 0;
Chris@91 64 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@91 65 paFramesPerBufferUnspecified,
Chris@81 66 paNoFlag, processStatic, this);
Chris@59 67
Chris@95 68 if (err != paNoError) {
Chris@95 69
Chris@95 70 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 71
Chris@95 72 err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
Chris@95 73 1024,
Chris@95 74 paNoFlag, processStatic, this);
Chris@96 75 m_bufferSize = 1024;
Chris@95 76 }
Chris@95 77
Chris@59 78 if (err != paNoError) {
Chris@59 79 std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@195 80 std::cerr << "Note: device ID was " << op.device << std::endl;
Chris@59 81 m_stream = 0;
Chris@59 82 Pa_Terminate();
Chris@59 83 return;
Chris@59 84 }
Chris@59 85
Chris@59 86 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
Chris@59 87 m_latency = int(info->outputLatency * m_sampleRate + 0.001);
Chris@96 88 if (m_bufferSize < m_latency) m_bufferSize = m_latency;
Chris@59 89
Chris@59 90 std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
Chris@59 91
Chris@59 92 err = Pa_StartStream(m_stream);
Chris@59 93
Chris@59 94 if (err != paNoError) {
Chris@59 95 std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 96 Pa_CloseStream(m_stream);
Chris@59 97 m_stream = 0;
Chris@59 98 Pa_Terminate();
Chris@59 99 return;
Chris@59 100 }
Chris@59 101
Chris@59 102 if (m_source) {
Chris@59 103 std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
Chris@91 104 m_source->setTarget(this, m_bufferSize);
Chris@59 105 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 106 m_source->setTargetPlayLatency(m_latency);
Chris@59 107 }
Chris@59 108
Chris@59 109 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@59 110 std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
Chris@59 111 #endif
Chris@59 112 }
Chris@59 113
Chris@59 114 AudioPortAudioTarget::~AudioPortAudioTarget()
Chris@59 115 {
Chris@233 116 SVDEBUG << "AudioPortAudioTarget::~AudioPortAudioTarget()" << endl;
Chris@70 117
Chris@91 118 if (m_source) {
Chris@91 119 m_source->setTarget(0, m_bufferSize);
Chris@91 120 }
Chris@91 121
Chris@70 122 shutdown();
Chris@70 123
Chris@59 124 if (m_stream) {
Chris@70 125
Chris@233 126 SVDEBUG << "closing stream" << endl;
Chris@70 127
Chris@59 128 PaError err;
Chris@59 129 err = Pa_CloseStream(m_stream);
Chris@59 130 if (err != paNoError) {
Chris@59 131 std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
Chris@59 132 }
Chris@70 133
Chris@70 134 std::cerr << "terminating" << std::endl;
Chris@70 135
Chris@59 136 err = Pa_Terminate();
Chris@59 137 if (err != paNoError) {
Chris@59 138 std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
Chris@59 139 }
Chris@59 140 }
Chris@70 141
Chris@70 142 m_stream = 0;
Chris@70 143
Chris@233 144 SVDEBUG << "AudioPortAudioTarget::~AudioPortAudioTarget() done" << endl;
Chris@70 145 }
Chris@70 146
Chris@70 147 void
Chris@70 148 AudioPortAudioTarget::shutdown()
Chris@70 149 {
Chris@177 150 #ifdef DEBUG_PORT_AUDIO_TARGET
Chris@233 151 SVDEBUG << "AudioPortAudioTarget::shutdown" << endl;
Chris@177 152 #endif
Chris@70 153 m_done = true;
Chris@59 154 }
Chris@59 155
Chris@59 156 bool
Chris@59 157 AudioPortAudioTarget::isOK() const
Chris@59 158 {
Chris@59 159 return (m_stream != 0);
Chris@59 160 }
Chris@59 161
Chris@91 162 double
Chris@91 163 AudioPortAudioTarget::getCurrentTime() const
Chris@91 164 {
Chris@91 165 if (!m_stream) return 0.0;
Chris@91 166 else return Pa_GetStreamTime(m_stream);
Chris@91 167 }
Chris@91 168
Chris@59 169 int
Chris@59 170 AudioPortAudioTarget::processStatic(const void *input, void *output,
Chris@59 171 unsigned long nframes,
Chris@59 172 const PaStreamCallbackTimeInfo *timeInfo,
Chris@59 173 PaStreamCallbackFlags flags, void *data)
Chris@59 174 {
Chris@59 175 return ((AudioPortAudioTarget *)data)->process(input, output,
Chris@59 176 nframes, timeInfo,
Chris@59 177 flags);
Chris@59 178 }
Chris@59 179
Chris@59 180 void
Chris@59 181 AudioPortAudioTarget::sourceModelReplaced()
Chris@59 182 {
Chris@59 183 m_source->setTargetSampleRate(m_sampleRate);
Chris@59 184 }
Chris@59 185
Chris@59 186 int
Chris@59 187 AudioPortAudioTarget::process(const void *, void *outputBuffer,
Chris@59 188 unsigned long nframes,
Chris@59 189 const PaStreamCallbackTimeInfo *,
Chris@59 190 PaStreamCallbackFlags)
Chris@59 191 {
Chris@59 192 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@233 193 SVDEBUG << "AudioPortAudioTarget::process(" << nframes << ")" << endl;
Chris@59 194 #endif
Chris@59 195
Chris@177 196 if (!m_source || m_done) {
Chris@177 197 #ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
Chris@233 198 SVDEBUG << "AudioPortAudioTarget::process: Doing nothing, no source or application done" << endl;
Chris@177 199 #endif
Chris@177 200 return 0;
Chris@177 201 }
Chris@59 202
Chris@182 203 if (!m_prioritySet) {
Chris@182 204 #ifndef _WIN32
Chris@182 205 sched_param param;
Chris@182 206 param.sched_priority = 20;
Chris@182 207 if (pthread_setschedparam(pthread_self(), SCHED_RR, &param)) {
Chris@233 208 SVDEBUG << "AudioPortAudioTarget: NOTE: couldn't set RT scheduling class" << endl;
Chris@182 209 } else {
Chris@233 210 SVDEBUG << "AudioPortAudioTarget: NOTE: successfully set RT scheduling class" << endl;
Chris@182 211 }
Chris@182 212 #endif
Chris@182 213 m_prioritySet = true;
Chris@182 214 }
Chris@182 215
Chris@59 216 float *output = (float *)outputBuffer;
Chris@59 217
Chris@59 218 assert(nframes <= m_bufferSize);
Chris@59 219
Chris@59 220 static float **tmpbuf = 0;
Chris@59 221 static size_t tmpbufch = 0;
Chris@59 222 static size_t tmpbufsz = 0;
Chris@59 223
Chris@59 224 size_t sourceChannels = m_source->getSourceChannelCount();
Chris@59 225
Chris@59 226 // Because we offer pan, we always want at least 2 channels
Chris@59 227 if (sourceChannels < 2) sourceChannels = 2;
Chris@59 228
Chris@59 229 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
Chris@59 230
Chris@59 231 if (tmpbuf) {
Chris@59 232 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 233 delete[] tmpbuf[i];
Chris@59 234 }
Chris@59 235 delete[] tmpbuf;
Chris@59 236 }
Chris@59 237
Chris@59 238 tmpbufch = sourceChannels;
Chris@59 239 tmpbufsz = m_bufferSize;
Chris@59 240 tmpbuf = new float *[tmpbufch];
Chris@59 241
Chris@59 242 for (size_t i = 0; i < tmpbufch; ++i) {
Chris@59 243 tmpbuf[i] = new float[tmpbufsz];
Chris@59 244 }
Chris@59 245 }
Chris@59 246
Chris@59 247 size_t received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@59 248
Chris@59 249 float peakLeft = 0.0, peakRight = 0.0;
Chris@59 250
Chris@59 251 for (size_t ch = 0; ch < 2; ++ch) {
Chris@59 252
Chris@59 253 float peak = 0.0;
Chris@59 254
Chris@59 255 if (ch < sourceChannels) {
Chris@59 256
Chris@59 257 // PortAudio samples are interleaved
Chris@59 258 for (size_t i = 0; i < nframes; ++i) {
Chris@59 259 if (i < received) {
Chris@59 260 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@59 261 float sample = fabsf(output[i * 2 + ch]);
Chris@59 262 if (sample > peak) peak = sample;
Chris@59 263 } else {
Chris@59 264 output[i * 2 + ch] = 0;
Chris@59 265 }
Chris@59 266 }
Chris@59 267
Chris@59 268 } else if (ch == 1 && sourceChannels == 1) {
Chris@59 269
Chris@59 270 for (size_t i = 0; i < nframes; ++i) {
Chris@59 271 if (i < received) {
Chris@59 272 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@59 273 float sample = fabsf(output[i * 2 + ch]);
Chris@59 274 if (sample > peak) peak = sample;
Chris@59 275 } else {
Chris@59 276 output[i * 2 + ch] = 0;
Chris@59 277 }
Chris@59 278 }
Chris@59 279
Chris@59 280 } else {
Chris@59 281 for (size_t i = 0; i < nframes; ++i) {
Chris@59 282 output[i * 2 + ch] = 0;
Chris@59 283 }
Chris@59 284 }
Chris@59 285
Chris@59 286 if (ch == 0) peakLeft = peak;
Chris@59 287 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@59 288 }
Chris@59 289
Chris@59 290 m_source->setOutputLevels(peakLeft, peakRight);
Chris@59 291
Chris@130 292 if (Pa_GetStreamCpuLoad(m_stream) > 0.7) {
Chris@130 293 if (m_source) m_source->audioProcessingOverload();
Chris@130 294 }
Chris@130 295
Chris@59 296 return 0;
Chris@59 297 }
Chris@59 298
Chris@59 299 #endif /* HAVE_PORTAUDIO */
Chris@59 300