* Copyright (c) 2003-2004, Marcus Overhagen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <DataIO.h>
#include <ByteOrder.h>
#include <InterfaceDefs.h>
#include "RawFormats.h"
#include "au_reader.h"
#ifdef TRACE_AU_READER
#define TRACE printf
#else
#define TRACE(a...)
#endif
#define BUFFER_SIZE 16384
#define UINT32(a) ((uint32)B_BENDIAN_TO_HOST_INT32((a)))
auReader::auReader()
{
TRACE("auReader::auReader\n");
fBuffer = 0;
}
auReader::~auReader()
{
if (fBuffer)
free(fBuffer);
}
const char *
auReader::Copyright()
{
return ".au & .snd reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen";
}
status_t
auReader::Sniff(int32 *streamCount)
{
TRACE("auReader::Sniff\n");
fSource = dynamic_cast<BPositionIO *>(Reader::Source());
if (!fSource) {
TRACE("auReader::Sniff: not a BPositionIO\n");
return B_ERROR;
}
int64 filesize = Source()->Seek(0, SEEK_END);
if (filesize < sizeof(struct snd_header)) {
TRACE("auReader::Sniff: File too small\n");
return B_ERROR;
}
struct snd_header header;
if (sizeof(header) != Source()->ReadAt(0, &header, sizeof(header))) {
TRACE("auReader::Sniff: header reading failed\n");
return B_ERROR;
}
if (UINT32(header.magic) != SND_MAGIC) {
TRACE("auReader::Sniff: header not recognized\n");
return B_ERROR;
}
TRACE("auReader::Sniff: we found something that looks like:\n");
TRACE(" data_start %ld\n", UINT32(header.data_start));
TRACE(" data_size %ld\n", UINT32(header.data_size));
TRACE(" data_format %ld\n", UINT32(header.data_format));
TRACE(" sampling_rate %ld\n", UINT32(header.sampling_rate));
TRACE(" channel_count %ld\n", UINT32(header.channel_count));
fDataStart = UINT32(header.data_start);
fDataSize = UINT32(header.data_size);
fChannelCount = UINT32(header.channel_count);
fFrameRate = UINT32(header.sampling_rate);
fFormatCode = UINT32(header.data_format);
if (fDataStart > filesize) {
TRACE("auReader::Sniff: data start too large\n");
return B_ERROR;
}
if (fDataStart + fDataSize > filesize)
fDataSize = filesize - fDataStart;
if (fDataSize < 1) {
TRACE("auReader::Sniff: data size too small\n");
return B_ERROR;
}
if (fChannelCount < 1)
fChannelCount = 1;
if (fFrameRate < 1)
fFrameRate = 44100;
switch (fFormatCode) {
case SND_FORMAT_UNSPECIFIED: TRACE("SND_FORMAT_UNSPECIFIED\n"); break;
case SND_FORMAT_MULAW_8: TRACE("SND_FORMAT_MULAW_8\n"); break;
case SND_FORMAT_LINEAR_8: TRACE("SND_FORMAT_LINEAR_8\n"); break;
case SND_FORMAT_LINEAR_16: TRACE("SND_FORMAT_LINEAR_16\n"); break;
case SND_FORMAT_LINEAR_24: TRACE("SND_FORMAT_LINEAR_24\n"); break;
case SND_FORMAT_LINEAR_32: TRACE("SND_FORMAT_LINEAR_32\n"); break;
case SND_FORMAT_FLOAT: TRACE("SND_FORMAT_FLOAT\n"); break;
case SND_FORMAT_DOUBLE: TRACE("SND_FORMAT_DOUBLE\n"); break;
case SND_FORMAT_INDIRECT: TRACE("SND_FORMAT_INDIRECT\n"); break;
case SND_FORMAT_NESTED: TRACE("SND_FORMAT_NESTED\n"); break;
case SND_FORMAT_DSP_CORE: TRACE("SND_FORMAT_DSP_CORE\n"); break;
case SND_FORMAT_DSP_DATA_8: TRACE("SND_FORMAT_DSP_DATA_8\n"); break;
case SND_FORMAT_DSP_DATA_16: TRACE("SND_FORMAT_DSP_DATA_16\n"); break;
case SND_FORMAT_DSP_DATA_24: TRACE("SND_FORMAT_DSP_DATA_24\n"); break;
case SND_FORMAT_DSP_DATA_32: TRACE("SND_FORMAT_DSP_DATA_32\n"); break;
case SND_FORMAT_DISPLAY: TRACE("SND_FORMAT_DISPLAY\n"); break;
case SND_FORMAT_MULAW_SQUELCH: TRACE("SND_FORMAT_MULAW_SQUELCH\n"); break;
case SND_FORMAT_EMPHASIZED: TRACE("SND_FORMAT_EMPHASIZED\n"); break;
case SND_FORMAT_COMPRESSED: TRACE("SND_FORMAT_COMPRESSED\n"); break;
case SND_FORMAT_COMPRESSED_EMPHASIZED: TRACE("SND_FORMAT_COMPRESSED_EMPHASIZED\n"); break;
case SND_FORMAT_DSP_COMMANDS: TRACE("SND_FORMAT_DSP_COMMANDS\n"); break;
case SND_FORMAT_DSP_COMMANDS_SAMPLES: TRACE("SND_FORMAT_DSP_COMMANDS_SAMPLES\n"); break;
case SND_FORMAT_ADPCM_G721: TRACE("SND_FORMAT_ADPCM_G721\n"); break;
case SND_FORMAT_ADPCM_G722: TRACE("SND_FORMAT_ADPCM_G722\n"); break;
case SND_FORMAT_ADPCM_G723_3: TRACE("SND_FORMAT_ADPCM_G723_3\n"); break;
case SND_FORMAT_ADPCM_G723_5: TRACE("SND_FORMAT_ADPCM_G723_5\n"); break;
case SND_FORMAT_ALAW_8: TRACE("SND_FORMAT_ALAW_8\n"); break;
}
switch (fFormatCode) {
case SND_FORMAT_MULAW_8:
fBitsPerSample = 8; fRaw = false; break;
case SND_FORMAT_LINEAR_8:
fBitsPerSample = 8; fRaw = true; break;
case SND_FORMAT_LINEAR_16:
fBitsPerSample = 16; fRaw = true; break;
case SND_FORMAT_LINEAR_24:
fBitsPerSample = 24; fRaw = true; break;
case SND_FORMAT_LINEAR_32:
fBitsPerSample = 32; fRaw = true; break;
case SND_FORMAT_FLOAT:
fBitsPerSample = 32; fRaw = true; break;
case SND_FORMAT_DOUBLE:
fBitsPerSample = 64; fRaw = true; break;
case SND_FORMAT_ADPCM_G721:
fBitsPerSample = 4; fRaw = false; break;
case SND_FORMAT_ADPCM_G722:
fBitsPerSample = 8; fRaw = false; break;
case SND_FORMAT_ADPCM_G723_3:
fBitsPerSample = 3; fRaw = false; break;
case SND_FORMAT_ADPCM_G723_5:
fBitsPerSample = 5; fRaw = false; break;
case SND_FORMAT_ALAW_8:
fBitsPerSample = 8; fRaw = false; break;
default:
fBitsPerSample = 0; break;
}
if (fBitsPerSample == 0) {
TRACE("auReader::Sniff: sample format not recognized\n");
return B_ERROR;
}
fFrameCount = (8 * fDataSize) / (fChannelCount * fBitsPerSample);
fDuration = (1000000LL * fFrameCount) / fFrameRate;
fBitsPerFrame = fChannelCount * fBitsPerSample;
fBlockAlign = fBitsPerFrame;
while (fBlockAlign % 8 && fBlockAlign < 1000)
fBlockAlign += fBlockAlign;
if (fBlockAlign % 8) {
TRACE("auReader::Sniff: can't find block alignment, fChannelCount %d, fBitsPerSample %d\n", fChannelCount, fBitsPerSample);
return B_ERROR;
}
fBlockAlign /= 8;
fPosition = 0;
fBufferSize = (BUFFER_SIZE / fBlockAlign) * fBlockAlign;
fBuffer = malloc(fBufferSize);
TRACE(" fDataStart %lld\n", fDataStart);
TRACE(" fDataSize %lld\n", fDataSize);
TRACE(" fFrameCount %lld\n", fFrameCount);
TRACE(" fDuration %lld\n", fDuration);
TRACE(" fChannelCount %d\n", fChannelCount);
TRACE(" fFrameRate %ld\n", fFrameRate);
TRACE(" fBitsPerSample %d\n", fBitsPerSample);
TRACE(" fBlockAlign %d\n", fBlockAlign);
TRACE(" fFormatCode %ld\n", fFormatCode);
TRACE(" fRaw %d\n", fRaw);
BMediaFormats formats;
if (fRaw) {
media_format_description description;
description.family = B_BEOS_FORMAT_FAMILY;
description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
formats.GetFormatFor(description, &fFormat);
fFormat.u.raw_audio.frame_rate = (fFrameRate == 8012) ? SND_RATE_8012 : fFrameRate;
fFormat.u.raw_audio.channel_count = fChannelCount;
switch (fFormatCode) {
case SND_FORMAT_LINEAR_8:
fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR;
break;
case SND_FORMAT_LINEAR_16:
fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
break;
case SND_FORMAT_LINEAR_24:
fFormat.u.raw_audio.format = B_AUDIO_FORMAT_INT24;
break;
case SND_FORMAT_LINEAR_32:
fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_INT;
break;
case SND_FORMAT_FLOAT:
fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
break;
case SND_FORMAT_DOUBLE:
fFormat.u.raw_audio.format = B_AUDIO_FORMAT_FLOAT64;
break;
default:
TRACE("auReader::Sniff: unhandled raw format\n");
return B_ERROR;
}
fFormat.u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
fFormat.u.raw_audio.buffer_size = fBufferSize;
} else {
media_format_description description;
description.family = B_MISC_FORMAT_FAMILY;
description.u.misc.file_format = 'au';
description.u.misc.codec = fFormatCode;
formats.GetFormatFor(description, &fFormat);
fFormat.u.encoded_audio.output.frame_rate = fFrameRate;
fFormat.u.encoded_audio.output.channel_count = fChannelCount;
}
*streamCount = 1;
return B_OK;
}
void
auReader::GetFileFormatInfo(media_file_format *mff)
{
mff->capabilities = media_file_format::B_READABLE
| media_file_format::B_KNOWS_RAW_AUDIO
| media_file_format::B_KNOWS_ENCODED_AUDIO
| media_file_format::B_IMPERFECTLY_SEEKABLE;
mff->family = B_MISC_FORMAT_FAMILY;
mff->version = 100;
strlcpy(mff->mime_type, "audio/x-au", sizeof(mff->mime_type));
strlcpy(mff->file_extension, "au", sizeof(mff->file_extension));
strlcpy(mff->short_name, "Sun audio file", sizeof(mff->short_name));
strlcpy(mff->pretty_name, "Sun audio file", sizeof(mff->pretty_name));
}
status_t
auReader::AllocateCookie(int32 streamNumber, void **cookie)
{
return B_OK;
}
status_t
auReader::FreeCookie(void *cookie)
{
return B_OK;
}
status_t
auReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
media_format *format, const void **infoBuffer, size_t *infoSize)
{
*frameCount = fFrameCount;
*duration = fDuration;
*format = fFormat;
*infoBuffer = 0;
*infoSize = 0;
return B_OK;
}
status_t
auReader::Seek(void *cookie,
uint32 seekTo,
int64 *frame, bigtime_t *time)
{
int64 pos;
if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
if (fRaw)
pos = (*frame * fBitsPerFrame) / 8;
else
pos = (*frame * fDataSize) / fFrameCount;
pos = (pos / fBlockAlign) * fBlockAlign;
TRACE("auReader::Seek to frame %lld, pos %lld\n", *frame, pos);
} else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
if (fRaw)
pos = (*time * fFrameRate * fBitsPerFrame) / (1000000LL * 8);
else
pos = (*time * fDataSize) / fDuration;
pos = (pos / fBlockAlign) * fBlockAlign;
TRACE("auReader::Seek to time %lld, pos %lld\n", *time, pos);
} else {
return B_ERROR;
}
if (fRaw)
*frame = (8 * pos) / fBitsPerFrame;
else
*frame = (pos * fFrameCount) / fDataSize;
*time = (*frame * 1000000LL) / fFrameRate;
TRACE("auReader::Seek newtime %lld\n", *time);
TRACE("auReader::Seek newframe %lld\n", *frame);
if (pos < 0 || pos > fDataSize) {
TRACE("auReader::Seek invalid position %lld\n", pos);
return B_ERROR;
}
fPosition = pos;
return B_OK;
}
status_t
auReader::GetNextChunk(void *cookie,
const void **chunkBuffer, size_t *chunkSize,
media_header *mediaHeader)
{
mediaHeader->start_time = (((8 * fPosition) / fBitsPerFrame) * 1000000LL) / fFrameRate;
mediaHeader->file_pos = fDataStart + fPosition;
int64 maxreadsize = fDataSize - fPosition;
int32 readsize = fBufferSize;
if (maxreadsize < readsize)
readsize = maxreadsize;
if (readsize == 0)
return B_LAST_BUFFER_ERROR;
if (readsize != Source()->ReadAt(fDataStart + fPosition, fBuffer, readsize)) {
TRACE("auReader::GetNextChunk: unexpected read error\n");
return B_ERROR;
}
fPosition += readsize;
*chunkBuffer = fBuffer;
*chunkSize = readsize;
return B_OK;
}
Reader *
auReaderPlugin::NewReader()
{
return new auReader;
}
MediaPlugin *instantiate_plugin()
{
return new auReaderPlugin;
}