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