root/src/kits/media/Sound.cpp
/*
 * Copyright 2009-2019, Haiku Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Jacob Secunda
 *              Marcus Overhagen
 *              Michael Lotz <mmlr@mlotz.ch>
 */

#include <Sound.h>

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

#include <File.h>
#include <MediaDebug.h>

#include "TrackReader.h"


BSound::BSound(void* data, size_t size, const media_raw_audio_format& format,
        bool freeWhenDone)
        :       fData(data),
                fDataSize(size),
                fFile(NULL),
                fRefCount(1),
                fStatus(B_NO_INIT),
                fFormat(format),
                fFreeWhenDone(freeWhenDone),
                fTrackReader(NULL)
{
        if (fData == NULL)
                return;

        fStatus = B_OK;
}


BSound::BSound(const entry_ref* soundFile, bool loadIntoMemory)
        :       fData(NULL),
                fDataSize(0),
                fFile(new(std::nothrow) BFile(soundFile, B_READ_ONLY)),
                fRefCount(1),
                fStatus(B_NO_INIT),
                fFreeWhenDone(false),
                fTrackReader(NULL)
{
        if (fFile == NULL) {
                fStatus = B_NO_MEMORY;
                return;
        }

        fStatus = fFile->InitCheck();
        if (fStatus != B_OK)
                return;

        memset(&fFormat, 0, sizeof(fFormat));
        fTrackReader = new(std::nothrow) BPrivate::BTrackReader(fFile, fFormat);
        if (fTrackReader == NULL) {
                fStatus = B_NO_MEMORY;
                return;
        }

        fStatus = fTrackReader->InitCheck();
        if (fStatus != B_OK)
                return;

        fFormat = fTrackReader->Format();
        fStatus = B_OK;
}


BSound::BSound(const media_raw_audio_format& format)
        :       fData(NULL),
                fDataSize(0),
                fFile(NULL),
                fRefCount(1),
                fStatus(B_ERROR),
                fFormat(format),
                fFreeWhenDone(false),
                fTrackReader(NULL)
{
        // unimplemented protected constructor
        UNIMPLEMENTED();
}


BSound::~BSound()
{
        delete fTrackReader;
        delete fFile;

        if (fFreeWhenDone)
                free(fData);
}


status_t
BSound::InitCheck()
{
        return fStatus;
}


BSound*
BSound::AcquireRef()
{
        atomic_add(&fRefCount, 1);
        return this;
}


bool
BSound::ReleaseRef()
{
        if (atomic_add(&fRefCount, -1) == 1) {
                delete this;
                return false;
        }

        // TODO: verify those returns
        return true;
}


int32
BSound::RefCount() const
{
        return fRefCount;
}


bigtime_t
BSound::Duration() const
{
        float frameRate = fFormat.frame_rate;

        if (frameRate == 0.0)
                return 0;

        uint32 bytesPerSample = fFormat.format &
                media_raw_audio_format::B_AUDIO_SIZE_MASK;
        int64 frameCount = Size() / (fFormat.channel_count * bytesPerSample);

        return (bigtime_t)ceil((1000000LL * frameCount) / frameRate);
}


const media_raw_audio_format&
BSound::Format() const
{
        return fFormat;
}


const void*
BSound::Data() const
{
        return fData;
}


off_t
BSound::Size() const
{
        if (fFile != NULL) {
                off_t result = 0;
                fFile->GetSize(&result);
                return result;
        }

        return fDataSize;
}


bool
BSound::GetDataAt(off_t offset, void* intoBuffer, size_t bufferSize,
        size_t* outUsed)
{
        if (intoBuffer == NULL)
                return false;

        if (fData != NULL) {
                size_t copySize = MIN(bufferSize, fDataSize - offset);
                memcpy(intoBuffer, (uint8*)fData + offset, copySize);
                if (outUsed != NULL)
                        *outUsed = copySize;
                return true;
        }

        if (fTrackReader != NULL) {
                int32 frameSize = fTrackReader->FrameSize();
                int64 frameCount = fTrackReader->CountFrames();
                int64 startFrame = offset / frameSize;
                if (startFrame > frameCount)
                        return false;

                if (fTrackReader->SeekToFrame(&startFrame) != B_OK)
                        return false;

                off_t bufferOffset = offset - startFrame * frameSize;
                int64 directStartFrame = (offset + frameSize - 1) / frameSize;
                int64 directFrameCount = (offset + bufferSize - directStartFrame
                        * frameSize) / frameSize;

                if (bufferOffset != 0) {
                        int64 indirectFrameCount = directStartFrame - startFrame;
                        size_t indirectSize = indirectFrameCount * frameSize;
                        void* buffer = malloc(indirectSize);
                        if (buffer == NULL)
                                return false;

                        if (fTrackReader->ReadFrames(buffer, indirectFrameCount) != B_OK) {
                                free(buffer);
                                return false;
                        }

                        memcpy(intoBuffer, (uint8*)buffer + bufferOffset,
                                indirectSize - bufferOffset);
                        if (outUsed != NULL)
                                *outUsed = indirectSize - bufferOffset;

                        free(buffer);
                } else if (outUsed != NULL)
                        *outUsed = 0;

                if (fTrackReader->ReadFrames((uint8*)intoBuffer + bufferOffset,
                        directFrameCount) != B_OK)
                        return false;

                if (outUsed != NULL)
                        *outUsed += directFrameCount * frameSize;

                return true;
        }

        return false;
}


status_t
BSound::BindTo(BSoundPlayer* player, const media_raw_audio_format& format)
{
        UNIMPLEMENTED();
        return B_ERROR;
}


status_t
BSound::UnbindFrom(BSoundPlayer* player)
{
        UNIMPLEMENTED();
        return B_ERROR;
}


status_t
BSound::Perform(int32 code, ...)
{
        UNIMPLEMENTED();
        return B_ERROR;
}


status_t BSound::_Reserved_Sound_0(void*) { return B_ERROR; }
status_t BSound::_Reserved_Sound_1(void*) { return B_ERROR; }
status_t BSound::_Reserved_Sound_2(void*) { return B_ERROR; }
status_t BSound::_Reserved_Sound_3(void*) { return B_ERROR; }
status_t BSound::_Reserved_Sound_4(void*) { return B_ERROR; }
status_t BSound::_Reserved_Sound_5(void*) { return B_ERROR; }