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