1 /* 2 * Copyright (c) 2003-2004, Marcus Overhagen 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 22 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 * OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #include <stdio.h> 26 #include <string.h> 27 #include <malloc.h> 28 #include <DataIO.h> 29 #include <ByteOrder.h> 30 #include <InterfaceDefs.h> 31 #include "RawFormats.h" 32 #include "au_reader.h" 33 34 //#define TRACE_AU_READER 35 #ifdef TRACE_AU_READER 36 #define TRACE printf 37 #else 38 #define TRACE(a...) 39 #endif 40 41 #define BUFFER_SIZE 16384 42 43 #define UINT32(a) ((uint32)B_BENDIAN_TO_HOST_INT32((a))) 44 45 auReader::auReader() 46 { 47 TRACE("auReader::auReader\n"); 48 fBuffer = 0; 49 } 50 51 52 auReader::~auReader() 53 { 54 if (fBuffer) 55 free(fBuffer); 56 } 57 58 59 const char * 60 auReader::Copyright() 61 { 62 return ".au & .snd reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen"; 63 } 64 65 66 status_t 67 auReader::Sniff(int32 *streamCount) 68 { 69 TRACE("auReader::Sniff\n"); 70 71 fSource = dynamic_cast<BPositionIO *>(Reader::Source()); 72 if (!fSource) { 73 TRACE("auReader::Sniff: not a BPositionIO\n"); 74 return B_ERROR; 75 } 76 77 int64 filesize = Source()->Seek(0, SEEK_END); 78 if (filesize < sizeof(struct snd_header)) { 79 TRACE("auReader::Sniff: File too small\n"); 80 return B_ERROR; 81 } 82 83 struct snd_header header; 84 85 if (sizeof(header) != Source()->ReadAt(0, &header, sizeof(header))) { 86 TRACE("auReader::Sniff: header reading failed\n"); 87 return B_ERROR; 88 } 89 90 if (UINT32(header.magic) != SND_MAGIC) { 91 TRACE("auReader::Sniff: header not recognized\n"); 92 return B_ERROR; 93 } 94 95 TRACE("auReader::Sniff: we found something that looks like:\n"); 96 97 TRACE(" data_start %ld\n", UINT32(header.data_start)); 98 TRACE(" data_size %ld\n", UINT32(header.data_size)); 99 TRACE(" data_format %ld\n", UINT32(header.data_format)); 100 TRACE(" sampling_rate %ld\n", UINT32(header.sampling_rate)); 101 TRACE(" channel_count %ld\n", UINT32(header.channel_count)); 102 103 fDataStart = UINT32(header.data_start); 104 fDataSize = UINT32(header.data_size); 105 fChannelCount = UINT32(header.channel_count); 106 fFrameRate = UINT32(header.sampling_rate); 107 fFormatCode = UINT32(header.data_format); 108 109 if (fDataStart > filesize) { 110 TRACE("auReader::Sniff: data start too large\n"); 111 return B_ERROR; 112 } 113 if (fDataStart + fDataSize > filesize) 114 fDataSize = filesize - fDataStart; 115 if (fDataSize < 1) { 116 TRACE("auReader::Sniff: data size too small\n"); 117 return B_ERROR; 118 } 119 if (fChannelCount < 1) 120 fChannelCount = 1; 121 if (fFrameRate < 1) 122 fFrameRate = 44100; 123 124 switch (fFormatCode) { 125 case SND_FORMAT_UNSPECIFIED: TRACE("SND_FORMAT_UNSPECIFIED\n"); break; 126 case SND_FORMAT_MULAW_8: TRACE("SND_FORMAT_MULAW_8\n"); break; 127 case SND_FORMAT_LINEAR_8: TRACE("SND_FORMAT_LINEAR_8\n"); break; 128 case SND_FORMAT_LINEAR_16: TRACE("SND_FORMAT_LINEAR_16\n"); break; 129 case SND_FORMAT_LINEAR_24: TRACE("SND_FORMAT_LINEAR_24\n"); break; 130 case SND_FORMAT_LINEAR_32: TRACE("SND_FORMAT_LINEAR_32\n"); break; 131 case SND_FORMAT_FLOAT: TRACE("SND_FORMAT_FLOAT\n"); break; 132 case SND_FORMAT_DOUBLE: TRACE("SND_FORMAT_DOUBLE\n"); break; 133 case SND_FORMAT_INDIRECT: TRACE("SND_FORMAT_INDIRECT\n"); break; 134 case SND_FORMAT_NESTED: TRACE("SND_FORMAT_NESTED\n"); break; 135 case SND_FORMAT_DSP_CORE: TRACE("SND_FORMAT_DSP_CORE\n"); break; 136 case SND_FORMAT_DSP_DATA_8: TRACE("SND_FORMAT_DSP_DATA_8\n"); break; 137 case SND_FORMAT_DSP_DATA_16: TRACE("SND_FORMAT_DSP_DATA_16\n"); break; 138 case SND_FORMAT_DSP_DATA_24: TRACE("SND_FORMAT_DSP_DATA_24\n"); break; 139 case SND_FORMAT_DSP_DATA_32: TRACE("SND_FORMAT_DSP_DATA_32\n"); break; 140 case SND_FORMAT_DISPLAY: TRACE("SND_FORMAT_DISPLAY\n"); break; 141 case SND_FORMAT_MULAW_SQUELCH: TRACE("SND_FORMAT_MULAW_SQUELCH\n"); break; 142 case SND_FORMAT_EMPHASIZED: TRACE("SND_FORMAT_EMPHASIZED\n"); break; 143 case SND_FORMAT_COMPRESSED: TRACE("SND_FORMAT_COMPRESSED\n"); break; 144 case SND_FORMAT_COMPRESSED_EMPHASIZED: TRACE("SND_FORMAT_COMPRESSED_EMPHASIZED\n"); break; 145 case SND_FORMAT_DSP_COMMANDS: TRACE("SND_FORMAT_DSP_COMMANDS\n"); break; 146 case SND_FORMAT_DSP_COMMANDS_SAMPLES: TRACE("SND_FORMAT_DSP_COMMANDS_SAMPLES\n"); break; 147 case SND_FORMAT_ADPCM_G721: TRACE("SND_FORMAT_ADPCM_G721\n"); break; 148 case SND_FORMAT_ADPCM_G722: TRACE("SND_FORMAT_ADPCM_G722\n"); break; 149 case SND_FORMAT_ADPCM_G723_3: TRACE("SND_FORMAT_ADPCM_G723_3\n"); break; 150 case SND_FORMAT_ADPCM_G723_5: TRACE("SND_FORMAT_ADPCM_G723_5\n"); break; 151 case SND_FORMAT_ALAW_8: TRACE("SND_FORMAT_ALAW_8\n"); break; 152 } 153 154 switch (fFormatCode) { 155 case SND_FORMAT_MULAW_8: 156 fBitsPerSample = 8; fRaw = false; break; 157 case SND_FORMAT_LINEAR_8: 158 fBitsPerSample = 8; fRaw = true; break; 159 case SND_FORMAT_LINEAR_16: 160 fBitsPerSample = 16; fRaw = true; break; 161 case SND_FORMAT_LINEAR_24: 162 fBitsPerSample = 24; fRaw = true; break; 163 case SND_FORMAT_LINEAR_32: 164 fBitsPerSample = 32; fRaw = true; break; 165 case SND_FORMAT_FLOAT: 166 fBitsPerSample = 32; fRaw = true; break; 167 case SND_FORMAT_DOUBLE: 168 fBitsPerSample = 64; fRaw = true; break; 169 case SND_FORMAT_ADPCM_G721: 170 fBitsPerSample = 4; fRaw = false; break; 171 case SND_FORMAT_ADPCM_G722: 172 fBitsPerSample = 8; fRaw = false; break; 173 case SND_FORMAT_ADPCM_G723_3: 174 fBitsPerSample = 3; fRaw = false; break; 175 case SND_FORMAT_ADPCM_G723_5: 176 fBitsPerSample = 5; fRaw = false; break; 177 case SND_FORMAT_ALAW_8: 178 fBitsPerSample = 8; fRaw = false; break; 179 default: 180 fBitsPerSample = 0; break; 181 } 182 if (fBitsPerSample == 0) { 183 TRACE("auReader::Sniff: sample format not recognized\n"); 184 return B_ERROR; 185 } 186 187 fFrameCount = (8 * fDataSize) / (fChannelCount * fBitsPerSample); 188 fDuration = (1000000LL * fFrameCount) / fFrameRate; 189 fBitsPerFrame = fChannelCount * fBitsPerSample; 190 fBlockAlign = fBitsPerFrame; 191 while (fBlockAlign % 8 && fBlockAlign < 1000) 192 fBlockAlign += fBlockAlign; 193 if (fBlockAlign % 8) { 194 TRACE("auReader::Sniff: can't find block alignment, fChannelCount %d, fBitsPerSample %d\n", fChannelCount, fBitsPerSample); 195 return B_ERROR; 196 } 197 fBlockAlign /= 8; 198 199 fPosition = 0; 200 201 fBufferSize = (BUFFER_SIZE / fBlockAlign) * fBlockAlign; 202 fBuffer = malloc(fBufferSize); 203 204 TRACE(" fDataStart %Ld\n", fDataStart); 205 TRACE(" fDataSize %Ld\n", fDataSize); 206 TRACE(" fFrameCount %Ld\n", fFrameCount); 207 TRACE(" fDuration %Ld\n", fDuration); 208 TRACE(" fChannelCount %d\n", fChannelCount); 209 TRACE(" fFrameRate %ld\n", fFrameRate); 210 TRACE(" fBitsPerSample %d\n", fBitsPerSample); 211 TRACE(" fBlockAlign %d\n", fBlockAlign); 212 TRACE(" fFormatCode %ld\n", fFormatCode); 213 TRACE(" fRaw %d\n", fRaw); 214 215 BMediaFormats formats; 216 if (fRaw) { 217 // a raw PCM format 218 media_format_description description; 219 description.family = B_BEOS_FORMAT_FAMILY; 220 description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO; 221 formats.GetFormatFor(description, &fFormat); 222 fFormat.u.raw_audio.frame_rate = (fFrameRate == 8012) ? SND_RATE_8012 : fFrameRate; 223 fFormat.u.raw_audio.channel_count = fChannelCount; 224 switch (fFormatCode) { 225 case SND_FORMAT_LINEAR_8: 226 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR; 227 break; 228 case SND_FORMAT_LINEAR_16: 229 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT; 230 break; 231 case SND_FORMAT_LINEAR_24: 232 fFormat.u.raw_audio.format = B_AUDIO_FORMAT_INT24; 233 break; 234 case SND_FORMAT_LINEAR_32: 235 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_INT; 236 break; 237 case SND_FORMAT_FLOAT: 238 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; 239 break; 240 case SND_FORMAT_DOUBLE: 241 fFormat.u.raw_audio.format = B_AUDIO_FORMAT_FLOAT64; 242 break; 243 default: 244 TRACE("auReader::Sniff: unhandled raw format\n"); 245 return B_ERROR; 246 } 247 fFormat.u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN; 248 fFormat.u.raw_audio.buffer_size = fBufferSize; 249 } else { 250 // some encoded format 251 media_format_description description; 252 description.family = B_MISC_FORMAT_FAMILY; 253 description.u.misc.file_format = 'au'; 254 description.u.misc.codec = fFormatCode; 255 formats.GetFormatFor(description, &fFormat); 256 fFormat.u.encoded_audio.output.frame_rate = fFrameRate; 257 fFormat.u.encoded_audio.output.channel_count = fChannelCount; 258 } 259 260 *streamCount = 1; 261 return B_OK; 262 } 263 264 265 void 266 auReader::GetFileFormatInfo(media_file_format *mff) 267 { 268 mff->capabilities = media_file_format::B_READABLE 269 | media_file_format::B_KNOWS_RAW_AUDIO 270 | media_file_format::B_KNOWS_ENCODED_AUDIO 271 | media_file_format::B_IMPERFECTLY_SEEKABLE; 272 mff->family = B_MISC_FORMAT_FAMILY; 273 mff->version = 100; 274 strcpy(mff->mime_type, "audio/x-au"); 275 strcpy(mff->file_extension, "au"); 276 strcpy(mff->short_name, "Sun audio file"); 277 strcpy(mff->pretty_name, "Sun audio file"); 278 } 279 280 281 status_t 282 auReader::AllocateCookie(int32 streamNumber, void **cookie) 283 { 284 return B_OK; 285 } 286 287 status_t 288 auReader::FreeCookie(void *cookie) 289 { 290 return B_OK; 291 } 292 293 294 status_t 295 auReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, 296 media_format *format, const void **infoBuffer, size_t *infoSize) 297 { 298 *frameCount = fFrameCount; 299 *duration = fDuration; 300 *format = fFormat; 301 *infoBuffer = 0; 302 *infoSize = 0; 303 return B_OK; 304 } 305 306 307 status_t 308 auReader::Seek(void *cookie, 309 uint32 seekTo, 310 int64 *frame, bigtime_t *time) 311 { 312 int64 pos; 313 314 if (seekTo & B_MEDIA_SEEK_TO_FRAME) { 315 if (fRaw) 316 pos = (*frame * fBitsPerFrame) / 8; 317 else 318 pos = (*frame * fDataSize) / fFrameCount; 319 pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start 320 TRACE("auReader::Seek to frame %Ld, pos %Ld\n", *frame, pos); 321 } else if (seekTo & B_MEDIA_SEEK_TO_TIME) { 322 if (fRaw) 323 pos = (*time * fFrameRate * fBitsPerFrame) / (1000000LL * 8); 324 else 325 pos = (*time * fDataSize) / fDuration; 326 pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start 327 TRACE("auReader::Seek to time %Ld, pos %Ld\n", *time, pos); 328 } else { 329 return B_ERROR; 330 } 331 332 if (fRaw) 333 *frame = (8 * pos) / fBitsPerFrame; 334 else 335 *frame = (pos * fFrameCount) / fDataSize; 336 *time = (*frame * 1000000LL) / fFrameRate; 337 338 TRACE("auReader::Seek newtime %Ld\n", *time); 339 TRACE("auReader::Seek newframe %Ld\n", *frame); 340 341 if (pos < 0 || pos > fDataSize) { 342 TRACE("auReader::Seek invalid position %Ld\n", pos); 343 return B_ERROR; 344 } 345 346 fPosition = pos; 347 return B_OK; 348 } 349 350 351 status_t 352 auReader::GetNextChunk(void *cookie, 353 const void **chunkBuffer, size_t *chunkSize, 354 media_header *mediaHeader) 355 { 356 // XXX it might be much better to not return any start_time information for encoded formats here, 357 // XXX and instead use the last time returned from seek and count forward after decoding. 358 mediaHeader->start_time = (((8 * fPosition) / fBitsPerFrame) * 1000000LL) / fFrameRate; 359 mediaHeader->file_pos = fDataStart + fPosition; 360 361 int64 maxreadsize = fDataSize - fPosition; 362 int32 readsize = fBufferSize; 363 if (maxreadsize < readsize) 364 readsize = maxreadsize; 365 if (readsize == 0) 366 return B_LAST_BUFFER_ERROR; 367 368 if (readsize != Source()->ReadAt(fDataStart + fPosition, fBuffer, readsize)) { 369 TRACE("auReader::GetNextChunk: unexpected read error\n"); 370 return B_ERROR; 371 } 372 373 // XXX if the stream has more than two channels, we need to reorder channel data here 374 375 fPosition += readsize; 376 *chunkBuffer = fBuffer; 377 *chunkSize = readsize; 378 return B_OK; 379 } 380 381 382 Reader * 383 auReaderPlugin::NewReader() 384 { 385 return new auReader; 386 } 387 388 389 MediaPlugin *instantiate_plugin() 390 { 391 return new auReaderPlugin; 392 } 393