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 "au_reader.h"
34 //#define TRACE_AU_READER
35 #ifdef TRACE_AU_READER
41 #define BUFFER_SIZE 16384
43 #define UINT32(a) ((uint32)B_BENDIAN_TO_HOST_INT32((a)))
47 TRACE("auReader::auReader\n");
62 return ".au & .snd reader, " B_UTF8_COPYRIGHT
" by Marcus Overhagen";
67 auReader::Sniff(int32
*streamCount
)
69 TRACE("auReader::Sniff\n");
71 fSource
= dynamic_cast<BPositionIO
*>(Reader::Source());
73 TRACE("auReader::Sniff: not a BPositionIO\n");
77 int64 filesize
= Source()->Seek(0, SEEK_END
);
78 if (filesize
< sizeof(struct snd_header
)) {
79 TRACE("auReader::Sniff: File too small\n");
83 struct snd_header header
;
85 if (sizeof(header
) != Source()->ReadAt(0, &header
, sizeof(header
))) {
86 TRACE("auReader::Sniff: header reading failed\n");
90 if (UINT32(header
.magic
) != SND_MAGIC
) {
91 TRACE("auReader::Sniff: header not recognized\n");
95 TRACE("auReader::Sniff: we found something that looks like:\n");
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
));
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
);
109 if (fDataStart
> filesize
) {
110 TRACE("auReader::Sniff: data start too large\n");
113 if (fDataStart
+ fDataSize
> filesize
)
114 fDataSize
= filesize
- fDataStart
;
116 TRACE("auReader::Sniff: data size too small\n");
119 if (fChannelCount
< 1)
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;
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;
180 fBitsPerSample
= 0; break;
182 if (fBitsPerSample
== 0) {
183 TRACE("auReader::Sniff: sample format not recognized\n");
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
);
201 fBufferSize
= (BUFFER_SIZE
/ fBlockAlign
) * fBlockAlign
;
202 fBuffer
= malloc(fBufferSize
);
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
);
215 BMediaFormats formats
;
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
;
228 case SND_FORMAT_LINEAR_16
:
229 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_SHORT
;
231 case SND_FORMAT_LINEAR_24
:
232 fFormat
.u
.raw_audio
.format
= B_AUDIO_FORMAT_INT24
;
234 case SND_FORMAT_LINEAR_32
:
235 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_INT
;
237 case SND_FORMAT_FLOAT
:
238 fFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_FLOAT
;
240 case SND_FORMAT_DOUBLE
:
241 fFormat
.u
.raw_audio
.format
= B_AUDIO_FORMAT_FLOAT64
;
244 TRACE("auReader::Sniff: unhandled raw format\n");
247 fFormat
.u
.raw_audio
.byte_order
= B_MEDIA_BIG_ENDIAN
;
248 fFormat
.u
.raw_audio
.buffer_size
= fBufferSize
;
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
;
266 auReader::GetFileFormatInfo(media_file_format
*mff
)
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
;
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");
282 auReader::AllocateCookie(int32 streamNumber
, void **cookie
)
288 auReader::FreeCookie(void *cookie
)
295 auReader::GetStreamInfo(void *cookie
, int64
*frameCount
, bigtime_t
*duration
,
296 media_format
*format
, const void **infoBuffer
, size_t *infoSize
)
298 *frameCount
= fFrameCount
;
299 *duration
= fDuration
;
308 auReader::Seek(void *cookie
,
310 int64
*frame
, bigtime_t
*time
)
314 if (seekTo
& B_MEDIA_SEEK_TO_FRAME
) {
316 pos
= (*frame
* fBitsPerFrame
) / 8;
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
) {
323 pos
= (*time
* fFrameRate
* fBitsPerFrame
) / (1000000LL * 8);
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
);
333 *frame
= (8 * pos
) / fBitsPerFrame
;
335 *frame
= (pos
* fFrameCount
) / fDataSize
;
336 *time
= (*frame
* 1000000LL) / fFrameRate
;
338 TRACE("auReader::Seek newtime %Ld\n", *time
);
339 TRACE("auReader::Seek newframe %Ld\n", *frame
);
341 if (pos
< 0 || pos
> fDataSize
) {
342 TRACE("auReader::Seek invalid position %Ld\n", pos
);
352 auReader::GetNextChunk(void *cookie
,
353 const void **chunkBuffer
, size_t *chunkSize
,
354 media_header
*mediaHeader
)
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
;
361 int64 maxreadsize
= fDataSize
- fPosition
;
362 int32 readsize
= fBufferSize
;
363 if (maxreadsize
< readsize
)
364 readsize
= maxreadsize
;
366 return B_LAST_BUFFER_ERROR
;
368 if (readsize
!= Source()->ReadAt(fDataStart
+ fPosition
, fBuffer
, readsize
)) {
369 TRACE("auReader::GetNextChunk: unexpected read error\n");
373 // XXX if the stream has more than two channels, we need to reorder channel data here
375 fPosition
+= readsize
;
376 *chunkBuffer
= fBuffer
;
377 *chunkSize
= readsize
;
383 auReaderPlugin::NewReader()
389 MediaPlugin
*instantiate_plugin()
391 return new auReaderPlugin
;