1/**********************************************************************************************************************************
2 *
3 *   OpenAL cross platform audio library
4 *	Copyright (c) 2004, Apple Computer, Inc., Copyright (c) 2012, Apple Inc. All rights reserved.
5 *
6 *   Redistribution and use in source and binary forms, with or without modification, are permitted provided
7 *   that the following conditions are met:
8 *
9 *   1.  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
10 *   2.  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
11 *       disclaimer in the documentation and/or other materials provided with the distribution.
12 *   3.  Neither the name of Apple Inc. ("Apple") nor the names of its contributors may be used to endorse or promote
13 *       products derived from this software without specific prior written permission.
14 *
15 *   THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS
17 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
18 *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
19 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
20 *   USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 *
22 **********************************************************************************************************************************/
23
24#include "oalRingBuffer.h"
25
26// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28#pragma mark ***** OALRingBuffer *****
29// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30OALRingBuffer::OALRingBuffer() : mBuffer(NULL), mCapacityFrames(0), mCapacityBytes(0)
31{
32}
33
34OALRingBuffer::OALRingBuffer(UInt32 bytesPerFrame, UInt32 capacityFrames) :
35mBuffer(NULL)
36{
37    Allocate(bytesPerFrame, capacityFrames);
38}
39
40OALRingBuffer::~OALRingBuffer()
41{
42    Deallocate();
43}
44
45void	OALRingBuffer::Allocate(UInt32 bytesPerFrame, UInt32 capacityFrames)
46{
47	Deallocate();
48	mBytesPerFrame = bytesPerFrame;
49	mCapacityFrames = capacityFrames;
50	mCapacityBytes = bytesPerFrame * capacityFrames;
51	mBuffer = (Byte *)malloc(mCapacityBytes);
52	Clear();
53}
54
55void	OALRingBuffer::Deallocate()
56{
57	if (mBuffer) {
58		free(mBuffer);
59		mBuffer = NULL;
60	}
61	mCapacityBytes = 0;
62	mCapacityFrames = 0;
63	Clear();
64}
65
66void	OALRingBuffer::Clear()
67{
68	if (mBuffer)
69		memset(mBuffer, 0, mCapacityBytes);
70	mStartOffset = 0;
71	mStartFrame = 0;
72	mEndFrame = 0;
73}
74
75bool	OALRingBuffer::Store(const Byte *data, UInt32 nFrames, SInt64 startFrame)
76{
77	if (nFrames > mCapacityFrames) return false;
78
79	// reading and writing could well be in separate threads
80
81	SInt64 endFrame = startFrame + nFrames;
82	if (startFrame >= mEndFrame + mCapacityFrames)
83		// writing more than one buffer ahead -- fine but that means that everything we have is now too far in the past
84		Clear();
85
86	if (mStartFrame == 0) {
87		// empty buffer
88		mStartOffset = 0;
89		mStartFrame = startFrame;
90		mEndFrame = endFrame;
91		memcpy(mBuffer, data, nFrames * mBytesPerFrame);
92	} else {
93		UInt32 offset0, offset1, nBytes;
94		if (endFrame > mEndFrame) {
95			// advancing (as will be usual with sequential stores)
96
97			if (startFrame > mEndFrame) {
98				// we are skipping some samples, so zero the range we are skipping
99				offset0 = FrameOffset(mEndFrame);
100				offset1 = FrameOffset(startFrame);
101				if (offset0 < offset1)
102					memset(mBuffer + offset0, 0, offset1 - offset0);
103				else {
104					nBytes = mCapacityBytes - offset0;
105					memset(mBuffer + offset0, 0, nBytes);
106					memset(mBuffer, 0, offset1);
107				}
108			}
109			mEndFrame = endFrame;
110
111			// except for the case of not having wrapped yet, we will normally
112			// have to advance the start
113			SInt64 newStart = mEndFrame - mCapacityFrames;
114			if (newStart > mStartFrame) {
115				mStartOffset = (mStartOffset + (newStart - mStartFrame) * mBytesPerFrame) % mCapacityBytes;
116				mStartFrame = newStart;
117			}
118		}
119		// now everything is lined up and we can just write the new data
120		offset0 = FrameOffset(startFrame);
121		offset1 = FrameOffset(endFrame);
122		if (offset0 < offset1)
123			memcpy(mBuffer + offset0, data, offset1 - offset0);
124		else {
125			nBytes = mCapacityBytes - offset0;
126			memcpy(mBuffer + offset0, data, nBytes);
127			memcpy(mBuffer, data + nBytes, offset1);
128		}
129	}
130	return true;
131}
132
133OSStatus	OALRingBuffer::Fetch(Byte *data, UInt32 nFrames, SInt64 startFrame)
134{
135	SInt64 endFrame = startFrame + nFrames;
136	if (startFrame < mStartFrame || endFrame > mEndFrame) {
137		return -1;
138	}
139
140	UInt32 offset0 = FrameOffset(startFrame);
141	UInt32 offset1 = FrameOffset(endFrame);
142
143	if (offset0 < offset1)
144		memcpy(data, mBuffer + offset0, offset1 - offset0);
145	else {
146		UInt32 nBytes = mCapacityBytes - offset0;
147		memcpy(data, mBuffer + offset0, nBytes);
148		memcpy(data + nBytes, mBuffer, offset1);
149	}
150	return noErr;
151}
152
153Byte *	OALRingBuffer::GetFramePtr(SInt64 frameNumber, UInt32 &outNFrames)
154{
155	if (frameNumber < mStartFrame || frameNumber >= mEndFrame) {
156		outNFrames = 0;
157		return NULL;
158	}
159	UInt32 offset0 = FrameOffset(frameNumber);
160	UInt32 offset1 = FrameOffset(mEndFrame);
161	if (offset0 < offset1) {
162		outNFrames = static_cast<UInt32>(mEndFrame - frameNumber);
163	} else {
164		outNFrames = (mCapacityBytes - offset0) / mBytesPerFrame;
165	}
166	return mBuffer + offset0;
167}
168