1/* Copyright 2005-2009 SHINTA
2 * Distributed under the terms of the MIT license
3 */
4
5
6#include <InterfaceDefs.h>
7#include <MediaIO.h>
8
9#include "APEReader.h"
10#include "MACLib.h"
11
12
13static const char*	kCopyrightString
14	= "Copyright " B_UTF8_COPYRIGHT " 2005-2009 by SHINTA";
15
16
17TAPEReader::TAPEReader()
18	: SUPER()
19{
20	mDecodedData = NULL;
21	mDecomp = NULL;
22	Unset();
23}
24
25
26TAPEReader::~TAPEReader()
27{
28}
29
30
31status_t
32TAPEReader::AllocateCookie(int32 oStreamNumber, void** oCookie)
33{
34	*oCookie = NULL;
35	return B_OK;
36}
37
38
39const char*
40TAPEReader::Copyright()
41{
42	return kCopyrightString;
43}
44
45
46bigtime_t
47TAPEReader::CurrentTime() const
48{
49	return mDecomp->GetInfo(APE_DECOMPRESS_CURRENT_MS)
50		* static_cast<bigtime_t>(1000);
51}
52
53
54status_t
55TAPEReader::FreeCookie(void* oCookie)
56{
57	return B_OK;
58}
59
60
61void
62TAPEReader::GetFileFormatInfo(media_file_format* oMFF)
63{
64	oMFF->capabilities = media_file_format::B_READABLE
65			| media_file_format::B_PERFECTLY_SEEKABLE
66//			| media_file_format::B_IMPERFECTLY_SEEKABLE
67			| media_file_format::B_KNOWS_RAW_AUDIO
68			| media_file_format::B_KNOWS_ENCODED_AUDIO;
69	oMFF->family = B_ANY_FORMAT_FAMILY;
70	oMFF->version = MEDIA_FILE_FORMAT_VERSION;
71	strlcpy(oMFF->mime_type, MIME_TYPE_APE, sizeof(oMFF->mime_type));
72	strlcpy(oMFF->pretty_name, MIME_TYPE_APE_LONG_DESCRIPTION, sizeof(oMFF->pretty_name));
73	strlcpy(oMFF->short_name, MIME_TYPE_APE_SHORT_DESCRIPTION, sizeof(oMFF->short_name));
74	strlcpy(oMFF->file_extension, MIME_TYPE_APE_EXTENSION, sizeof(oMFF->file_extension));
75}
76
77
78status_t
79TAPEReader::GetNextChunk(void* oCookie, const void** oChunkBuffer,
80	size_t* oChunkSize, media_header* oMediaHeader)
81{
82	int64		aOutSize;
83
84	// check whether song is finished or not
85	if (mReadPosTotal - mReadPos + mPlayPos >= mDataSize)
86		return B_ERROR;
87
88	// reading data
89	if (mPlayPos >= mReadPos )
90		ReadBlocks();
91
92	// passing data
93	if (mReadPos-mPlayPos >= BUFFER_SIZE)
94		aOutSize = BUFFER_SIZE;
95	else
96		aOutSize = mReadPos-mPlayPos;
97
98	*oChunkBuffer = &mDecodedData[mPlayPos];
99	mPlayPos += aOutSize;
100
101	// passing info
102	*oChunkSize = aOutSize;
103	oMediaHeader->start_time = CurrentTime();
104	oMediaHeader->file_pos = mPlayPos;
105	return B_OK;
106}
107
108
109status_t
110TAPEReader::GetStreamInfo(void* oCookie, int64* oFrameCount,
111	bigtime_t* oDuration, media_format* oFormat, const void** oInfoBuffer,
112	size_t* oInfoSize)
113{
114	if (LoadAPECheck() != B_OK)
115		return LoadAPECheck();
116
117	*oFrameCount = mDataSize / (mDecomp->GetInfo(APE_INFO_BITS_PER_SAMPLE) / 8
118			* mDecomp->GetInfo(APE_INFO_CHANNELS));
119	*oDuration = mDecomp->GetInfo(APE_INFO_LENGTH_MS)
120		* static_cast<bigtime_t>(1000);
121	// media_format
122	oFormat->type = B_MEDIA_RAW_AUDIO;
123	oFormat->u.raw_audio.frame_rate = mDecomp->GetInfo(APE_INFO_SAMPLE_RATE);
124	oFormat->u.raw_audio.channel_count = mDecomp->GetInfo(APE_INFO_CHANNELS);
125	if ( mDecomp->GetInfo(APE_INFO_BITS_PER_SAMPLE) == 16 )
126		oFormat->u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
127	else
128		oFormat->u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR;
129
130	oFormat->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
131	oFormat->u.raw_audio.buffer_size = BUFFER_SIZE;
132	oInfoBuffer = NULL;
133	oInfoSize = NULL;
134	return B_OK;
135}
136
137
138status_t
139TAPEReader::LoadAPECheck() const
140{
141	return mLoadAPECheck;
142}
143
144
145status_t
146TAPEReader::ReadBlocks()
147{
148	int aBlocksRead;
149	int aRetVal = 0;
150
151	aRetVal = mDecomp->GetData(reinterpret_cast<char*>(mDecodedData),
152		BLOCK_COUNT, &aBlocksRead);
153	if (aRetVal != ERROR_SUCCESS)
154		return B_ERROR;
155
156	mPlayPos = 0;
157	mReadPos = aBlocksRead*mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN);
158	mReadPosTotal += mReadPos;
159	return B_OK;
160}
161
162
163status_t
164TAPEReader::FindKeyFrame(void* cookie, uint32 flags, int64* frame,
165	bigtime_t* time)
166{
167	if (flags & B_MEDIA_SEEK_TO_FRAME) {
168		*time = *frame * 1000 /  mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS)
169			* mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS);
170		printf("FindKeyFrame for frame %lld: %lld\n", *frame, *time);
171	} else if (flags & B_MEDIA_SEEK_TO_TIME) {
172		*frame = (*time) / 1000 * mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS)
173			/ mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS);
174		printf("FindKeyFrame for time %lld: %lld\n", *time, *frame);
175	} else
176		return B_ERROR;
177
178	return B_OK;
179}
180
181
182status_t
183TAPEReader::Seek(void *cookie, uint32 flags, int64 *frame, bigtime_t *time)
184{
185	int32 aNewBlock;
186
187	if (flags & B_MEDIA_SEEK_TO_FRAME) {
188		printf("Seek to frame %lld\n", *frame);
189		aNewBlock = *frame;
190	} else if (flags & B_MEDIA_SEEK_TO_TIME) {
191		printf("Seek for time %lld\n", *time);
192		aNewBlock = (*time) / 1000 * mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS)
193			/ mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS);
194	} else
195		return B_ERROR;
196
197	int64 aNewTime = aNewBlock * mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN);
198	if (mReadPosTotal - mReadPos < aNewTime && mReadPosTotal > aNewTime) {
199		// Requested seek frame is already in the current buffer, no need to
200		// actually seek, just set the play position
201		mPlayPos = aNewTime - mReadPosTotal + mReadPos;
202	} else {
203		mReadPosTotal = aNewBlock * mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN);
204		mDecomp->Seek(aNewBlock);
205		ReadBlocks();
206	}
207	return B_OK;
208}
209
210
211status_t
212TAPEReader::Sniff(int32* oStreamCount)
213{
214	Unset();
215	// prepare about file
216	mSrcPIO = dynamic_cast<BPositionIO*>(Source());
217	if (mSrcPIO == NULL)
218		return B_ERROR;
219
220	BMediaIO* mediaIO = dynamic_cast<BMediaIO*>(Source());
221	if (mediaIO != NULL) {
222		int32 flags = 0;
223		mediaIO->GetFlags(&flags);
224		// This plugin doesn't support streamed data.
225		// The APEHeader::FindDescriptor function always
226		// analyze the whole file to find the APE_DESCRIPTOR.
227		if ((flags & B_MEDIA_STREAMING) == true)
228			return B_ERROR;
229	}
230
231	int nFunctionRetVal = ERROR_SUCCESS;
232	mPositionBridgeIO.SetPositionIO(mSrcPIO);
233
234	mDecomp = CreateIAPEDecompressEx(&mPositionBridgeIO, &nFunctionRetVal);
235	if (mDecomp == NULL || nFunctionRetVal != ERROR_SUCCESS)
236		return B_ERROR;
237
238	// prepare about data
239	mDataSize = static_cast<int64>(mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS))
240			*mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN);
241	mDecodedData = new char [max_c(BUFFER_SIZE*mDecomp->GetInfo(APE_INFO_CHANNELS),
242			BLOCK_COUNT*mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN))];
243	mLoadAPECheck = B_OK;
244	*oStreamCount = 1;
245	return B_OK;
246}
247
248
249void
250TAPEReader::Unset()
251{
252	mLoadAPECheck = B_NO_INIT;
253	// about file
254	mPositionBridgeIO.SetPositionIO(NULL);
255	mSrcPIO = NULL;
256	delete mDecomp;
257	// about data
258	mDataSize = 0;
259	mReadPos = 0;
260	mReadPosTotal = 0;
261	mPlayPos = 0;
262	delete [] mDecodedData;
263}
264
265
266TAPEReaderPlugin::TAPEReaderPlugin()
267{
268}
269
270
271TAPEReaderPlugin::~TAPEReaderPlugin()
272{
273}
274
275
276Reader*
277TAPEReaderPlugin::NewReader()
278{
279	return new TAPEReader();
280}
281
282
283MediaPlugin*
284instantiate_plugin()
285{
286	return new TAPEReaderPlugin();
287}
288