2 * Copyright (c) 2003-2004, Marcus Overhagen
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
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.
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.
29 #include <ByteOrder.h>
30 #include <InterfaceDefs.h>
31 #include "RawFormats.h"
32 #include "aiff_reader.h"
34 //#define TRACE_AIFF_READER
35 #ifdef TRACE_AIFF_READER
41 #define BUFFER_SIZE 16384
43 #define FOURCC(a,b,c,d) ((((uint32)(a)) << 24) | (((uint32)(b)) << 16) | (((uint32)(c)) << 8) | ((uint32)(d)))
44 #define UINT16(a) ((uint16)B_BENDIAN_TO_HOST_INT16((a)))
45 #define UINT32(a) ((uint32)B_BENDIAN_TO_HOST_INT32((a)))
47 aiffReader::aiffReader()
49 TRACE("aiffReader::aiffReader\n");
54 aiffReader::~aiffReader()
62 aiffReader::Copyright()
64 return "AIFF & AIFF-C reader, " B_UTF8_COPYRIGHT
" by Marcus Overhagen";
69 aiffReader::Sniff(int32
*streamCount
)
71 TRACE("aiffReader::Sniff\n");
73 fSource
= dynamic_cast<BPositionIO
*>(Reader::Source());
75 TRACE("aiffReader::Sniff: not a BPositionIO\n");
79 int64 filesize
= Source()->Seek(0, SEEK_END
);
81 TRACE("aiffReader::Sniff: File too small\n");
87 if (sizeof(aiff
) != Source()->ReadAt(0, &aiff
, sizeof(aiff
))) {
88 TRACE("aiffReader::Sniff: aiff_chunk reading failed\n");
92 if (UINT32(aiff
.chunk_id
) != FOURCC('F','O','R','M')
93 || (UINT32(aiff
.aiff_id
) != FOURCC('A','I','F','F')
94 && UINT32(aiff
.aiff_id
) != FOURCC('A','I','F','C'))) {
95 TRACE("aiffReader::Sniff: aiff not recognized\n");
101 int64 pos
= sizeof(aiff
);
106 // read all chunks and search for "COMM" and "SSND", everything else is ignored
107 bool foundCOMM
= false;
108 bool foundSSND
= false;
109 while (pos
+ sizeof(chunk_struct
) <= filesize
) {
111 if (sizeof(chunk
) != Source()->ReadAt(pos
, &chunk
, sizeof(chunk
))) {
112 TRACE("aiffReader::Sniff: chunk header reading failed\n");
115 pos
+= sizeof(chunk
);
116 if (UINT32(chunk
.chunk_size
) == 0) {
117 TRACE("aiffReader::Sniff: Error: chunk of size 0 found\n");
120 switch (UINT32(chunk
.chunk_id
)) {
121 case FOURCC('C','O','M','M'):
125 uint32 size
= UINT32(chunk
.chunk_size
);
127 size
= min_c(size
, sizeof(comm
));
128 if (size
!= (uint32
)Source()->ReadAt(pos
, &comm
, size
)) {
129 TRACE("aiffReader::Sniff: COMM chunk reading failed\n");
133 comm
.compression_id
= B_HOST_TO_BENDIAN_INT32(FOURCC('N','O','N','E'));
136 TRACE("aiffReader::Sniff: COMM chunk too small\n");
140 case FOURCC('S','S','N','D'):
144 uint32 size
= UINT32(chunk
.chunk_size
);
145 if (size
>= sizeof(ssnd
)) {
146 if (sizeof(ssnd
) != Source()->ReadAt(pos
, &ssnd
, sizeof(ssnd
))) {
147 TRACE("aiffReader::Sniff: SSND chunk reading failed\n");
150 fDataStart
= pos
+ sizeof(ssnd
) + UINT32(ssnd
.offset
);
151 fDataSize
= UINT32(chunk
.chunk_size
) - sizeof(ssnd
);
152 if (pos
+ fDataSize
+ sizeof(ssnd
) > filesize
)
153 fDataSize
= filesize
- pos
- sizeof(ssnd
);
156 TRACE("aiffReader::Sniff: SSND chunk too small\n");
161 TRACE("aiffReader::Sniff: ignoring chunk 0x%08lx of %lu bytes\n", UINT32(chunk
.chunk_id
), UINT32(chunk
.chunk_size
));
164 pos
+= UINT32(chunk
.chunk_size
);
169 TRACE("aiffReader::Sniff: couldn't find format chunk\n");
173 TRACE("aiffReader::Sniff: couldn't find data chunk\n");
177 TRACE("aiffReader::Sniff: we found something that looks like:\n");
179 TRACE(" channel_count %d\n", UINT16(comm
.channel_count
));
180 TRACE(" frame_count %ld\n", UINT32(comm
.frame_count
));
181 TRACE(" bits_per_sample %d\n", UINT16(comm
.bits_per_sample
));
182 TRACE(" sample_rate %ld\n", DecodeFrameRate(comm
.sample_rate
));
183 TRACE(" compression_id %s\n", string_for_compression(UINT32(comm
.compression_id
)));
184 TRACE(" offset %ld\n", UINT32(ssnd
.offset
));
185 TRACE(" block_size %ld\n", UINT32(ssnd
.block_size
));
187 fChannelCount
= UINT16(comm
.channel_count
);
188 fFrameCount
= UINT32(comm
.frame_count
);
189 fFrameRate
= DecodeFrameRate(comm
.sample_rate
);
190 fDuration
= (1000000LL * fFrameCount
) / fFrameRate
;
191 fValidBitsPerSample
= UINT16(comm
.bits_per_sample
);
192 fBytesPerSample
= (fValidBitsPerSample
+ 7) / 8;
193 fBytesPerFrame
= fBytesPerSample
* fChannelCount
;
194 fFormatCode
= UINT32(comm
.compression_id
);
196 if (fChannelCount
< 1)
201 if (fBytesPerSample
== 0) {
202 TRACE("aiffReader::Sniff: sample format not recognized\n");
206 switch (fFormatCode
) {
207 case FOURCC('N','O','N','E'):
208 case FOURCC('F','L','3','2'):
209 case FOURCC('f','l','3','2'):
210 case FOURCC('f','l','6','4'):
220 fBufferSize
= (BUFFER_SIZE
/ fBytesPerFrame
) * fBytesPerFrame
;
221 fBuffer
= malloc(fBufferSize
);
223 TRACE(" fDataStart %Ld\n", fDataStart
);
224 TRACE(" fDataSize %Ld\n", fDataSize
);
225 TRACE(" fFrameCount %Ld\n", fFrameCount
);
226 TRACE(" fDuration %Ld\n", fDuration
);
227 TRACE(" fChannelCount %d\n", fChannelCount
);
228 TRACE(" fFrameRate %ld\n", fFrameRate
);
229 TRACE(" fValidBitsPerSample %d\n", fValidBitsPerSample
);
230 TRACE(" fBytesPerSample %d\n", fBytesPerSample
);
231 TRACE(" fBytesPerFrame %d\n", fBytesPerFrame
);
232 TRACE(" fFormatCode %ld\n", fFormatCode
);
233 TRACE(" fRaw %d\n", fRaw
);
235 BMediaFormats formats
;
238 media_format_description description
;
239 description
.family
= B_BEOS_FORMAT_FAMILY
;
240 description
.u
.beos
.format
= B_BEOS_FORMAT_RAW_AUDIO
;
241 formats
.GetFormatFor(description
, &fFormat
);
242 fFormat
.u
.raw_audio
.frame_rate
= fFrameRate
;
243 fFormat
.u
.raw_audio
.channel_count
= fChannelCount
;
244 switch (fFormatCode
) {
245 case FOURCC('N','O','N','E'):
246 switch (fBytesPerSample
) {
248 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_CHAR
;
251 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_SHORT
;
254 fFormat
.u
.raw_audio
.format
= B_AUDIO_FORMAT_INT24
;
257 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_INT
;
260 TRACE("aiffReader::Sniff: unknown bytes per sample for raw format\n");
264 case FOURCC('F','L','3','2'):
265 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_FLOAT
;
267 case FOURCC('f','l','3','2'):
268 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_FLOAT
;
270 case FOURCC('f','l','6','4'):
271 fFormat
.u
.raw_audio
.format
= B_AUDIO_FORMAT_FLOAT64
;
274 TRACE("aiffReader::Sniff: unknown raw format\n");
277 fFormat
.u
.raw_audio
.byte_order
= B_MEDIA_BIG_ENDIAN
;
278 fFormat
.u
.raw_audio
.buffer_size
= fBufferSize
;
280 // some encoded format
281 media_format_description description
;
282 description
.family
= B_AIFF_FORMAT_FAMILY
;
283 description
.u
.aiff
.codec
= fFormatCode
;
284 formats
.GetFormatFor(description
, &fFormat
);
285 fFormat
.u
.encoded_audio
.output
.frame_rate
= fFrameRate
;
286 fFormat
.u
.encoded_audio
.output
.channel_count
= fChannelCount
;
294 aiffReader::GetFileFormatInfo(media_file_format
*mff
)
296 mff
->capabilities
= media_file_format::B_READABLE
297 | media_file_format::B_KNOWS_ENCODED_AUDIO
298 | media_file_format::B_IMPERFECTLY_SEEKABLE
;
299 mff
->family
= B_MISC_FORMAT_FAMILY
;
301 strcpy(mff
->mime_type
, "audio/x-aiff");
302 strcpy(mff
->file_extension
, "aiff");
303 strcpy(mff
->short_name
, "AIFF / AIFF-C");
304 strcpy(mff
->pretty_name
, "Audio Interchange File Format");
308 aiffReader::AllocateCookie(int32 streamNumber
, void **cookie
)
314 aiffReader::FreeCookie(void *cookie
)
321 aiffReader::GetStreamInfo(void *cookie
, int64
*frameCount
, bigtime_t
*duration
,
322 media_format
*format
, const void **infoBuffer
, size_t *infoSize
)
324 *frameCount
= fFrameCount
;
325 *duration
= fDuration
;
334 aiffReader::Seek(void *cookie
,
336 int64
*frame
, bigtime_t
*time
)
340 if (seekTo
& B_MEDIA_SEEK_TO_FRAME
) {
342 pos
= *frame
* fBytesPerFrame
;
344 pos
= (*frame
* fDataSize
) / fFrameCount
;
345 pos
= (pos
/ fBytesPerFrame
) * fBytesPerFrame
; // round down to a block start
346 TRACE("aiffReader::Seek to frame %Ld, pos %Ld\n", *frame
, pos
);
347 } else if (seekTo
& B_MEDIA_SEEK_TO_TIME
) {
349 pos
= (*time
* fFrameRate
* fBytesPerFrame
) / 1000000LL;
351 pos
= (*time
* fDataSize
) / fDuration
;
352 pos
= (pos
/ fBytesPerFrame
) * fBytesPerFrame
; // round down to a block start
353 TRACE("aiffReader::Seek to time %Ld, pos %Ld\n", *time
, pos
);
359 *frame
= pos
/ fBytesPerFrame
;
361 *frame
= (pos
* fFrameCount
) / fDataSize
;
362 *time
= (*frame
* 1000000LL) / fFrameRate
;
364 TRACE("aiffReader::Seek newtime %Ld\n", *time
);
365 TRACE("aiffReader::Seek newframe %Ld\n", *frame
);
367 if (pos
< 0 || pos
> fDataSize
) {
368 TRACE("aiffReader::Seek invalid position %Ld\n", pos
);
378 aiffReader::GetNextChunk(void *cookie
,
379 const void **chunkBuffer
, size_t *chunkSize
,
380 media_header
*mediaHeader
)
382 // XXX it might be much better to not return any start_time information for encoded formats here,
383 // XXX and instead use the last time returned from seek and count forward after decoding.
384 mediaHeader
->start_time
= ((fPosition
/ fBytesPerFrame
) * 1000000LL) / fFrameRate
;
385 mediaHeader
->file_pos
= fDataStart
+ fPosition
;
387 int64 maxreadsize
= fDataSize
- fPosition
;
388 int32 readsize
= fBufferSize
;
389 if (maxreadsize
< readsize
)
390 readsize
= maxreadsize
;
392 return B_LAST_BUFFER_ERROR
;
394 if (readsize
!= Source()->ReadAt(fDataStart
+ fPosition
, fBuffer
, readsize
)) {
395 TRACE("aiffReader::GetNextChunk: unexpected read error\n");
399 // XXX if the stream has more than two channels, we need to reorder channel data here
401 fPosition
+= readsize
;
402 *chunkBuffer
= fBuffer
;
403 *chunkSize
= readsize
;
408 aiffReader::DecodeFrameRate(const void *_80bit_float
)
410 // algorithm from http://www.borg.com/~jglatt/tech/aiff.htm
415 mantissa
= (uint32
)B_BENDIAN_TO_HOST_INT32(*(const uint32
*)((const char *)_80bit_float
+ 2));
416 exp
= 30 - *(const uint8
*)((const char *)_80bit_float
+ 1);
431 aiffReaderPlugin::NewReader()
433 return new aiffReader
;
437 MediaPlugin
*instantiate_plugin()
439 return new aiffReaderPlugin
;