1/*
2 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files or portions
6 * thereof (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so, subject
10 * to the following conditions:
11 *
12 *  * Redistributions of source code must retain the above copyright notice,
13 *    this list of conditions and the following disclaimer.
14 *
15 *  * Redistributions in binary form must reproduce the above copyright notice
16 *    in the  binary, as well as this list of conditions and the following
17 *    disclaimer in the documentation and/or other materials provided with
18 *    the distribution.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 * THE SOFTWARE.
27 *
28 */
29
30/*
31 * The undocumented BTrackReader class,
32 * used by BSound and the GameSound classes
33 */
34
35#include <File.h>
36#include <MediaTrack.h>
37#include <MediaFile.h>
38#include <string.h>
39#include "TrackReader.h"
40#include "MediaDebug.h"
41
42namespace BPrivate
43{
44
45BTrackReader::BTrackReader(BMediaTrack *track, media_raw_audio_format const &format) :
46	fFrameSize(0),
47	fBuffer(0),
48	fBufferOffset(0),
49	fBufferUsedSize(0),
50	fMediaFile(0),
51	fMediaTrack(0),
52	fFormat(format)
53{
54	CALLED();
55	if (track == NULL)
56		return;
57	if (track->InitCheck() != B_OK)
58		return;
59
60	SetToTrack(track);
61
62	// if the track was not set abort now
63	if (fMediaTrack == 0)
64		return;
65
66	fBuffer = new uint8[fFormat.buffer_size];
67	fFrameSize = fFormat.channel_count * (fFormat.format & media_raw_audio_format::B_AUDIO_SIZE_MASK);
68
69	TRACE("BTrackReader::BTrackReader successful\n");
70}
71
72
73BTrackReader::BTrackReader(BFile *file, media_raw_audio_format const &format) :
74	fFrameSize(0),
75	fBuffer(0),
76	fBufferOffset(0),
77	fBufferUsedSize(0),
78	fMediaFile(0),
79	fMediaTrack(0),
80	fFormat(format)
81{
82	CALLED();
83	if (file == NULL)
84		return;
85	if (file->InitCheck() != B_OK)
86		return;
87
88	fMediaFile = new BMediaFile(file);
89	if (fMediaFile->InitCheck() != B_OK)
90		return;
91
92	int count = fMediaFile->CountTracks();
93	if (count == 0) {
94		ERROR("BTrackReader: no tracks in file\n");
95		return;
96	}
97
98	// find the first audio track
99	BMediaTrack *track;
100	BMediaTrack *audiotrack = 0;
101	for (int tr = 0; tr < count; tr++) {
102		track = fMediaFile->TrackAt(tr);
103		if (track == 0 || track->InitCheck() != B_OK)
104			continue;
105		media_format fmt;
106		if (track->DecodedFormat(&fmt) != B_OK)
107			continue;
108		if (fmt.type == B_MEDIA_RAW_AUDIO) {
109			audiotrack = track;
110			break;
111		}
112		fMediaFile->ReleaseTrack(track);
113	}
114	if (audiotrack == 0) {
115		ERROR("BTrackReader: no audio track in file\n");
116		return;
117	}
118
119	SetToTrack(audiotrack);
120
121	// if the track was not set, release it
122	if (fMediaTrack == 0) {
123		fMediaFile->ReleaseTrack(audiotrack);
124		return;
125	}
126
127	fBuffer = new uint8[fFormat.buffer_size];
128	fFrameSize = fFormat.channel_count * (fFormat.format & media_raw_audio_format::B_AUDIO_SIZE_MASK);
129
130	TRACE("BTrackReader::BTrackReader successful\n");
131}
132
133
134void
135BTrackReader::SetToTrack(BMediaTrack *track)
136{
137	media_format fmt;
138	fmt.Clear(); //wildcard
139	memcpy(&fmt.u.raw_audio, &fFormat, sizeof(fFormat));
140	fmt.type = B_MEDIA_RAW_AUDIO;
141	//try to find a output format
142	if (track->DecodedFormat(&fmt) == B_OK) {
143		memcpy(&fFormat, &fmt.u.raw_audio, sizeof(fFormat));
144		fMediaTrack = track;
145		return;
146	}
147
148	//try again
149	fmt.u.raw_audio.buffer_size = 2 * 4096;
150	fmt.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
151	if (track->DecodedFormat(&fmt) == B_OK) {
152		memcpy(&fFormat, &fmt.u.raw_audio, sizeof(fFormat));
153		fMediaTrack = track;
154		return;
155	}
156
157	//try again
158	fmt.u.raw_audio.buffer_size = 4096;
159	fmt.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
160	if (track->DecodedFormat(&fmt) == B_OK) {
161		memcpy(&fFormat, &fmt.u.raw_audio, sizeof(fFormat));
162		fMediaTrack = track;
163		return;
164	}
165
166	//we have failed
167	ERROR("BTrackReader::SetToTrack failed\n");
168}
169
170
171BTrackReader::~BTrackReader()
172{
173	CALLED();
174	if (fMediaFile && fMediaTrack)
175		fMediaFile->ReleaseTrack(fMediaTrack);
176	delete fMediaFile;
177	delete[] fBuffer;
178}
179
180
181status_t
182BTrackReader::InitCheck()
183{
184	CALLED();
185	return fMediaTrack ? fMediaTrack->InitCheck() : B_ERROR;
186}
187
188
189int64
190BTrackReader::CountFrames(void)
191{
192	CALLED();
193	return fMediaTrack ? fMediaTrack->CountFrames() : 0;
194}
195
196
197const media_raw_audio_format &
198BTrackReader::Format(void) const
199{
200	CALLED();
201	return fFormat;
202}
203
204
205int32
206BTrackReader::FrameSize(void)
207{
208	CALLED();
209	return fFrameSize;
210}
211
212
213status_t
214BTrackReader::ReadFrames(void* in_buffer, int32 frame_count)
215{
216	CALLED();
217
218	uint8* buffer = static_cast<uint8*>(in_buffer);
219	int32 bytes_to_read = frame_count * fFrameSize;
220
221	status_t last_status = B_OK;
222	while (bytes_to_read > 0) {
223		int32 bytes_to_copy = min_c(fBufferUsedSize, bytes_to_read);
224		if (bytes_to_copy > 0) {
225			memcpy(buffer, fBuffer + fBufferOffset, bytes_to_copy);
226			buffer += bytes_to_copy;
227			bytes_to_read -= bytes_to_copy;
228			fBufferOffset += bytes_to_copy;
229			fBufferUsedSize -= bytes_to_copy;
230		}
231		if (last_status != B_OK)
232			break;
233		if (fBufferUsedSize == 0) {
234			int64 outFrameCount;
235			last_status = fMediaTrack->ReadFrames(fBuffer,
236				&outFrameCount);
237			fBufferOffset = 0;
238			fBufferUsedSize = outFrameCount * fFrameSize;
239		}
240	}
241	if (bytes_to_read > 0) {
242		memset(buffer, 0, bytes_to_read);
243		return B_LAST_BUFFER_ERROR;
244	}
245	return B_OK;
246}
247
248
249status_t
250BTrackReader::SeekToFrame(int64* in_out_frame)
251{
252	CALLED();
253	status_t s = fMediaTrack->SeekToFrame(in_out_frame, B_MEDIA_SEEK_CLOSEST_BACKWARD);
254	if (s != B_OK)
255		return s;
256	fBufferUsedSize = 0;
257	fBufferOffset = 0;
258	return B_OK;
259}
260
261
262BMediaTrack*
263BTrackReader::Track(void)
264{
265	CALLED();
266	return fMediaTrack;
267}
268
269}; //namespace BPrivate
270
271