root/src/apps/mediaplayer/media_node_framework/audio/AudioChannelConverter.cpp
/*
 * Copyright 2008 Stephan Aßmus <superstippi@gmx.de>
 * All rights reserved. Distributed under the terms of the MIT licensce.
 */


#include "AudioChannelConverter.h"

#include <new>
#include <stdio.h>
#include <string.h>

using std::nothrow;


//#define TRACE_AUDIO_CONVERTER
#ifdef TRACE_AUDIO_CONVERTER
#       define TRACE(x...)      printf(x)
#else
#       define TRACE(x...)
#endif


template<typename Type, typename BigType>
static void
convert(Type* inBuffer, Type* outBuffer, int32 inChannels, int32 outChannels,
        int32 frames)
{
        // TODO: more conversions!
        switch (inChannels) {
                case 0:
                        break;
                case 1:
                        switch (outChannels) {
                                case 2:
                                        for (int32 i = 0; i < frames; i++) {
                                                *outBuffer++ = *inBuffer;
                                                *outBuffer++ = *inBuffer++;
                                        }
                                        break;
                        }
                        break;
                case 2:
                        switch (outChannels) {
                                case 1:
                                        for (int32 i = 0; i < frames; i++) {
                                                *outBuffer++
                                                        = (Type)((BigType)inBuffer[0] + inBuffer[1]) / 2;
                                                inBuffer += 2;
                                        }
                                        break;
                        }
                        break;
                default:
                        switch (outChannels) {
                                case 2:
                                        for (int32 i = 0; i < frames; i++) {
                                                outBuffer[0] = inBuffer[0];
                                                outBuffer[1] = inBuffer[1];
                                                inBuffer += outChannels;
                                                outBuffer += 2;
                                        }
                                        break;
                        }
                        break;
        }
}


// #pragma mark -


AudioChannelConverter::AudioChannelConverter(AudioReader* source,
                const media_format& format)
        : AudioReader(format),
          fSource(source)
{
        // TODO: check the format and make sure everything matches
        // except for channel count
}


AudioChannelConverter::~AudioChannelConverter()
{
}


bigtime_t
AudioChannelConverter::InitialLatency() const
{
        return fSource->InitialLatency();
}


status_t
AudioChannelConverter::Read(void* outBuffer, int64 pos, int64 frames)
{
        TRACE("AudioChannelConverter::Read(%p, %lld, %lld)\n", outBuffer, pos, frames);
        status_t error = InitCheck();
        if (error != B_OK)
                return error;
        pos += fOutOffset;

        int32 inChannels = fSource->Format().u.raw_audio.channel_count;
        int32 outChannels = fFormat.u.raw_audio.channel_count;
        TRACE("   convert %ld -> %ld channels\n", inChannels, outChannels);

        int32 inSampleSize = fSource->Format().u.raw_audio.format
                & media_raw_audio_format::B_AUDIO_SIZE_MASK;
        int32 inFrameSize = inSampleSize * inChannels;
        uint8* inBuffer = new (nothrow) uint8[inFrameSize * frames];

        TRACE("   fSource->Read()\n");
        status_t ret = fSource->Read(inBuffer, pos, frames);
        if (ret != B_OK) {
                delete[] inBuffer;
                return ret;
        }

        // We know that both formats are the same except for channel count
        switch (fFormat.u.raw_audio.format) {
                case media_raw_audio_format::B_AUDIO_FLOAT:
                        convert<float, float>((float*)inBuffer, (float*)outBuffer,
                                inChannels, outChannels, frames);
                        break;
                case media_raw_audio_format::B_AUDIO_INT:
                        convert<int32, int64>((int32*)inBuffer, (int32*)outBuffer,
                                inChannels, outChannels, frames);
                        break;
                case media_raw_audio_format::B_AUDIO_SHORT:
                        convert<int16, int32>((int16*)inBuffer, (int16*)outBuffer,
                                inChannels, outChannels, frames);
                        break;
                case media_raw_audio_format::B_AUDIO_UCHAR:
                        convert<uint8, uint16>((uint8*)inBuffer, (uint8*)outBuffer,
                                inChannels, outChannels, frames);
                        break;
                case media_raw_audio_format::B_AUDIO_CHAR:
                        convert<int8, int16>((int8*)inBuffer, (int8*)outBuffer,
                                inChannels, outChannels, frames);
                        break;
        }

        delete[] inBuffer;

        TRACE("AudioChannelConverter::Read() done: %s\n", strerror(ret));
        return ret;
}


status_t
AudioChannelConverter::InitCheck() const
{
        status_t error = AudioReader::InitCheck();
        if (error == B_OK && !fSource)
                error = B_NO_INIT;
        return error;
}


AudioReader*
AudioChannelConverter::Source() const
{
        return fSource;
}