1 /*
2 Copyright (C) 2016 Apple Inc. All Rights Reserved.
3 See LICENSE.txt for this sample’s licensing information
4 
5 Abstract:
6 Part of Core Audio AUBase Classes
7 */
8 
9 #ifndef __AUBuffer_h__
10 #define __AUBuffer_h__
11 
12 #include <TargetConditionals.h>
13 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
14 	#include <AudioUnit/AudioUnit.h>
15 #else
16 	#include <AudioUnit.h>
17 #endif
18 
19 #include <string.h>
20 #include "CAStreamBasicDescription.h"
21 #include "CAAutoDisposer.h"
22 #include "CADebugMacros.h"
23 
24 // make this usable outside the stricter context of AudiUnits
25 #ifndef COMPONENT_THROW
26 	#define COMPONENT_THROW(err) \
27 		do { DebugMessage(#err); throw static_cast<OSStatus>(err); } while (0)
28 #endif
29 
30 
31 	/*! @class AUBufferList */
32 class AUBufferList {
33 	enum EPtrState {
34 		kPtrsInvalid,
35 		kPtrsToMyMemory,
36 		kPtrsToExternalMemory
37 	};
38 public:
39 	/*! @ctor AUBufferList */
AUBufferList()40 	AUBufferList() : mPtrState(kPtrsInvalid), mExternalMemory(false), mPtrs(NULL), mMemory(NULL),
41 		mAllocatedStreams(0), mAllocatedFrames(0), mAllocatedBytes(0) { }
42 	/*! @dtor ~AUBufferList */
43 	~AUBufferList();
44 
45 	/*! @method PrepareBuffer */
46 	AudioBufferList &	PrepareBuffer(const CAStreamBasicDescription &format, UInt32 nFrames);
47 	/*! @method PrepareNullBuffer */
48 	AudioBufferList &	PrepareNullBuffer(const CAStreamBasicDescription &format, UInt32 nFrames);
49 
50 	/*! @method SetBufferList */
SetBufferList(const AudioBufferList & abl)51 	AudioBufferList &	SetBufferList(const AudioBufferList &abl) {
52 							if (mAllocatedStreams < abl.mNumberBuffers)
53 								COMPONENT_THROW(-1);
54 							mPtrState = kPtrsToExternalMemory;
55 							memcpy(mPtrs, &abl, (char *)&abl.mBuffers[abl.mNumberBuffers] - (char *)&abl);
56 							return *mPtrs;
57 						}
58 
59 	/*! @method SetBuffer */
SetBuffer(UInt32 index,const AudioBuffer & ab)60 	void				SetBuffer(UInt32 index, const AudioBuffer &ab) {
61 							if (mPtrState == kPtrsInvalid || index >= mPtrs->mNumberBuffers)
62 								COMPONENT_THROW(-1);
63 							mPtrState = kPtrsToExternalMemory;
64 							mPtrs->mBuffers[index] = ab;
65 						}
66 
67 	/*! @method InvalidateBufferList */
InvalidateBufferList()68 	void				InvalidateBufferList() { mPtrState = kPtrsInvalid; }
69 
70 	/*! @method GetBufferList */
GetBufferList()71 	AudioBufferList &	GetBufferList() const {
72 							if (mPtrState == kPtrsInvalid)
73 								COMPONENT_THROW(-1);
74 							return *mPtrs;
75 						}
76 
77 	/*! @method CopyBufferListTo */
CopyBufferListTo(AudioBufferList & abl)78 	void				CopyBufferListTo(AudioBufferList &abl) const {
79 							if (mPtrState == kPtrsInvalid)
80 								COMPONENT_THROW(-1);
81 							memcpy(&abl, mPtrs, (char *)&abl.mBuffers[abl.mNumberBuffers] - (char *)&abl);
82 						}
83 
84 	/*! @method CopyBufferContentsTo */
CopyBufferContentsTo(AudioBufferList & abl)85 	void				CopyBufferContentsTo(AudioBufferList &abl) const {
86 							if (mPtrState == kPtrsInvalid)
87 								COMPONENT_THROW(-1);
88 							const AudioBuffer *srcbuf = mPtrs->mBuffers;
89 							AudioBuffer *destbuf = abl.mBuffers;
90 
91 							for (UInt32 i = 0; i < abl.mNumberBuffers; ++i, ++srcbuf, ++destbuf) {
92 								if (i >= mPtrs->mNumberBuffers) // duplicate last source to additional outputs [4341137]
93 									--srcbuf;
94 								if (destbuf->mData != srcbuf->mData)
95 									memmove(destbuf->mData, srcbuf->mData, srcbuf->mDataByteSize);
96 								destbuf->mDataByteSize = srcbuf->mDataByteSize;
97 							}
98 						}
99 
100 	/*! @method Allocate */
101 	void				Allocate(const CAStreamBasicDescription &format, UInt32 nFrames);
102 	/*! @method Deallocate */
103 	void				Deallocate();
104 
105 	/*! @method UseExternalBuffer */
106 	void				UseExternalBuffer(const CAStreamBasicDescription &format, const AudioUnitExternalBuffer &buf);
107 
108 	// AudioBufferList utilities
109 	/*! @method ZeroBuffer */
ZeroBuffer(AudioBufferList & abl)110 	static void			ZeroBuffer(AudioBufferList &abl) {
111 							AudioBuffer *buf = abl.mBuffers;
112 							for (UInt32 i = abl.mNumberBuffers ; i--; ++buf)
113 								memset(buf->mData, 0, buf->mDataByteSize);
114 						}
115 #if DEBUG
116 	/*! @method PrintBuffer */
117 	static void			PrintBuffer(const char *label, int subscript, const AudioBufferList &abl, UInt32 nFrames = 8, bool asFloats = true);
118 #endif
119 
120 	/*! @method GetAllocatedFrames */
GetAllocatedFrames()121 	UInt32				GetAllocatedFrames() const { return mAllocatedFrames; }
122 
123 private:
124 	/*! @ctor AUBufferList */
AUBufferList(AUBufferList &)125 	AUBufferList(AUBufferList &) { }	// prohibit copy constructor
126 
127 	/*! @var mPtrState */
128 	EPtrState					mPtrState;
129 	/*! @var mExternalMemory */
130 	bool						mExternalMemory;
131 	/*! @var mPtrs */
132 	AudioBufferList *			mPtrs;
133 	/*! @var mMemory */
134 	Byte *						mMemory;
135 	/*! @var mAllocatedStreams */
136 	UInt32						mAllocatedStreams;
137 	/*! @var mAllocatedFrames */
138 	UInt32						mAllocatedFrames;
139 	/*! @var mAllocatedBytes */
140 	UInt32						mAllocatedBytes;
141 };
142 
143 
144 // Allocates an array of samples (type T), to be optimally aligned for the processor
145 	/*! @class TAUBuffer */
146 template <class T>
147 class TAUBuffer {
148 public:
149 	enum {
150 		kAlignInterval = 0x10,
151 		kAlignMask = kAlignInterval - 1
152 	};
153 
154 	/*! @ctor TAUBuffer.0 */
TAUBuffer()155 	TAUBuffer() :	mMemObject(NULL), mAlignedBuffer(NULL), mBufferSizeBytes(0)
156 	{
157 	}
158 
159 	/*! @ctor TAUBuffer.1 */
TAUBuffer(UInt32 numElems,UInt32 numChannels)160 	TAUBuffer(UInt32 numElems, UInt32 numChannels) :	mMemObject(NULL), mAlignedBuffer(NULL),
161 														mBufferSizeBytes(0)
162 	{
163 		Allocate(numElems, numChannels);
164 	}
165 
166 	/*! @dtor ~TAUBuffer */
~TAUBuffer()167 	~TAUBuffer()
168 	{
169 		Deallocate();
170 	}
171 
172 	/*! @method Allocate */
Allocate(UInt32 numElems)173 	void	Allocate(UInt32 numElems)			// can also re-allocate
174 	{
175 		UInt32 reqSize = numElems * sizeof(T);
176 
177 		if (mMemObject != NULL && reqSize == mBufferSizeBytes)
178 			return;	// already allocated
179 
180 		mBufferSizeBytes = reqSize;
181 		mMemObject = CA_realloc(mMemObject, reqSize);
182 		UInt32 misalign = (uintptr_t)mMemObject & kAlignMask;
183 		if (misalign) {
184 			mMemObject = CA_realloc(mMemObject, reqSize + kAlignMask);
185 			mAlignedBuffer = (T *)((char *)mMemObject + kAlignInterval - misalign);
186 		} else
187 			mAlignedBuffer = (T *)mMemObject;
188 	}
189 
190 	/*! @method Deallocate */
Deallocate()191 	void	Deallocate()
192 	{
193 		if (mMemObject == NULL) return;			// so this method has no effect if we're using
194 												// an external buffer
195 
196 		free(mMemObject);
197 		mMemObject = NULL;
198 		mAlignedBuffer = NULL;
199 		mBufferSizeBytes = 0;
200 	}
201 
202 	/*! @method AllocateClear */
AllocateClear(UInt32 numElems)203 	void	AllocateClear(UInt32 numElems)		// can also re-allocate
204 	{
205 		Allocate(numElems);
206 		Clear();
207 	}
208 
209 	/*! @method Clear */
Clear()210 	void	Clear()
211 	{
212 		memset(mAlignedBuffer, 0, mBufferSizeBytes);
213 	}
214 
215 	// accessors
216 
217 	/*! @method operator T *()@ */
218 	operator T *()				{ return mAlignedBuffer; }
219 
220 private:
221 	/*! @var mMemObject */
222 	void *		mMemObject;			// null when using an external buffer
223 	/*! @var mAlignedBuffer */
224 	T *			mAlignedBuffer;		// always valid once allocated
225 	/*! @var mBufferSizeBytes */
226 	UInt32		mBufferSizeBytes;
227 };
228 
229 #endif // __AUBuffer_h__
230