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 strcpy(oMFF->mime_type, MIME_TYPE_APE); 72 strcpy(oMFF->pretty_name, MIME_TYPE_APE_LONG_DESCRIPTION); 73 strcpy(oMFF->short_name, MIME_TYPE_APE_SHORT_DESCRIPTION); 74 strcpy(oMFF->file_extension, MIME_TYPE_APE_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 %Ld: %Ld\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 %Ld: %Ld\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 %Ld\n", *frame); 189 aNewBlock = *frame; 190 } else if (flags & B_MEDIA_SEEK_TO_TIME) { 191 printf("Seek for time %Ld\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