* Copyright 2000-2006 Ingo Weinhold <ingo_weinhold@gmx.de>
* Copyright 2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include "AudioFormatConverter.h"
#include <ByteOrder.h>
#include <MediaDefs.h>
#ifdef TRACE_AUDIO_CONVERTER
# include <stdio.h>
# define TRACE(x...) printf(x)
#else
# define TRACE(x...)
#endif
struct ReadFloat {
inline int operator()(const void* buffer) const {
float b = *(float*)buffer;
if (b < -1.0f)
b = -1.0f;
else if (b > 1.0f)
b = 1.0f;
return (int)((double)b * (double)0x7fffffff);
}
};
struct ReadInt {
inline int operator()(const void* buffer) const {
int b = *(int*)buffer;
if (b == INT_MIN)
b++;
return b;
}
};
struct ReadShort {
inline int operator()(const void* buffer) const {
short b = *(short*)buffer;
if (b == -32768)
b++;
return int(int64(b) * 0x7fffffff / 32767);
}
};
struct ReadUChar {
inline int operator()(const void* buffer) const {
uchar b = *(uchar*)buffer;
if (b == 0)
b++;
return int((int64(b) - 0x80) * 0x7fffffff / 127);
}
};
struct ReadChar {
inline int operator()(const void* buffer) const {
char b = *(char*)buffer;
if (b == 0)
b++;
return int(int64(b) * 0x7fffffff / 127);
}
};
struct WriteFloat {
inline void operator()(void* buffer, int value) const {
*(float*)buffer = (double)value / (double)0x7fffffff;
}
};
struct WriteInt {
inline void operator()(void* buffer, int value) const {
*(int*)buffer = value;
}
};
struct WriteShort {
inline void operator()(void* buffer, int value) const {
*(short*)buffer = (short)(value / (int)0x10000);
}
};
struct WriteUChar {
inline void operator()(void* buffer, int value) const {
*(uchar*)buffer = (uchar)(value / (int)0x1000000 + 128);
}
};
struct WriteChar {
inline void operator()(void* buffer, int value) const {
*(char*)buffer = (char)(value / (int)0x1000000);
}
};
template<typename ReadT, typename WriteT>
static void
convert(const ReadT& read, const WriteT& write,
const char* inBuffer, char* outBuffer, int32 frames,
int32 inSampleSize, int32 outSampleSize, int32 channelCount)
{
for (int32 i = 0; i < frames; i++) {
for (int32 c = 0; c < channelCount; c++) {
write(outBuffer, read(inBuffer));
inBuffer += inSampleSize;
outBuffer += outSampleSize;
}
}
}
static void
swap_sample_byte_order(void* buffer, uint32 format, size_t length)
{
type_code type = B_ANY_TYPE;
switch (format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
type = B_FLOAT_TYPE;
break;
case media_raw_audio_format::B_AUDIO_INT:
type = B_INT32_TYPE;
break;
case media_raw_audio_format::B_AUDIO_SHORT:
type = B_INT16_TYPE;
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
break;
case media_raw_audio_format::B_AUDIO_CHAR:
break;
}
if (type != B_ANY_TYPE)
swap_data(type, buffer, length, B_SWAP_ALWAYS);
}
AudioFormatConverter::AudioFormatConverter(AudioReader* source, uint32 format,
uint32 byteOrder)
:
AudioReader(),
fSource(NULL)
{
uint32 hostByteOrder
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
if (source && source->Format().type == B_MEDIA_RAW_AUDIO
&& source->Format().u.raw_audio.byte_order == hostByteOrder) {
fFormat = source->Format();
fFormat.u.raw_audio.format = format;
fFormat.u.raw_audio.byte_order = byteOrder;
int32 inSampleSize = source->Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 outSampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
if (inSampleSize != outSampleSize) {
fFormat.u.raw_audio.buffer_size
= source->Format().u.raw_audio.buffer_size * outSampleSize
/ inSampleSize;
}
} else
source = NULL;
fSource = source;
}
AudioFormatConverter::~AudioFormatConverter()
{
}
bigtime_t
AudioFormatConverter::InitialLatency() const
{
return fSource->InitialLatency();
}
status_t
AudioFormatConverter::Read(void* buffer, int64 pos, int64 frames)
{
TRACE("AudioFormatConverter::Read(%p, %lld, %lld)\n", buffer, pos, frames);
status_t error = InitCheck();
if (error != B_OK) {
TRACE("AudioFormatConverter::Read() done 1\n");
return error;
}
pos += fOutOffset;
if (fFormat.u.raw_audio.format == fSource->Format().u.raw_audio.format
&& fFormat.u.raw_audio.byte_order
== fSource->Format().u.raw_audio.byte_order) {
TRACE("AudioFormatConverter::Read() done 2\n");
return fSource->Read(buffer, pos, frames);
}
int32 inSampleSize = fSource->Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 outSampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 channelCount = fFormat.u.raw_audio.channel_count;
int32 inFrameSize = inSampleSize * channelCount;
int32 outFrameSize = outSampleSize * channelCount;
char* reformatBuffer = NULL;
char* inBuffer = (char*)buffer;
#ifdef TRACE_AUDIO_CONVERTER
char formatString[256];
string_for_format(fSource->Format(), formatString, 256);
TRACE(" source format: %s\n", formatString);
TRACE(" in format : format: %lx, sample size: %ld, channels: %ld, "
"byte order: %lu\n", fSource->Format().u.raw_audio.format,
inSampleSize, channelCount,
fSource->Format().u.raw_audio.byte_order);
TRACE(" out format: format: %lx, sample size: %ld, channels: %ld, "
"byte order: %lu\n", fFormat.u.raw_audio.format, outSampleSize,
channelCount, fFormat.u.raw_audio.byte_order);
#endif
if (inSampleSize != outSampleSize) {
reformatBuffer = new char[frames * inFrameSize];
inBuffer = reformatBuffer;
}
error = fSource->Read(inBuffer, pos, frames);
uint32 hostByteOrder
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
if (fSource->Format().u.raw_audio.byte_order != hostByteOrder) {
swap_sample_byte_order(inBuffer, fSource->Format().u.raw_audio.format,
frames * inFrameSize);
}
switch (fSource->Format().u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadFloat(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadFloat(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadFloat(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadFloat(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
case media_raw_audio_format::B_AUDIO_INT:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadInt(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadInt(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadInt(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadInt(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
case media_raw_audio_format::B_AUDIO_SHORT:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadShort(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadShort(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadShort(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadShort(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadUChar(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadUChar(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadUChar(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadUChar(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
case media_raw_audio_format::B_AUDIO_CHAR:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadChar(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadChar(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadChar(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadChar(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
break;
}
break;
}
if (fFormat.u.raw_audio.byte_order != hostByteOrder) {
swap_sample_byte_order(buffer, fFormat.u.raw_audio.format,
frames * outFrameSize);
}
delete[] reformatBuffer;
TRACE("AudioFormatConverter::Read() done\n");
return B_OK;
}
status_t
AudioFormatConverter::InitCheck() const
{
status_t error = AudioReader::InitCheck();
if (error == B_OK && !fSource)
error = B_NO_INIT;
if (error == B_OK)
error = fSource->InitCheck();
return error;
}
AudioReader*
AudioFormatConverter::Source() const
{
return fSource;
}