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