xref: /haiku/src/add-ons/media/plugins/au_reader/au_reader.cpp (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
1 #include <stdio.h>
2 #include <string.h>
3 #include <malloc.h>
4 #include <DataIO.h>
5 #include <ByteOrder.h>
6 #include <InterfaceDefs.h>
7 #include "RawFormats.h"
8 #include "au_reader.h"
9 
10 #define TRACE_THIS 1
11 #if TRACE_THIS
12   #define TRACE printf
13 #else
14   #define TRACE(a...)
15 #endif
16 
17 #define BUFFER_SIZE	16384
18 
19 #define UINT32(a) 		((uint32)B_BENDIAN_TO_HOST_INT32((a)))
20 
21 auReader::auReader()
22 {
23 	TRACE("auReader::auReader\n");
24 	fBuffer = 0;
25 }
26 
27 
28 auReader::~auReader()
29 {
30 	if (fBuffer)
31 		free(fBuffer);
32 }
33 
34 
35 const char *
36 auReader::Copyright()
37 {
38 	return ".au & .snd reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen";
39 }
40 
41 
42 status_t
43 auReader::Sniff(int32 *streamCount)
44 {
45 	TRACE("auReader::Sniff\n");
46 
47 	fSource = dynamic_cast<BPositionIO *>(Reader::Source());
48 	if (!fSource) {
49 		TRACE("auReader::Sniff: not a BPositionIO\n");
50 		return B_ERROR;
51 	}
52 
53 	int64 filesize = Source()->Seek(0, SEEK_END);
54 	if (filesize < 28) {
55 		TRACE("auReader::Sniff: File too small\n");
56 		return B_ERROR;
57 	}
58 
59 	snd_header header;
60 
61 	if (sizeof(header) != Source()->ReadAt(0, &header, sizeof(header))) {
62 		TRACE("auReader::Sniff: header reading failed\n");
63 		return B_ERROR;
64 	}
65 
66 	if (UINT32(header.magic) != SND_MAGIC) {
67 		TRACE("auReader::Sniff: header not recognized\n");
68 		return B_ERROR;
69 	}
70 
71 	TRACE("auReader::Sniff: we found something that looks like:\n");
72 
73 	TRACE("  data_start        %ld\n", UINT32(header.data_start));
74 	TRACE("  data_size         %ld\n", UINT32(header.data_size));
75 	TRACE("  data_format       %ld\n", UINT32(header.data_format));
76 	TRACE("  sampling_rate     %ld\n", UINT32(header.sampling_rate));
77 	TRACE("  channel_count     %ld\n", UINT32(header.channel_count));
78 
79 	fDataStart = UINT32(header.data_start);
80 	fDataSize = UINT32(header.data_size);
81 	fChannelCount = UINT32(header.channel_count);
82 	fFrameRate = UINT32(header.sampling_rate);
83 	fFormatCode = UINT32(header.data_format);
84 
85 	if (fDataStart > filesize) {
86 		TRACE("auReader::Sniff: data start too large\n");
87 		return B_ERROR;
88 	}
89 	if (fDataStart + fDataSize > filesize)
90 		fDataSize = filesize - fDataStart;
91 	if (fDataSize < 1) {
92 		TRACE("auReader::Sniff: data size too small\n");
93 		return B_ERROR;
94 	}
95 	if (fChannelCount < 1)
96 		fChannelCount = 1;
97 	if (fFrameRate < 1)
98 		fFrameRate = 44100;
99 
100 	switch (fFormatCode) {
101 		case SND_FORMAT_UNSPECIFIED: TRACE("SND_FORMAT_UNSPECIFIED\n"); break;
102 		case SND_FORMAT_MULAW_8: TRACE("SND_FORMAT_MULAW_8\n"); break;
103 		case SND_FORMAT_LINEAR_8: TRACE("SND_FORMAT_LINEAR_8\n"); break;
104 		case SND_FORMAT_LINEAR_16: TRACE("SND_FORMAT_LINEAR_16\n"); break;
105 		case SND_FORMAT_LINEAR_24: TRACE("SND_FORMAT_LINEAR_24\n"); break;
106 		case SND_FORMAT_LINEAR_32: TRACE("SND_FORMAT_LINEAR_32\n"); break;
107 		case SND_FORMAT_FLOAT: TRACE("SND_FORMAT_FLOAT\n"); break;
108 		case SND_FORMAT_DOUBLE: TRACE("SND_FORMAT_DOUBLE\n"); break;
109 		case SND_FORMAT_INDIRECT: TRACE("SND_FORMAT_INDIRECT\n"); break;
110 		case SND_FORMAT_NESTED: TRACE("SND_FORMAT_NESTED\n"); break;
111 		case SND_FORMAT_DSP_CORE: TRACE("SND_FORMAT_DSP_CORE\n"); break;
112 		case SND_FORMAT_DSP_DATA_8: TRACE("SND_FORMAT_DSP_DATA_8\n"); break;
113 		case SND_FORMAT_DSP_DATA_16: TRACE("SND_FORMAT_DSP_DATA_16\n"); break;
114 		case SND_FORMAT_DSP_DATA_24: TRACE("SND_FORMAT_DSP_DATA_24\n"); break;
115 		case SND_FORMAT_DSP_DATA_32: TRACE("SND_FORMAT_DSP_DATA_32\n"); break;
116 		case SND_FORMAT_DISPLAY: TRACE("SND_FORMAT_DISPLAY\n"); break;
117 		case SND_FORMAT_MULAW_SQUELCH: TRACE("SND_FORMAT_MULAW_SQUELCH\n"); break;
118 		case SND_FORMAT_EMPHASIZED: TRACE("SND_FORMAT_EMPHASIZED\n"); break;
119 		case SND_FORMAT_COMPRESSED: TRACE("SND_FORMAT_COMPRESSED\n"); break;
120 		case SND_FORMAT_COMPRESSED_EMPHASIZED: TRACE("SND_FORMAT_COMPRESSED_EMPHASIZED\n"); break;
121 		case SND_FORMAT_DSP_COMMANDS: TRACE("SND_FORMAT_DSP_COMMANDS\n"); break;
122 		case SND_FORMAT_DSP_COMMANDS_SAMPLES: TRACE("SND_FORMAT_DSP_COMMANDS_SAMPLES\n"); break;
123 		case SND_FORMAT_ADPCM_G721: TRACE("SND_FORMAT_ADPCM_G721\n"); break;
124 		case SND_FORMAT_ADPCM_G722: TRACE("SND_FORMAT_ADPCM_G722\n"); break;
125 		case SND_FORMAT_ADPCM_G723_3: TRACE("SND_FORMAT_ADPCM_G723_3\n"); break;
126 		case SND_FORMAT_ADPCM_G723_5: TRACE("SND_FORMAT_ADPCM_G723_5\n"); break;
127 		case SND_FORMAT_ALAW_8: TRACE("SND_FORMAT_ALAW_8\n"); break;
128 	}
129 
130 	switch (fFormatCode) {
131 		case SND_FORMAT_MULAW_8:
132 			fBitsPerSample = 8; fRaw = false; break;
133 		case SND_FORMAT_LINEAR_8:
134 			fBitsPerSample = 8; fRaw = true; break;
135 		case SND_FORMAT_LINEAR_16:
136 			fBitsPerSample = 16; fRaw = true; break;
137 		case SND_FORMAT_LINEAR_24:
138 			fBitsPerSample = 24; fRaw = true; break;
139 		case SND_FORMAT_LINEAR_32:
140 			fBitsPerSample = 32; fRaw = true; break;
141 		case SND_FORMAT_FLOAT:
142 			fBitsPerSample = 32; fRaw = true; break;
143 		case SND_FORMAT_DOUBLE:
144 			fBitsPerSample = 64; fRaw = true; break;
145 		case SND_FORMAT_ADPCM_G721:
146 			fBitsPerSample = 4; fRaw = false; break;
147 		case SND_FORMAT_ADPCM_G722:
148 			fBitsPerSample = 8; fRaw = false; break;
149 		case SND_FORMAT_ADPCM_G723_3:
150 			fBitsPerSample = 3; fRaw = false; break;
151 		case SND_FORMAT_ADPCM_G723_5:
152 			fBitsPerSample = 5; fRaw = false; break;
153 		case SND_FORMAT_ALAW_8:
154 			fBitsPerSample = 8; fRaw = false; break;
155 		default:
156 			fBitsPerSample = 0; break;
157 	}
158 	if (fBitsPerSample == 0) {
159 		TRACE("auReader::Sniff: sample format not recognized\n");
160 		return B_ERROR;
161 	}
162 
163 	fFrameCount = (8 * fDataSize) / (fChannelCount * fBitsPerSample);
164 	fDuration = (1000000LL * fFrameCount) / fFrameRate;
165 	fBitsPerFrame = fChannelCount * fBitsPerSample;
166 	fBlockAlign = fBitsPerFrame;
167 	while (fBlockAlign % 8 && fBlockAlign < 1000)
168 		fBlockAlign += fBlockAlign;
169 	if (fBlockAlign % 8) {
170 		TRACE("auReader::Sniff: can't find block alignment, fChannelCount %d, fBitsPerSample %d\n", fChannelCount, fBitsPerSample);
171 		return B_ERROR;
172 	}
173 	fBlockAlign /= 8;
174 
175 	fPosition = 0;
176 
177 	fBufferSize = (BUFFER_SIZE / fBlockAlign) * fBlockAlign;
178 	fBuffer = malloc(fBufferSize);
179 
180 	TRACE("  fDataStart     %Ld\n", fDataStart);
181 	TRACE("  fDataSize      %Ld\n", fDataSize);
182 	TRACE("  fFrameCount    %Ld\n", fFrameCount);
183 	TRACE("  fDuration      %Ld\n", fDuration);
184 	TRACE("  fChannelCount  %d\n", fChannelCount);
185 	TRACE("  fFrameRate     %ld\n", fFrameRate);
186 	TRACE("  fBitsPerSample %d\n", fBitsPerSample);
187 	TRACE("  fBlockAlign    %d\n", fBlockAlign);
188 	TRACE("  fFormatCode    %ld\n", fFormatCode);
189 	TRACE("  fRaw           %d\n", fRaw);
190 
191 	BMediaFormats formats;
192 	if (fRaw) {
193 		// a raw PCM format
194 		media_format_description description;
195 		description.family = B_BEOS_FORMAT_FAMILY;
196 		description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
197 		formats.GetFormatFor(description, &fFormat);
198 		fFormat.u.raw_audio.frame_rate = (fFrameRate == 8012) ? SND_RATE_8012 : fFrameRate;
199 		fFormat.u.raw_audio.channel_count = fChannelCount;
200 		switch (fFormatCode) {
201 			case SND_FORMAT_LINEAR_8:
202 				fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR;
203 				break;
204 			case SND_FORMAT_LINEAR_16:
205 				fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
206 				break;
207 			case SND_FORMAT_LINEAR_24:
208 				fFormat.u.raw_audio.format = B_AUDIO_FORMAT_INT24;
209 				break;
210 			case SND_FORMAT_LINEAR_32:
211 				fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_INT;
212 				break;
213 			case SND_FORMAT_FLOAT:
214 				fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
215 				break;
216 			case SND_FORMAT_DOUBLE:
217 				fFormat.u.raw_audio.format = B_AUDIO_FORMAT_FLOAT64;
218 				break;
219 			default:
220 				TRACE("WavReader::Sniff: unhandled raw format\n");
221 				return B_ERROR;
222 		}
223 		fFormat.u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
224 		fFormat.u.raw_audio.buffer_size = fBufferSize;
225 	} else {
226 		// some encoded format
227 		media_format_description description;
228 		description.family = B_MISC_FORMAT_FAMILY;
229 		description.u.misc.file_format = 'au';
230 		description.u.misc.codec = fFormatCode;
231 		formats.GetFormatFor(description, &fFormat);
232 		fFormat.u.encoded_audio.output.frame_rate = fFrameRate;
233 		fFormat.u.encoded_audio.output.channel_count = fChannelCount;
234 	}
235 
236 	*streamCount = 1;
237 	return B_OK;
238 }
239 
240 
241 void
242 auReader::GetFileFormatInfo(media_file_format *mff)
243 {
244 	mff->capabilities =   media_file_format::B_READABLE
245 						| media_file_format::B_KNOWS_ENCODED_AUDIO
246 						| media_file_format::B_IMPERFECTLY_SEEKABLE;
247 	mff->family = B_MISC_FORMAT_FAMILY;
248 	mff->version = 100;
249 	strcpy(mff->mime_type, "audio/x-au");
250 	strcpy(mff->file_extension, "au");
251 	strcpy(mff->short_name,  "Sun audio file");
252 	strcpy(mff->pretty_name, "Sun audio file");
253 }
254 
255 
256 status_t
257 auReader::AllocateCookie(int32 streamNumber, void **cookie)
258 {
259 	return B_OK;
260 }
261 
262 status_t
263 auReader::FreeCookie(void *cookie)
264 {
265 	return B_OK;
266 }
267 
268 
269 status_t
270 auReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
271 						 media_format *format, void **infoBuffer, int32 *infoSize)
272 {
273 	*frameCount = fFrameCount;
274 	*duration = fDuration;
275 	*format = fFormat;
276 	*infoBuffer = 0;
277 	*infoSize = 0;
278 	return B_OK;
279 }
280 
281 
282 status_t
283 auReader::Seek(void *cookie,
284 				uint32 seekTo,
285 				int64 *frame, bigtime_t *time)
286 {
287 	int64 pos;
288 
289 	if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
290 		if (fRaw)
291 			pos = (*frame * fBitsPerFrame) / 8;
292 		else
293 			pos = (*frame * fDataSize) / fFrameCount;
294 		pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
295 		TRACE("auReader::Seek to frame %Ld, pos %Ld\n", *frame, pos);
296 	} else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
297 		if (fRaw)
298 			pos = (*time * fFrameRate * fBitsPerFrame) / (1000000LL * 8);
299 		else
300 			pos = (*time * fDataSize) / fDuration;
301 		pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
302 		TRACE("auReader::Seek to time %Ld, pos %Ld\n", *time, pos);
303 	} else {
304 		return B_ERROR;
305 	}
306 
307 	if (fRaw)
308 		*frame = (8 * pos) / fBitsPerFrame;
309 	else
310 		*frame = (pos * fFrameCount) / fDataSize;
311 	*time = (*frame * 1000000LL) / fFrameRate;
312 
313 	TRACE("auReader::Seek newtime %Ld\n", *time);
314 	TRACE("auReader::Seek newframe %Ld\n", *frame);
315 
316 	if (pos < 0 || pos > fDataSize) {
317 		TRACE("auReader::Seek invalid position %Ld\n", pos);
318 		return B_ERROR;
319 	}
320 
321 	fPosition = pos;
322 	return B_OK;
323 }
324 
325 
326 status_t
327 auReader::GetNextChunk(void *cookie,
328 						void **chunkBuffer, int32 *chunkSize,
329 						media_header *mediaHeader)
330 {
331 	// XXX it might be much better to not return any start_time information for encoded formats here,
332 	// XXX and instead use the last time returned from seek and count forward after decoding.
333 	mediaHeader->start_time = (((8 * fPosition) / fBitsPerFrame) * 1000000LL) / fFrameRate;
334 	mediaHeader->file_pos = fDataStart + fPosition;
335 
336 	int64 maxreadsize = fDataSize - fPosition;
337 	int32 readsize = fBufferSize;
338 	if (maxreadsize < readsize)
339 		readsize = maxreadsize;
340 	if (readsize == 0)
341 		return B_LAST_BUFFER_ERROR;
342 
343 	if (readsize != Source()->ReadAt(fDataStart + fPosition, fBuffer, readsize)) {
344 		TRACE("auReader::GetNextChunk: unexpected read error\n");
345 		return B_ERROR;
346 	}
347 
348 	// XXX if the stream has more than two channels, we need to reorder channel data here
349 
350 	fPosition += readsize;
351 	*chunkBuffer = fBuffer;
352 	*chunkSize = readsize;
353 	return B_OK;
354 }
355 
356 
357 Reader *
358 auReaderPlugin::NewReader()
359 {
360 	return new auReader;
361 }
362 
363 
364 MediaPlugin *instantiate_plugin()
365 {
366 	return new auReaderPlugin;
367 }
368