xref: /haiku/src/add-ons/media/plugins/au_reader/au_reader.cpp (revision 95bac3fda53a4cb21880712d7b43f8c21db32a2e)
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 < 28) {
79 		TRACE("auReader::Sniff: File too small\n");
80 		return B_ERROR;
81 	}
82 
83 	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     %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);
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("WavReader::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_ENCODED_AUDIO
270 						| media_file_format::B_IMPERFECTLY_SEEKABLE;
271 	mff->family = B_MISC_FORMAT_FAMILY;
272 	mff->version = 100;
273 	strcpy(mff->mime_type, "audio/x-au");
274 	strcpy(mff->file_extension, "au");
275 	strcpy(mff->short_name,  "Sun audio file");
276 	strcpy(mff->pretty_name, "Sun audio file");
277 }
278 
279 
280 status_t
281 auReader::AllocateCookie(int32 streamNumber, void **cookie)
282 {
283 	return B_OK;
284 }
285 
286 status_t
287 auReader::FreeCookie(void *cookie)
288 {
289 	return B_OK;
290 }
291 
292 
293 status_t
294 auReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
295 						 media_format *format, void **infoBuffer, int32 *infoSize)
296 {
297 	*frameCount = fFrameCount;
298 	*duration = fDuration;
299 	*format = fFormat;
300 	*infoBuffer = 0;
301 	*infoSize = 0;
302 	return B_OK;
303 }
304 
305 
306 status_t
307 auReader::Seek(void *cookie,
308 				uint32 seekTo,
309 				int64 *frame, bigtime_t *time)
310 {
311 	int64 pos;
312 
313 	if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
314 		if (fRaw)
315 			pos = (*frame * fBitsPerFrame) / 8;
316 		else
317 			pos = (*frame * fDataSize) / fFrameCount;
318 		pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
319 		TRACE("auReader::Seek to frame %Ld, pos %Ld\n", *frame, pos);
320 	} else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
321 		if (fRaw)
322 			pos = (*time * fFrameRate * fBitsPerFrame) / (1000000LL * 8);
323 		else
324 			pos = (*time * fDataSize) / fDuration;
325 		pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
326 		TRACE("auReader::Seek to time %Ld, pos %Ld\n", *time, pos);
327 	} else {
328 		return B_ERROR;
329 	}
330 
331 	if (fRaw)
332 		*frame = (8 * pos) / fBitsPerFrame;
333 	else
334 		*frame = (pos * fFrameCount) / fDataSize;
335 	*time = (*frame * 1000000LL) / fFrameRate;
336 
337 	TRACE("auReader::Seek newtime %Ld\n", *time);
338 	TRACE("auReader::Seek newframe %Ld\n", *frame);
339 
340 	if (pos < 0 || pos > fDataSize) {
341 		TRACE("auReader::Seek invalid position %Ld\n", pos);
342 		return B_ERROR;
343 	}
344 
345 	fPosition = pos;
346 	return B_OK;
347 }
348 
349 
350 status_t
351 auReader::GetNextChunk(void *cookie,
352 						void **chunkBuffer, int32 *chunkSize,
353 						media_header *mediaHeader)
354 {
355 	// XXX it might be much better to not return any start_time information for encoded formats here,
356 	// XXX and instead use the last time returned from seek and count forward after decoding.
357 	mediaHeader->start_time = (((8 * fPosition) / fBitsPerFrame) * 1000000LL) / fFrameRate;
358 	mediaHeader->file_pos = fDataStart + fPosition;
359 
360 	int64 maxreadsize = fDataSize - fPosition;
361 	int32 readsize = fBufferSize;
362 	if (maxreadsize < readsize)
363 		readsize = maxreadsize;
364 	if (readsize == 0)
365 		return B_LAST_BUFFER_ERROR;
366 
367 	if (readsize != Source()->ReadAt(fDataStart + fPosition, fBuffer, readsize)) {
368 		TRACE("auReader::GetNextChunk: unexpected read error\n");
369 		return B_ERROR;
370 	}
371 
372 	// XXX if the stream has more than two channels, we need to reorder channel data here
373 
374 	fPosition += readsize;
375 	*chunkBuffer = fBuffer;
376 	*chunkSize = readsize;
377 	return B_OK;
378 }
379 
380 
381 Reader *
382 auReaderPlugin::NewReader()
383 {
384 	return new auReader;
385 }
386 
387 
388 MediaPlugin *instantiate_plugin()
389 {
390 	return new auReaderPlugin;
391 }
392