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