|
|
|
@@ -0,0 +1,348 @@
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <malloc.h>
|
|
|
|
|
#include <DataIO.h>
|
|
|
|
|
#include <ByteOrder.h>
|
|
|
|
|
#include <InterfaceDefs.h>
|
|
|
|
|
#include "au_reader.h"
|
|
|
|
|
|
|
|
|
|
#define TRACE_THIS 1
|
|
|
|
|
#if TRACE_THIS
|
|
|
|
|
#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 < 28) {
|
|
|
|
|
TRACE("auReader::Sniff: File too small\n");
|
|
|
|
|
return B_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = 28 + UINT32(header.data_start);
|
|
|
|
|
fDataSize = UINT32(header.data_size);
|
|
|
|
|
fChannelCount = UINT32(header.channel_count);
|
|
|
|
|
fFrameRate = UINT32(header.sampling_rate);
|
|
|
|
|
fFormatCode = UINT32(header.data_format);
|
|
|
|
|
|
|
|
|
|
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 = 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 %Ld\n", fDataStart);
|
|
|
|
|
TRACE(" fDataSize %Ld\n", fDataSize);
|
|
|
|
|
TRACE(" fFrameCount %Ld\n", fFrameCount);
|
|
|
|
|
TRACE(" fDuration %Ld\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) {
|
|
|
|
|
// a raw PCM format
|
|
|
|
|
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 = media_raw_audio_format::B_AUDIO_INT;
|
|
|
|
|
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 = media_raw_audio_format::B_AUDIO_FLOAT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fFormat.u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
|
|
|
|
|
fFormat.u.raw_audio.buffer_size = fBufferSize;
|
|
|
|
|
} else {
|
|
|
|
|
// some encoded format
|
|
|
|
|
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_ENCODED_AUDIO
|
|
|
|
|
| media_file_format::B_IMPERFECTLY_SEEKABLE;
|
|
|
|
|
mff->family = B_MISC_FORMAT_FAMILY;
|
|
|
|
|
mff->version = 100;
|
|
|
|
|
strcpy(mff->mime_type, "audio/x-au");
|
|
|
|
|
strcpy(mff->file_extension, "au");
|
|
|
|
|
strcpy(mff->short_name, "Sun audio file");
|
|
|
|
|
strcpy(mff->pretty_name, "Sun audio file");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, void **infoBuffer, int32 *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; // round down to a block start
|
|
|
|
|
TRACE("auReader::Seek to frame %Ld, pos %Ld\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; // round down to a block start
|
|
|
|
|
TRACE("auReader::Seek to time %Ld, pos %Ld\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 %Ld\n", *time);
|
|
|
|
|
TRACE("auReader::Seek newframe %Ld\n", *frame);
|
|
|
|
|
|
|
|
|
|
if (pos < 0 || pos > fDataSize) {
|
|
|
|
|
TRACE("auReader::Seek invalid position %Ld\n", pos);
|
|
|
|
|
return B_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fPosition = pos;
|
|
|
|
|
return B_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
|
auReader::GetNextChunk(void *cookie,
|
|
|
|
|
void **chunkBuffer, int32 *chunkSize,
|
|
|
|
|
media_header *mediaHeader)
|
|
|
|
|
{
|
|
|
|
|
// XXX it might be much better to not return any start_time information for encoded formats here,
|
|
|
|
|
// XXX and instead use the last time returned from seek and count forward after decoding.
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX if the stream has more than two channels, we need to reorder channel data here
|
|
|
|
|
|
|
|
|
|
fPosition += readsize;
|
|
|
|
|
*chunkBuffer = fBuffer;
|
|
|
|
|
*chunkSize = readsize;
|
|
|
|
|
return B_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Reader *
|
|
|
|
|
auReaderPlugin::NewReader()
|
|
|
|
|
{
|
|
|
|
|
return new auReader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MediaPlugin *instantiate_plugin()
|
|
|
|
|
{
|
|
|
|
|
return new auReaderPlugin;
|
|
|
|
|
}
|