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