forked from yozlet/interface
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaudio.cpp
More file actions
390 lines (357 loc) · 14.3 KB
/
Copy pathaudio.cpp
File metadata and controls
390 lines (357 loc) · 14.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
//
// audio.cpp
// interface
//
// Created by Seiji Emery on 9/2/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
/**
* @file audio.cpp
* Low level audio i/o portaudio wrapper.
*
* @author Seiji Emery
*
*/
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include "audio.h"
// static member definitions
// (required – will cause linker errors if left out...):
bool Audio::initialized;
Audio::AudioData *Audio::data;
PaStream *Audio::stream;
PaError Audio::err;
float Audio::AudioData::inputGain;
/**
* Audio callback used by portaudio.
* Communicates with Audio via a shared pointer to Audio::data.
* Writes input audio channels (if they exist) into Audio::data->buffer,
multiplied by Audio::data->inputGain.
* Then writes Audio::data->buffer into output audio channels, and clears
the portion of Audio::data->buffer that has been read from for reuse.
*
* @param[in] inputBuffer A pointer to an internal portaudio data buffer containing data read by portaudio.
* @param[out] outputBuffer A pointer to an internal portaudio data buffer to be read by the configured output device.
* @param[in] frames Number of frames that portaudio requests to be read/written.
(Valid size of input/output buffers = frames * number of channels (2) * sizeof data type (float)).
* @param[in] timeInfo Portaudio time info. Currently unused.
* @param[in] statusFlags Portaudio status flags. Currently unused.
* @param[in] userData Pointer to supplied user data (in this case, a pointer to Audio::data).
Used to communicate with external code (since portaudio calls this function from another thread).
* @return Should be of type PaStreamCallbackResult. Return paComplete to end the stream, or paContinue to continue (default).
Can be used to end the stream from within the callback.
*/
int audioCallback (const void *inputBuffer,
void *outputBuffer,
unsigned long frames,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
Audio::AudioData *data = (Audio::AudioData*)userData;
float *input = (float*)inputBuffer;
float *output = (float*)outputBuffer;
#if WRITE_AUDIO_INPUT_TO_OUTPUT
if (input != NULL) {// && Audio::writeAudioInputToOutput) {
// combine input into data buffer
// temp variables (frames and bufferPos need to remain untouched so they can be used in the second block of code)
unsigned int f = (unsigned int)frames,
p = data->bufferPos;
for (; p < data->bufferLength && f > 0; --f, ++p) {
#if WRITE_AUDIO_INPUT_TO_BUFFER
data->buffer[p].l +=
data->inputBuffer[p].l = (*input++) * data->inputGain;
data->buffer[p].r +=
data->inputBuffer[p].r = (*input++) * data->inputGain;
#else
data->buffer[p].l += (*input++) * data->inputGain;
data->buffer[p].r += (*input++) * data->inputGain;
#endif
}
if (f > 0) {
// handle data->buffer wraparound
for (p = 0; f > 0; --f, ++p) {
#if WRITE_AUDIO_INPUT_TO_BUFFER
data->buffer[p].l +=
data->inputBuffer[p].l = (*input++) * data->inputGain;
data->buffer[p].r +=
data->inputBuffer[p].r = (*input++) * data->inputGain;
#else
data->buffer[p].l += (*input++) * data->inputGain;
data->buffer[p].r += (*input++) * data->inputGain;
#endif
}
}
}
#elif WRITE_AUDIO_INPUT_TO_BUFFER
if (input != NULL) {// && Audio::writeAudioInputToBuffer) {
unsigned int f = (unsigned int)frames,
p = data->bufferPos;
for (; p < data->bufferLength && f > 0; --f, ++p) {
data->inputBuffer[p].l = (*input++) * data->inputGain;
data->inputBuffer[p].r = (*input++) * data->inputGain;
}
if (f > 0) {
// handle data->buffer wraparound
for (p = 0; f > 0; --f, ++p) {
data->inputBuffer[p].l = (*input++) * data->inputGain;
data->inputBuffer[p].r = (*input++) * data->inputGain;
}
}
}
#endif
// Write data->buffer into outputBuffer
if (data->bufferPos + frames >= data->bufferLength) {
// wraparound: write first section (end of buffer) first
// note: buffer is just an array of a struct of floats, so it can be typecast to float*
memcpy(output, (float*)(data->buffer + data->bufferPos), // write data buffer
(data->bufferLength - data->bufferPos) * 2 * sizeof(float));
memset((float*)(data->buffer + data->bufferPos), 0, // clear data buffer
(data->bufferLength - data->bufferPos) * 2 * sizeof(float));
frames -= (data->bufferLength - data->bufferPos); // adjust frames to be written
data->bufferPos = 0; // reset position to start
}
memcpy(output, (float*)(data->buffer + data->bufferPos), // write data buffer
frames * 2 * sizeof(float));
memset((float*)(data->buffer + data->bufferPos), 0, // clear data buffer
frames * 2 * sizeof(float));
data->bufferPos += frames; // update position
return paContinue;
}
/**
* Initialize portaudio and start an audio stream.
* Should be called at the beginning of program exection.
* @seealso Audio::terminate
* @return Returns true if successful or false if an error occurred.
Use Audio::getError() to retrieve the error code.
*/
bool Audio::init()
{
initialized = true;
data = new AudioData();
err = Pa_Initialize();
if (err != paNoError) goto error;
err = Pa_OpenDefaultStream(&stream,
1, // input channels
1, // output channels
paFloat32, // sample format
22050, // sample rate (hz)
512, // frames per buffer
audioCallback, // callback function
(void*)data); // user data to be passed to callback
if (err != paNoError) goto error;
err = Pa_StartStream(stream);
if (err != paNoError) goto error;
return paNoError;
error:
fprintf(stderr, "-- Failed to initialize portaudio --\n");
fprintf(stderr, "PortAudio error (%d): %s\n", err, Pa_GetErrorText(err));
initialized = false;
delete[] data;
return false;
}
/**
* Close the running audio stream, and deinitialize portaudio.
* Should be called at the end of program execution.
* @return Returns true if the initialization was successful, or false if an error occured.
The error code may be retrieved by Audio::getError().
*/
bool Audio::terminate ()
{
if (!initialized)
return true;
initialized = false;
// err = Pa_StopStream(stream);
// if (err != paNoError) goto error;
err = Pa_CloseStream(stream);
if (err != paNoError) goto error;
delete data;
err = Pa_Terminate();
if (err != paNoError) goto error;
return true;
error:
fprintf(stderr, "-- portaudio termination error --\n");
fprintf(stderr, "PortAudio error (%d): %s\n", err, Pa_GetErrorText(err));
return false;
}
/**
* Write a stereo audio stream (float*) to the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of audio channels to be read.
* @param[in] left Left channel of the audio stream.
* @param[in] right Right channel of the audio stream.
*/
void Audio::writeAudio (unsigned int offset, unsigned int length, float const *left, float const *right) {
if (data->buffer == NULL)
return;
if (length > data->bufferLength) {
fprintf(stderr, "Audio::writeAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
length = data->bufferLength;
}
unsigned int p = data->bufferPos + offset;
if (p > data->bufferLength)
p -= data->bufferLength;
for (; p < data->bufferLength && length > 0; --length, ++p) {
data->buffer[p].l = *left++;
data->buffer[p].r = *right++;
}
if (length > 0) {
p = 0;
for (; length > 0; --length, ++p) {
data->buffer[p].l = *left++;
data->buffer[p].r = *right++;
}
}
}
/**
* Write a repeated stereo sample (float) to the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of tone.
* @param[in] left Left component.
* @param[in] right Right component.
*/
void Audio::writeTone (unsigned int offset, unsigned int length, float const left, float const right) {
if (data->buffer == NULL)
return;
if (length > data->bufferLength) {
fprintf(stderr, "Audio::writeTone length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
length = data->bufferLength;
}
unsigned int p = data->bufferPos + offset;
if (p > data->bufferLength)
p -= data->bufferLength;
for (; p < data->bufferLength && length > 0; --length, ++p) {
data->buffer[p].l = left;
data->buffer[p].r = right;
}
if (length > 0) {
p = 0;
for (; length > 0; --length, ++p) {
data->buffer[p].l = left;
data->buffer[p].r = right;
}
}
}
/**
* Write a stereo audio stream (float*) to the audio buffer.
* Audio stream is added to the existing contents of the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of audio channels to be read.
* @param[in] left Left channel of the audio stream.
* @param[in] right Right channel of the audio stream.
*/
void Audio::addAudio (unsigned int offset, unsigned int length, float const *left, float const *right) {
if (data->buffer == NULL)
return;
if (length > data->bufferLength) {
fprintf(stderr, "Audio::addAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
length = data->bufferLength;
}
unsigned int p = data->bufferPos + offset;
if (p > data->bufferLength)
p -= data->bufferLength;
for (; p < data->bufferLength && length > 0; --length, ++p) {
data->buffer[p].l += *left++;
data->buffer[p].r += *right++;
}
if (length > 0) {
p = 0;
for (; length > 0; --length, ++p) {
data->buffer[p].l += *left++;
data->buffer[p].r += *right++;
}
}
}
/**
* Write a repeated stereo sample (float) to the audio buffer.
* Sample is added to the existing contents of the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of tone.
* @param[in] left Left component.
* @param[in] right Right component.
*/
void Audio::addTone (unsigned int offset, unsigned int length, float const left, float const right) {
if (data->buffer == NULL)
return;
if (length > data->bufferLength) {
fprintf(stderr, "Audio::writeTone length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
length = data->bufferLength;
}
unsigned int p = data->bufferPos + offset;
if (p > data->bufferLength)
p -= data->bufferLength;
for (; p < data->bufferLength && length > 0; --length, ++p) {
data->buffer[p].l += left;
data->buffer[p].r += right;
}
if (length > 0) {
p = 0;
for (; length > 0; --length, ++p) {
data->buffer[p].l += left;
data->buffer[p].r += right;
}
}
}
/**
* Clear a section of the audio buffer.
* @param[in] offset Offset from the start of the audio buffer.
* @param[in] length Length of section to clear.
*/
void Audio::clearAudio(unsigned int offset, unsigned int length) {
if (data->buffer == NULL)
return;
if (length > data->bufferLength) {
fprintf(stderr, "Audio::clearAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
length = data->bufferLength;
}
unsigned int p = data->bufferPos + offset;
if (p > data->bufferLength)
p -= data->bufferLength;
if (length + p < data->bufferLength) {
memset((float*)(data->buffer + p), 0,
sizeof(float) * 2 * length);
} else {
memset((float*)(data->buffer + p), 0,
sizeof(float) * 2 * (data->bufferLength - p));
memset((float*)(data->buffer + p), 0,
sizeof(float) * 2 * (data->bufferLength + p - data->bufferLength));
}
}
/**
* Read audio input into the target buffer.
* @param[in] offset Offset from the start of the input audio buffer to read from.
* @param[in] length Length of the target buffer.
* @param[out] left Left channel of the target buffer.
* @param[out] right Right channel of the target buffer.
*/
void Audio::readAudioInput (unsigned int offset, unsigned int length, float *left, float *right) {
#if WRITE_AUDIO_INPUT_TO_BUFFER
if (data->inputBuffer == NULL)
return;
if (length + offset > data->bufferLength) {
fprintf(stderr, "Audio::readAudioInput length exceeded (%d + %d). Truncating to %d + %d.\n", offset, length, offset, data->bufferLength - offset);
length = data->bufferLength - offset;
}
unsigned int p = data->bufferPos + offset;
if (p > data->bufferLength)
p -= data->bufferLength;
for (; p < data->bufferLength && length > 0; --length, ++p) {
*left++ = data->inputBuffer[p].l;
*right++ = data->inputBuffer[p].r;
}
if (length > 0) {
p = 0;
for (; length > 0; --length, ++p) {
*left++ = data->inputBuffer[p].l;
*right++ = data->inputBuffer[p].r;
}
}
#else
return;
#endif
}