From 228c585f821e8e09c25518a08c465fc93054011d Mon Sep 17 00:00:00 2001 From: beveloper Date: Mon, 22 Dec 2003 23:46:49 +0000 Subject: [PATCH] added added au (Sun audio file) format reader git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5734 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/plugins/Jamfile | 1 + src/add-ons/media/plugins/au_reader/Jamfile | 9 + src/add-ons/media/plugins/au_reader/au.h | 51 +++ .../media/plugins/au_reader/au_reader.cpp | 348 ++++++++++++++++++ .../media/plugins/au_reader/au_reader.h | 64 ++++ 5 files changed, 473 insertions(+) create mode 100644 src/add-ons/media/plugins/au_reader/Jamfile create mode 100644 src/add-ons/media/plugins/au_reader/au.h create mode 100644 src/add-ons/media/plugins/au_reader/au_reader.cpp create mode 100644 src/add-ons/media/plugins/au_reader/au_reader.h diff --git a/src/add-ons/media/plugins/Jamfile b/src/add-ons/media/plugins/Jamfile index 91d9605105..d609443bcb 100644 --- a/src/add-ons/media/plugins/Jamfile +++ b/src/add-ons/media/plugins/Jamfile @@ -1,5 +1,6 @@ SubDir OBOS_TOP src add-ons media plugins ; +SubInclude OBOS_TOP src add-ons media plugins au_reader ; SubInclude OBOS_TOP src add-ons media plugins avi_reader ; SubInclude OBOS_TOP src add-ons media plugins mp3_reader ; SubInclude OBOS_TOP src add-ons media plugins wav_reader ; diff --git a/src/add-ons/media/plugins/au_reader/Jamfile b/src/add-ons/media/plugins/au_reader/Jamfile new file mode 100644 index 0000000000..61fc847627 --- /dev/null +++ b/src/add-ons/media/plugins/au_reader/Jamfile @@ -0,0 +1,9 @@ +SubDir OBOS_TOP src add-ons media plugins au_reader ; + +UsePrivateHeaders media ; + +Addon au_reader : media plugins : + au_reader.cpp +; + +LinkSharedOSLibs au_reader : be libmedia.so ; diff --git a/src/add-ons/media/plugins/au_reader/au.h b/src/add-ons/media/plugins/au_reader/au.h new file mode 100644 index 0000000000..6c7567079b --- /dev/null +++ b/src/add-ons/media/plugins/au_reader/au.h @@ -0,0 +1,51 @@ +#ifndef _AU_H +#define _AU_H + +struct snd_header { + uint32 magic; // magic number '.snd' + uint32 data_start; // offset to the data + uint32 data_size; // number of bytes of data + uint32 data_format; // data format code + uint32 sampling_rate; // sampling rate + uint32 channel_count; // number of channels + char info[4]; // optional text information, NULL termintated, at least 4 bytes +}; + +enum { + SND_FORMAT_UNSPECIFIED = 0, // unspecified format + SND_FORMAT_MULAW_8 = 1, // 8-bit mu-law samples + SND_FORMAT_LINEAR_8 = 2, // 8-bit linear samples + SND_FORMAT_LINEAR_16 = 3, // 16-bit linear samples + SND_FORMAT_LINEAR_24 = 4, // 24-bit linear samples + SND_FORMAT_LINEAR_32 = 5, // 32-bit linear samples + SND_FORMAT_FLOAT = 6, // 32-bit IEEE floating-point samples + SND_FORMAT_DOUBLE = 7, // 64-bit IEEE floating-point samples + SND_FORMAT_INDIRECT = 8, // fragmented sampled data + SND_FORMAT_NESTED = 9, + SND_FORMAT_DSP_CORE = 10, // DSP program + SND_FORMAT_DSP_DATA_8 = 11, // 8-bit fixed-point samples + SND_FORMAT_DSP_DATA_16 = 12,// 16-bit fixed-point samples + SND_FORMAT_DSP_DATA_24 = 13,// 24-bit fixed-point samples + SND_FORMAT_DSP_DATA_32 = 14,// 32-bit fixed-point samples + SND_FORMAT_15 = 15, + SND_FORMAT_DISPLAY = 16, // non-audio display data + SND_FORMAT_MULAW_SQUELCH = 17, + SND_FORMAT_EMPHASIZED = 18, // 16-bit linear with emphasis + SND_FORMAT_COMPRESSED = 19, // 16-bit linear with compression + SND_FORMAT_COMPRESSED_EMPHASIZED = 20, // A combination of the two above + SND_FORMAT_DSP_COMMANDS = 21,// Music Kit DSP commands + SND_FORMAT_DSP_COMMANDS_SAMPLES = 22, + SND_FORMAT_ADPCM_G721 = 23, + SND_FORMAT_ADPCM_G722 = 24, + SND_FORMAT_ADPCM_G723_3 = 25, + SND_FORMAT_ADPCM_G723_5 = 26, + SND_FORMAT_ALAW_8 = 27, +}; + +#define SND_RATE_8012 8012.821 // CODEC input +#define SND_RATE_22050 22050.0 // low sampling rate output +#define SND_RATE_44100 44100.0 // high sampling rate output + +#define SND_MAGIC 0x2e736e64 // '.snd' + +#endif diff --git a/src/add-ons/media/plugins/au_reader/au_reader.cpp b/src/add-ons/media/plugins/au_reader/au_reader.cpp new file mode 100644 index 0000000000..f1dc710da3 --- /dev/null +++ b/src/add-ons/media/plugins/au_reader/au_reader.cpp @@ -0,0 +1,348 @@ +#include +#include +#include +#include +#include +#include +#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(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; +} diff --git a/src/add-ons/media/plugins/au_reader/au_reader.h b/src/add-ons/media/plugins/au_reader/au_reader.h new file mode 100644 index 0000000000..ac6c259694 --- /dev/null +++ b/src/add-ons/media/plugins/au_reader/au_reader.h @@ -0,0 +1,64 @@ +#include "ReaderPlugin.h" +#include "au.h" + +class auReader : public Reader +{ +public: + auReader(); + ~auReader(); + + const char *Copyright(); + + status_t Sniff(int32 *streamCount); + + void GetFileFormatInfo(media_file_format *mff); + + status_t AllocateCookie(int32 streamNumber, void **cookie); + status_t FreeCookie(void *cookie); + + status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, + media_format *format, void **infoBuffer, int32 *infoSize); + + status_t Seek(void *cookie, + uint32 seekTo, + int64 *frame, bigtime_t *time); + + status_t GetNextChunk(void *cookie, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + + BPositionIO *Source() { return fSource; } + +private: + BPositionIO * fSource; + + media_format fFormat; + int64 fDataStart; + int64 fDataSize; + + int64 fFrameCount; + bigtime_t fDuration; + + bool fRaw; + + int64 fPosition; + + int fChannelCount; + int32 fFrameRate; + int fBitsPerSample; + int fBitsPerFrame; + int fBlockAlign; + uint32 fFormatCode; + + void * fBuffer; + int32 fBufferSize; +}; + + +class auReaderPlugin : public ReaderPlugin +{ +public: + Reader *NewReader(); +}; + +MediaPlugin *instantiate_plugin();