3rdparty/licenseReport: Add seperate LGPL checks
[haiku.git] / src / add-ons / media / plugins / aiff_reader / aiff_reader.cpp
blob3e2dc5836f4571c597d3917bfe2145fa52015e9c
1 /*
2 * Copyright (c) 2003-2004, Marcus Overhagen
3 * All rights reserved.
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.
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 "aiff_reader.h"
34 //#define TRACE_AIFF_READER
35 #ifdef TRACE_AIFF_READER
36 #define TRACE printf
37 #else
38 #define TRACE(a...)
39 #endif
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");
50 fBuffer = 0;
54 aiffReader::~aiffReader()
56 if (fBuffer)
57 free(fBuffer);
61 const char *
62 aiffReader::Copyright()
64 return "AIFF & AIFF-C reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen";
68 status_t
69 aiffReader::Sniff(int32 *streamCount)
71 TRACE("aiffReader::Sniff\n");
73 fSource = dynamic_cast<BPositionIO *>(Reader::Source());
74 if (!fSource) {
75 TRACE("aiffReader::Sniff: not a BPositionIO\n");
76 return B_ERROR;
79 int64 filesize = Source()->Seek(0, SEEK_END);
80 if (filesize < 42) {
81 TRACE("aiffReader::Sniff: File too small\n");
82 return B_ERROR;
85 aiff_chunk aiff;
87 if (sizeof(aiff) != Source()->ReadAt(0, &aiff, sizeof(aiff))) {
88 TRACE("aiffReader::Sniff: aiff_chunk reading failed\n");
89 return B_ERROR;
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");
96 return B_ERROR;
99 comm_chunk comm;
100 ssnd_chunk ssnd;
101 int64 pos = sizeof(aiff);
103 fDataStart = 0;
104 fDataSize = 0;
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) {
110 chunk_struct chunk;
111 if (sizeof(chunk) != Source()->ReadAt(pos, &chunk, sizeof(chunk))) {
112 TRACE("aiffReader::Sniff: chunk header reading failed\n");
113 return B_ERROR;
115 pos += sizeof(chunk);
116 if (UINT32(chunk.chunk_size) == 0) {
117 TRACE("aiffReader::Sniff: Error: chunk of size 0 found\n");
118 return B_ERROR;
120 switch (UINT32(chunk.chunk_id)) {
121 case FOURCC('C','O','M','M'):
123 if (foundCOMM)
124 break;
125 uint32 size = UINT32(chunk.chunk_size);
126 if (size >= 18) {
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");
130 break;
132 if (size < 22)
133 comm.compression_id = B_HOST_TO_BENDIAN_INT32(FOURCC('N','O','N','E'));
134 foundCOMM = true;
135 } else {
136 TRACE("aiffReader::Sniff: COMM chunk too small\n");
138 break;
140 case FOURCC('S','S','N','D'):
142 if (foundSSND)
143 break;
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");
148 break;
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);
154 foundSSND = true;
155 } else {
156 TRACE("aiffReader::Sniff: SSND chunk too small\n");
158 break;
160 default:
161 TRACE("aiffReader::Sniff: ignoring chunk 0x%08lx of %lu bytes\n", UINT32(chunk.chunk_id), UINT32(chunk.chunk_size));
162 break;
164 pos += UINT32(chunk.chunk_size);
165 pos += (pos & 1);
168 if (!foundCOMM) {
169 TRACE("aiffReader::Sniff: couldn't find format chunk\n");
170 return B_ERROR;
172 if (!foundSSND) {
173 TRACE("aiffReader::Sniff: couldn't find data chunk\n");
174 return B_ERROR;
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)
197 fChannelCount = 1;
198 if (fFrameRate < 1)
199 fFrameRate = 44100;
201 if (fBytesPerSample == 0) {
202 TRACE("aiffReader::Sniff: sample format not recognized\n");
203 return B_ERROR;
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'):
211 fRaw = true;
212 break;
213 default:
214 fRaw = false;
215 break;
218 fPosition = 0;
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;
236 if (fRaw) {
237 // a raw PCM format
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) {
247 case 1:
248 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_CHAR;
249 break;
250 case 2:
251 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
252 break;
253 case 3:
254 fFormat.u.raw_audio.format = B_AUDIO_FORMAT_INT24;
255 break;
256 case 4:
257 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_INT;
258 break;
259 default:
260 TRACE("aiffReader::Sniff: unknown bytes per sample for raw format\n");
261 return B_ERROR;
263 break;
264 case FOURCC('F','L','3','2'):
265 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
266 break;
267 case FOURCC('f','l','3','2'):
268 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
269 break;
270 case FOURCC('f','l','6','4'):
271 fFormat.u.raw_audio.format = B_AUDIO_FORMAT_FLOAT64;
272 break;
273 default:
274 TRACE("aiffReader::Sniff: unknown raw format\n");
275 return B_ERROR;
277 fFormat.u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
278 fFormat.u.raw_audio.buffer_size = fBufferSize;
279 } else {
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;
289 *streamCount = 1;
290 return B_OK;
293 void
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;
300 mff->version = 100;
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");
307 status_t
308 aiffReader::AllocateCookie(int32 streamNumber, void **cookie)
310 return B_OK;
313 status_t
314 aiffReader::FreeCookie(void *cookie)
316 return B_OK;
320 status_t
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;
326 *format = fFormat;
327 *infoBuffer = 0;
328 *infoSize = 0;
329 return B_OK;
333 status_t
334 aiffReader::Seek(void *cookie,
335 uint32 seekTo,
336 int64 *frame, bigtime_t *time)
338 int64 pos;
340 if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
341 if (fRaw)
342 pos = *frame * fBytesPerFrame;
343 else
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) {
348 if (fRaw)
349 pos = (*time * fFrameRate * fBytesPerFrame) / 1000000LL;
350 else
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);
354 } else {
355 return B_ERROR;
358 if (fRaw)
359 *frame = pos / fBytesPerFrame;
360 else
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);
369 return B_ERROR;
372 fPosition = pos;
373 return B_OK;
377 status_t
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;
391 if (readsize == 0)
392 return B_LAST_BUFFER_ERROR;
394 if (readsize != Source()->ReadAt(fDataStart + fPosition, fBuffer, readsize)) {
395 TRACE("aiffReader::GetNextChunk: unexpected read error\n");
396 return B_ERROR;
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;
404 return B_OK;
407 uint32
408 aiffReader::DecodeFrameRate(const void *_80bit_float)
410 // algorithm from http://www.borg.com/~jglatt/tech/aiff.htm
411 uint32 mantissa;
412 uint32 last;
413 uint32 exp;
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);
417 if (exp > 32)
418 return 0;
419 last = 0;
420 while (exp--) {
421 last = mantissa;
422 mantissa >>= 1;
424 if (last & 1)
425 mantissa++;
426 return mantissa;
430 Reader *
431 aiffReaderPlugin::NewReader()
433 return new aiffReader;
437 MediaPlugin *instantiate_plugin()
439 return new aiffReaderPlugin;