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
45auReader::auReader()
46{
47	TRACE("auReader::auReader\n");
48	fBuffer = 0;
49}
50
51
52auReader::~auReader()
53{
54	if (fBuffer)
55		free(fBuffer);
56}
57
58
59const char *
60auReader::Copyright()
61{
62	return ".au & .snd reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen";
63}
64
65
66status_t
67auReader::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     %lld\n", fDataStart);
205	TRACE("  fDataSize      %lld\n", fDataSize);
206	TRACE("  fFrameCount    %lld\n", fFrameCount);
207	TRACE("  fDuration      %lld\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
265void
266auReader::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	strlcpy(mff->mime_type, "audio/x-au", sizeof(mff->mime_type));
275	strlcpy(mff->file_extension, "au", sizeof(mff->file_extension));
276	strlcpy(mff->short_name,  "Sun audio file", sizeof(mff->short_name));
277	strlcpy(mff->pretty_name, "Sun audio file", sizeof(mff->pretty_name));
278}
279
280
281status_t
282auReader::AllocateCookie(int32 streamNumber, void **cookie)
283{
284	return B_OK;
285}
286
287status_t
288auReader::FreeCookie(void *cookie)
289{
290	return B_OK;
291}
292
293
294status_t
295auReader::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
307status_t
308auReader::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 %lld, pos %lld\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 %lld, pos %lld\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 %lld\n", *time);
339	TRACE("auReader::Seek newframe %lld\n", *frame);
340
341	if (pos < 0 || pos > fDataSize) {
342		TRACE("auReader::Seek invalid position %lld\n", pos);
343		return B_ERROR;
344	}
345
346	fPosition = pos;
347	return B_OK;
348}
349
350
351status_t
352auReader::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
382Reader *
383auReaderPlugin::NewReader()
384{
385	return new auReader;
386}
387
388
389MediaPlugin *instantiate_plugin()
390{
391	return new auReaderPlugin;
392}
393