xref: /haiku/src/add-ons/media/plugins/au_reader/au_reader.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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     %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 
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 	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 
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 %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 
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