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 <MediaTrack.h>
36#include <MediaFile.h>
37#include <File.h>
38#include <string.h>
39#include "TrackReader.h"
40#include "debug.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
72BTrackReader::BTrackReader(BFile *file, media_raw_audio_format const &format) :
73	fFrameSize(0),
74	fBuffer(0),
75	fBufferOffset(0),
76	fBufferUsedSize(0),
77	fMediaFile(0),
78	fMediaTrack(0),
79	fFormat(format)
80{
81	CALLED();
82	if (file == NULL)
83		return;
84	if (file->InitCheck() != B_OK)
85		return;
86
87	fMediaFile = new BMediaFile(file);
88	if (fMediaFile->InitCheck() != B_OK)
89		return;
90
91	int count = fMediaFile->CountTracks();
92	if (count == 0) {
93		ERROR("BTrackReader: no tracks in file\n");
94		return;
95	}
96
97	// find the first audio track
98	BMediaTrack *track;
99	BMediaTrack *audiotrack = 0;
100	for (int tr = 0; tr < count; tr++) {
101		track = fMediaFile->TrackAt(tr);
102		if (track == 0 || track->InitCheck() != B_OK)
103			continue;
104		media_format fmt;
105		if (track->DecodedFormat(&fmt) != B_OK)
106			continue;
107		if (fmt.type == B_MEDIA_RAW_AUDIO) {
108			audiotrack = track;
109			break;
110		}
111		fMediaFile->ReleaseTrack(track);
112	}
113	if (audiotrack == 0) {
114		ERROR("BTrackReader: no audio track in file\n");
115		return;
116	}
117
118	SetToTrack(audiotrack);
119
120	// if the track was not set, release it
121	if (fMediaTrack == 0) {
122		fMediaFile->ReleaseTrack(audiotrack);
123		return;
124	}
125
126	fBuffer = new uint8[fFormat.buffer_size];
127	fFrameSize = fFormat.channel_count * (fFormat.format & media_raw_audio_format::B_AUDIO_SIZE_MASK);
128
129	TRACE("BTrackReader::BTrackReader successful\n");
130}
131
132void
133BTrackReader::SetToTrack(BMediaTrack *track)
134{
135	media_format fmt;
136	memset(&fmt,0,sizeof(fmt)); //wildcard
137	memcpy(&fmt.u.raw_audio,&fFormat,sizeof(fFormat));
138	fmt.type = B_MEDIA_RAW_AUDIO;
139	//try to find a output format
140	if (B_OK == track->DecodedFormat(&fmt)) {
141		memcpy(&fFormat,&fmt.u.raw_audio,sizeof(fFormat));
142		fMediaTrack = track;
143		return;
144	}
145
146	//try again
147	fmt.u.raw_audio.buffer_size = 2 * 4096;
148	fmt.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
149	if (B_OK == track->DecodedFormat(&fmt)) {
150		memcpy(&fFormat,&fmt.u.raw_audio,sizeof(fFormat));
151		fMediaTrack = track;
152		return;
153	}
154
155	//try again
156	fmt.u.raw_audio.buffer_size = 4096;
157	fmt.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
158	if (B_OK == track->DecodedFormat(&fmt)) {
159		memcpy(&fFormat,&fmt.u.raw_audio,sizeof(fFormat));
160		fMediaTrack = track;
161		return;
162	}
163
164	//we have failed
165	ERROR("BTrackReader::SetToTrack failed\n");
166}
167
168BTrackReader::~BTrackReader()
169{
170	CALLED();
171	if (fMediaFile && fMediaTrack)
172		fMediaFile->ReleaseTrack(fMediaTrack);
173	delete fMediaFile;
174	delete [] fBuffer;
175}
176
177status_t
178BTrackReader::InitCheck()
179{
180	CALLED();
181	return fMediaTrack ? fMediaTrack->InitCheck() : B_ERROR;
182}
183
184int64
185BTrackReader::CountFrames(void)
186{
187	CALLED();
188	return fMediaTrack ? fMediaTrack->CountFrames() : 0;
189}
190
191const media_raw_audio_format &
192BTrackReader::Format(void) const
193{
194	CALLED();
195	return fFormat;
196}
197
198int32
199BTrackReader::FrameSize(void)
200{
201	CALLED();
202	return fFrameSize;
203}
204
205status_t
206BTrackReader::ReadFrames(void *in_buffer, int32 frame_count)
207{
208	CALLED();
209
210	uint8 *buffer = static_cast<uint8 *>(in_buffer);
211	int32 bytes_to_read = frame_count * fFrameSize;
212
213	status_t last_status = B_OK;
214	while (bytes_to_read > 0) {
215		int32 bytes_to_copy = min_c(fBufferUsedSize,bytes_to_read);
216		if (bytes_to_copy > 0) {
217			memcpy(buffer,fBuffer + fBufferOffset,bytes_to_copy);
218			buffer += bytes_to_copy;
219			bytes_to_read -= bytes_to_copy;
220			fBufferOffset += bytes_to_copy;
221			fBufferUsedSize -= bytes_to_copy;
222		}
223		if (last_status != B_OK)
224			break;
225		if (fBufferUsedSize == 0) {
226			int64 outFrameCount;
227			last_status = fMediaTrack->ReadFrames(fBuffer,&outFrameCount);
228			fBufferOffset = 0;
229			fBufferUsedSize = outFrameCount * fFrameSize;
230		}
231	}
232	if (bytes_to_read > 0) {
233		memset(buffer,0,bytes_to_read);
234		return B_LAST_BUFFER_ERROR;
235	} else {
236		return B_OK;
237	}
238}
239
240status_t
241BTrackReader::SeekToFrame(int64 *in_out_frame)
242{
243	CALLED();
244	status_t s = fMediaTrack->SeekToFrame(in_out_frame, B_MEDIA_SEEK_CLOSEST_BACKWARD);
245	if (s != B_OK)
246		return s;
247	fBufferUsedSize = 0;
248	fBufferOffset = 0;
249	return B_OK;
250}
251
252BMediaTrack *
253BTrackReader::Track(void)
254{
255	CALLED();
256	return fMediaTrack;
257}
258
259}; //namespace BPrivate
260
261