1/*
2 * Copyright 2009-2019, Haiku Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Jacob Secunda
7 *		Marcus Overhagen
8 *		Michael Lotz <mmlr@mlotz.ch>
9 */
10
11#include <Sound.h>
12
13#include <new>
14#include <string.h>
15
16#include <File.h>
17#include <MediaDebug.h>
18
19#include "TrackReader.h"
20
21
22BSound::BSound(void* data, size_t size, const media_raw_audio_format& format,
23	bool freeWhenDone)
24	:	fData(data),
25		fDataSize(size),
26		fFile(NULL),
27		fRefCount(1),
28		fStatus(B_NO_INIT),
29		fFormat(format),
30		fFreeWhenDone(freeWhenDone),
31		fTrackReader(NULL)
32{
33	if (fData == NULL)
34		return;
35
36	fStatus = B_OK;
37}
38
39
40BSound::BSound(const entry_ref* soundFile, bool loadIntoMemory)
41	:	fData(NULL),
42		fDataSize(0),
43		fFile(new(std::nothrow) BFile(soundFile, B_READ_ONLY)),
44		fRefCount(1),
45		fStatus(B_NO_INIT),
46		fFreeWhenDone(false),
47		fTrackReader(NULL)
48{
49	if (fFile == NULL) {
50		fStatus = B_NO_MEMORY;
51		return;
52	}
53
54	fStatus = fFile->InitCheck();
55	if (fStatus != B_OK)
56		return;
57
58	memset(&fFormat, 0, sizeof(fFormat));
59	fTrackReader = new(std::nothrow) BPrivate::BTrackReader(fFile, fFormat);
60	if (fTrackReader == NULL) {
61		fStatus = B_NO_MEMORY;
62		return;
63	}
64
65	fStatus = fTrackReader->InitCheck();
66	if (fStatus != B_OK)
67		return;
68
69	fFormat = fTrackReader->Format();
70	fStatus = B_OK;
71}
72
73
74BSound::BSound(const media_raw_audio_format& format)
75	:	fData(NULL),
76		fDataSize(0),
77		fFile(NULL),
78		fRefCount(1),
79		fStatus(B_ERROR),
80		fFormat(format),
81		fFreeWhenDone(false),
82		fTrackReader(NULL)
83{
84	// unimplemented protected constructor
85	UNIMPLEMENTED();
86}
87
88
89BSound::~BSound()
90{
91	delete fTrackReader;
92	delete fFile;
93
94	if (fFreeWhenDone)
95		free(fData);
96}
97
98
99status_t
100BSound::InitCheck()
101{
102	return fStatus;
103}
104
105
106BSound*
107BSound::AcquireRef()
108{
109	atomic_add(&fRefCount, 1);
110	return this;
111}
112
113
114bool
115BSound::ReleaseRef()
116{
117	if (atomic_add(&fRefCount, -1) == 1) {
118		delete this;
119		return false;
120	}
121
122	// TODO: verify those returns
123	return true;
124}
125
126
127int32
128BSound::RefCount() const
129{
130	return fRefCount;
131}
132
133
134bigtime_t
135BSound::Duration() const
136{
137	float frameRate = fFormat.frame_rate;
138
139	if (frameRate == 0.0)
140		return 0;
141
142	uint32 bytesPerSample = fFormat.format &
143		media_raw_audio_format::B_AUDIO_SIZE_MASK;
144	int64 frameCount = Size() / (fFormat.channel_count * bytesPerSample);
145
146	return (bigtime_t)ceil((1000000LL * frameCount) / frameRate);
147}
148
149
150const media_raw_audio_format&
151BSound::Format() const
152{
153	return fFormat;
154}
155
156
157const void*
158BSound::Data() const
159{
160	return fData;
161}
162
163
164off_t
165BSound::Size() const
166{
167	if (fFile != NULL) {
168		off_t result = 0;
169		fFile->GetSize(&result);
170		return result;
171	}
172
173	return fDataSize;
174}
175
176
177bool
178BSound::GetDataAt(off_t offset, void* intoBuffer, size_t bufferSize,
179	size_t* outUsed)
180{
181	if (intoBuffer == NULL)
182		return false;
183
184	if (fData != NULL) {
185		size_t copySize = MIN(bufferSize, fDataSize - offset);
186		memcpy(intoBuffer, (uint8*)fData + offset, copySize);
187		if (outUsed != NULL)
188			*outUsed = copySize;
189		return true;
190	}
191
192	if (fTrackReader != NULL) {
193		int32 frameSize = fTrackReader->FrameSize();
194		int64 frameCount = fTrackReader->CountFrames();
195		int64 startFrame = offset / frameSize;
196		if (startFrame > frameCount)
197			return false;
198
199		if (fTrackReader->SeekToFrame(&startFrame) != B_OK)
200			return false;
201
202		off_t bufferOffset = offset - startFrame * frameSize;
203		int64 directStartFrame = (offset + frameSize - 1) / frameSize;
204		int64 directFrameCount = (offset + bufferSize - directStartFrame
205			* frameSize) / frameSize;
206
207		if (bufferOffset != 0) {
208			int64 indirectFrameCount = directStartFrame - startFrame;
209			size_t indirectSize = indirectFrameCount * frameSize;
210			void* buffer = malloc(indirectSize);
211			if (buffer == NULL)
212				return false;
213
214			if (fTrackReader->ReadFrames(buffer, indirectFrameCount) != B_OK) {
215				free(buffer);
216				return false;
217			}
218
219			memcpy(intoBuffer, (uint8*)buffer + bufferOffset,
220				indirectSize - bufferOffset);
221			if (outUsed != NULL)
222				*outUsed = indirectSize - bufferOffset;
223
224			free(buffer);
225		} else if (outUsed != NULL)
226			*outUsed = 0;
227
228		if (fTrackReader->ReadFrames((uint8*)intoBuffer + bufferOffset,
229			directFrameCount) != B_OK)
230			return false;
231
232		if (outUsed != NULL)
233			*outUsed += directFrameCount * frameSize;
234
235		return true;
236	}
237
238	return false;
239}
240
241
242status_t
243BSound::BindTo(BSoundPlayer* player, const media_raw_audio_format& format)
244{
245	UNIMPLEMENTED();
246	return B_ERROR;
247}
248
249
250status_t
251BSound::UnbindFrom(BSoundPlayer* player)
252{
253	UNIMPLEMENTED();
254	return B_ERROR;
255}
256
257
258status_t
259BSound::Perform(int32 code, ...)
260{
261	UNIMPLEMENTED();
262	return B_ERROR;
263}
264
265
266status_t BSound::_Reserved_Sound_0(void*) { return B_ERROR; }
267status_t BSound::_Reserved_Sound_1(void*) { return B_ERROR; }
268status_t BSound::_Reserved_Sound_2(void*) { return B_ERROR; }
269status_t BSound::_Reserved_Sound_3(void*) { return B_ERROR; }
270status_t BSound::_Reserved_Sound_4(void*) { return B_ERROR; }
271status_t BSound::_Reserved_Sound_5(void*) { return B_ERROR; }
272