Mercurial > hg > svapp
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 |