xref: /haiku/src/add-ons/media/plugins/ape_reader/APEReader.cpp (revision 106388ddbfdd00f4409c86bd3fe8d581bae532ec)
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 static const char*	kCopyrightString
14 	= "Copyright " B_UTF8_COPYRIGHT " 2005-2009 by SHINTA";
15 
16 
17 TAPEReader::TAPEReader()
18 	: SUPER()
19 {
20 	mDecodedData = NULL;
21 	mDecomp = NULL;
22 	Unset();
23 }
24 
25 
26 TAPEReader::~TAPEReader()
27 {
28 }
29 
30 
31 status_t
32 TAPEReader::AllocateCookie(int32 oStreamNumber, void** oCookie)
33 {
34 	*oCookie = NULL;
35 	return B_OK;
36 }
37 
38 
39 const char*
40 TAPEReader::Copyright()
41 {
42 	return kCopyrightString;
43 }
44 
45 
46 bigtime_t
47 TAPEReader::CurrentTime() const
48 {
49 	return mDecomp->GetInfo(APE_DECOMPRESS_CURRENT_MS)
50 		* static_cast<bigtime_t>(1000);
51 }
52 
53 
54 status_t
55 TAPEReader::FreeCookie(void* oCookie)
56 {
57 	return B_OK;
58 }
59 
60 
61 void
62 TAPEReader::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 
78 status_t
79 TAPEReader::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 
109 status_t
110 TAPEReader::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 
138 status_t
139 TAPEReader::LoadAPECheck() const
140 {
141 	return mLoadAPECheck;
142 }
143 
144 
145 status_t
146 TAPEReader::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 
163 status_t
164 TAPEReader::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 
182 status_t
183 TAPEReader::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 
211 status_t
212 TAPEReader::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 
249 void
250 TAPEReader::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 
266 TAPEReaderPlugin::TAPEReaderPlugin()
267 {
268 }
269 
270 
271 TAPEReaderPlugin::~TAPEReaderPlugin()
272 {
273 }
274 
275 
276 Reader*
277 TAPEReaderPlugin::NewReader()
278 {
279 	return new TAPEReader();
280 }
281 
282 
283 MediaPlugin*
284 instantiate_plugin()
285 {
286 	return new TAPEReaderPlugin();
287 }
288