-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathREACAudioClip.cpp
More file actions
277 lines (253 loc) · 13.3 KB
/
Copy pathREACAudioClip.cpp
File metadata and controls
277 lines (253 loc) · 13.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
/*
* REACAudioClip.cpp
* REAC
*
* Created by Per Eckerdal on 02/01/2011.
* Copyright 2011 Per Eckerdal. All rights reserved.
*
*
* This file is part of the OS X REAC driver.
*
* The OS X REAC driver is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* The OS X REAC driver is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OS X REAC driver. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#include "REACAudioEngine.h"
#include <IOKit/IOLib.h>
#include "PCMBlitterLib.h"
// The function clipOutputSamples() is called to clip and convert samples from the float mix buffer into the actual
// hardware sample buffer. The samples to be clipped, are guaranteed not to wrap from the end of the buffer to the
// beginning.
//
// The parameters are as follows:
// mixBuf - a pointer to the beginning of the float mix buffer - its size is based on the number of sample frames
// times the number of channels for the stream
// sampleBuf - a pointer to the beginning of the hardware formatted sample buffer - this is the same buffer passed
// to the IOAudioStream using setSampleBuffer()
// firstSampleFrame - this is the index of the first sample frame to perform the clipping and conversion on
// numSampleFrames - the total number of sample frames to clip and convert
// streamFormat - the current format of the IOAudioStream this function is operating on
// audioStream - the audio stream this function is operating on
IOReturn REACAudioEngine::clipOutputSamples(const void* inMixBuffer, void* destBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat* streamFormat, IOAudioStream* /*audioStream*/)
{
// figure out what sort of blit we need to do
if((streamFormat->fSampleFormat == kIOAudioStreamSampleFormatLinearPCM) && streamFormat->fIsMixable)
{
// it's mixable linear PCM, which means we will be calling a blitter, which works in samples not frames
Float32* theMixBuffer = (Float32*)inMixBuffer;
UInt32 theFirstSample = firstSampleFrame * streamFormat->fNumChannels;
UInt32 theNumberSamples = numSampleFrames * streamFormat->fNumChannels;
if(streamFormat->fNumericRepresentation == kIOAudioStreamNumericRepresentationSignedInt)
{
// it's some kind of signed integer, which we handle as some kind of even byte length
bool nativeEndianInts;
#if TARGET_RT_BIG_ENDIAN
nativeEndianInts = (streamFormat->fByteOrder == kIOAudioStreamByteOrderBigEndian);
#else
nativeEndianInts = (streamFormat->fByteOrder == kIOAudioStreamByteOrderLittleEndian);
#endif
switch(streamFormat->fBitWidth)
{
case 8:
{
IOLog("REACAudioEngine::clipOutputSamples(): Can't handle signed integers "\
"with a bit width of 8 at the moment.\n");
}
break;
case 16:
{
SInt16* theTargetBuffer = (SInt16*)destBuf;
if (nativeEndianInts)
Float32ToNativeInt16(&(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples);
else
Float32ToSwapInt16(&(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples);
}
break;
case 24:
{
UInt8* theTargetBuffer = (UInt8*)destBuf;
if (nativeEndianInts)
Float32ToNativeInt24(&(theMixBuffer[theFirstSample]), &(theTargetBuffer[3*theFirstSample]), theNumberSamples);
else
Float32ToSwapInt24(&(theMixBuffer[theFirstSample]), &(theTargetBuffer[3*theFirstSample]), theNumberSamples);
}
break;
case 32:
{
SInt32* theTargetBuffer = (SInt32*)destBuf;
if (nativeEndianInts)
Float32ToNativeInt32(&(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples);
else
Float32ToSwapInt32(&(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples);
}
break;
default:
IOLog("REACAudioEngine::clipOutputSamples(): Can't handle signed integers "\
"with a bit width of %d.\n", streamFormat->fBitWidth);
break;
}
}
else if(streamFormat->fNumericRepresentation == kIOAudioStreamNumericRepresentationIEEE754Float)
{
// it is some kind of floating point format
#if TARGET_RT_BIG_ENDIAN
if((streamFormat->fBitWidth == 32) && (streamFormat->fBitDepth == 32) && (streamFormat->fByteOrder == kIOAudioStreamByteOrderBigEndian))
#else
if((streamFormat->fBitWidth == 32) && (streamFormat->fBitDepth == 32) && (streamFormat->fByteOrder == kIOAudioStreamByteOrderLittleEndian))
#endif
{
// it's Float32, so we are just going to copy the data
Float32* theTargetBuffer = (Float32*)destBuf;
memcpy(&(theTargetBuffer[theFirstSample]), &(theMixBuffer[theFirstSample]), theNumberSamples * sizeof(Float32));
}
else
{
IOLog("REACAudioEngine::clipOutputSamples(): Can't handle floats with a bit width "\
"of %d, bit depth of %d, and/or the given byte order.\n", streamFormat->fBitWidth,
streamFormat->fBitDepth);
}
}
}
else
{
// it's not linear PCM or it's not mixable, so just copy the data into the target buffer
SInt8* theMixBuffer = (SInt8*)inMixBuffer;
SInt8* theTargetBuffer = (SInt8*)destBuf;
UInt32 theFirstByte = firstSampleFrame * (streamFormat->fBitWidth / 8) * streamFormat->fNumChannels;
UInt32 theNumberBytes = numSampleFrames * (streamFormat->fBitWidth / 8) * streamFormat->fNumChannels;
memcpy(&(theTargetBuffer[theFirstByte]), &(theMixBuffer[theFirstByte]), theNumberBytes);
}
return kIOReturnSuccess;
}
// The function convertInputSamples() is responsible for converting from the hardware format
// in the input sample buffer to float samples in the destination buffer and scale the samples
// to a range of -1.0 to 1.0. This function is guaranteed not to have the samples wrapped
// from the end of the buffer to the beginning.
// This function only needs to be implemented if the device has any input IOAudioStreams
//
// The parameters are as follows:
// sampleBuf - a pointer to the beginning of the hardware formatted sample buffer - this is the same buffer passed
// to the IOAudioStream using setSampleBuffer()
// destBuf - a pointer to the float destination buffer - this is the buffer that the CoreAudio.framework uses
// its size is numSampleFrames * numChannels * sizeof(float)
// firstSampleFrame - this is the index of the first sample frame to the input conversion on
// numSampleFrames - the total number of sample frames to convert and scale
// streamFormat - the current format of the IOAudioStream this function is operating on
// audioStream - the audio stream this function is operating on
IOReturn REACAudioEngine::convertInputSamples(const void* sampleBuf, void* destBuf, UInt32 firstSampleFrame,
UInt32 numSampleFrames, const IOAudioStreamFormat* streamFormat,
IOAudioStream* /*audioStream*/) {
{ // Check if we'll have an audio drop out, and log if that's the case.
const int numChannels = inputStream->format.fNumChannels;
const int resolution = inputStream->format.fBitWidth/8;
const int bytesPerSample = resolution * numChannels;
// This is the place in the buffer where we're currently receiving data from the network
UInt8 *inBufferPosition = (UInt8 *)mInBuffer + currentBlock*blockSize*bytesPerSample;
// Pointers to where we'll begin and stop writing
UInt8 *bufferBeginWritePosition = ((UInt8 *)sampleBuf) + firstSampleFrame*bytesPerSample;
UInt8 *bufferStopWritePosition = bufferBeginWritePosition + numSampleFrames*bytesPerSample;
// Check if we're going to cross inBufferPosition (this leads to audio dropouts)
if (inBufferPosition >= bufferBeginWritePosition && inBufferPosition < bufferStopWritePosition) {
IOLog("REACAudioEngine::convertInputSamples(): Audio drop-out! (by %d samples, when converting %d samples)\n",
(int) (firstSampleFrame+numSampleFrames - currentBlock*blockSize), (int) numSampleFrames);
}
}
// figure out what sort of blit we need to do
if((streamFormat->fSampleFormat == kIOAudioStreamSampleFormatLinearPCM) && streamFormat->fIsMixable)
{
// it's linear PCM, which means the target is Float32 and we will be calling a blitter, which works in samples not frames
Float32* theTargetBuffer = (Float32*)destBuf;
const UInt32 theFirstSample = firstSampleFrame * inputStream->format.fNumChannels;
const UInt32 theNumberSamples = numSampleFrames * inputStream->format.fNumChannels;
if(streamFormat->fNumericRepresentation == kIOAudioStreamNumericRepresentationSignedInt)
{
// it's some kind of signed integer, which we handle as some kind of even byte length
bool nativeEndianInts;
#if TARGET_RT_BIG_ENDIAN
nativeEndianInts = (streamFormat->fByteOrder == kIOAudioStreamByteOrderBigEndian);
#else
nativeEndianInts = (streamFormat->fByteOrder == kIOAudioStreamByteOrderLittleEndian);
#endif
switch(streamFormat->fBitWidth)
{
case 8:
{
IOLog("REACAudioEngine::convertInputSamples(): can't handle signed "\
"integers with a bit width of 8 at the moment.\n");
}
break;
case 16:
{
SInt16* theSourceBuffer = (SInt16*)sampleBuf;
if (nativeEndianInts)
NativeInt16ToFloat32(&(theSourceBuffer[theFirstSample]), theTargetBuffer, theNumberSamples);
else
SwapInt16ToFloat32(&(theSourceBuffer[theFirstSample]), theTargetBuffer, theNumberSamples);
}
break;
case 24:
{
UInt8* theSourceBuffer = (UInt8*)sampleBuf;
if (nativeEndianInts)
NativeInt24ToFloat32(&(theSourceBuffer[3*theFirstSample]), theTargetBuffer, theNumberSamples);
else
SwapInt24ToFloat32(&(theSourceBuffer[3*theFirstSample]), theTargetBuffer, theNumberSamples);
}
break;
case 32:
{
SInt32* theSourceBuffer = (SInt32*)sampleBuf;
if (nativeEndianInts)
NativeInt32ToFloat32(&(theSourceBuffer[theFirstSample]), theTargetBuffer, theNumberSamples);
else
SwapInt32ToFloat32(&(theSourceBuffer[theFirstSample]), theTargetBuffer, theNumberSamples);
}
break;
default:
IOLog("REACAudioEngine::convertInputSamples(): can't handle signed integers with a bit width of %d.\n",
streamFormat->fBitWidth);
break;
}
}
else if(streamFormat->fNumericRepresentation == kIOAudioStreamNumericRepresentationIEEE754Float)
{
// it is some kind of floating point format
#if TARGET_RT_BIG_ENDIAN
if((streamFormat->fBitWidth == 32) && (streamFormat->fBitDepth == 32) && (streamFormat->fByteOrder == kIOAudioStreamByteOrderBigEndian))
#else
if((streamFormat->fBitWidth == 32) && (streamFormat->fBitDepth == 32) && (streamFormat->fByteOrder == kIOAudioStreamByteOrderLittleEndian))
#endif
{
// it's Float32, so we are just going to copy the data
Float32* theSourceBuffer = (Float32*)sampleBuf;
memcpy(theTargetBuffer, &(theSourceBuffer[theFirstSample]), theNumberSamples * sizeof(Float32));
}
else
{
IOLog("REACEngine::convertInputSamples(): can't handle floats with a bit width of %d, "\
"bit depth of %d, and/or the given byte order.\n",
streamFormat->fBitWidth, streamFormat->fBitDepth);
}
}
}
else
{
// it's not linear PCM or it's not mixable, so just copy the data into the target buffer
SInt8* theSourceBuffer = (SInt8*)sampleBuf;
UInt32 theFirstByte = firstSampleFrame * (streamFormat->fBitWidth / 8) * streamFormat->fNumChannels;
UInt32 theNumberBytes = numSampleFrames * (streamFormat->fBitWidth / 8) * streamFormat->fNumChannels;
memcpy(destBuf, &(theSourceBuffer[theFirstByte]), theNumberBytes);
}
return kIOReturnSuccess;
}