* Copyright 2001-2012 Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christopher ML Zumwalt May (zummy@users.sf.net)
* Jérôme Duval
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Entry.h>
#include <FileGameSound.h>
#include <MediaFile.h>
#include <MediaTrack.h>
#include <scheduler.h>
#include "GameSoundDevice.h"
#include "GSUtility.h"
const int32 kPages = 20;
struct _gs_media_tracker
{
BMediaFile* file;
BMediaTrack* stream;
int64 frames;
size_t position;
};
bool
FillBuffer(_gs_ramp* ramp, uint8* data, uint8* buffer, size_t* bytes)
{
int32 samples = *bytes / sizeof(uint8);
for (int32 byte = 0; byte < samples; byte++) {
float gain = *ramp->value;
data[byte] = uint8(float(buffer[byte]) * gain);
if (ChangeRamp(ramp)) {
*bytes = byte * sizeof(uint8);
return true;
}
}
return false;
}
bool
FillBuffer(_gs_ramp* ramp, int16* data, int16* buffer, size_t* bytes)
{
int32 samples = *bytes / sizeof(int16);
for (int32 byte = 0; byte < samples; byte++) {
float gain = *ramp->value;
data[byte] = int16(float(buffer[byte]) * gain);
if (ChangeRamp(ramp)) {
*bytes = byte * sizeof(int16);
return true;
}
}
return false;
}
bool
FillBuffer(_gs_ramp* ramp, int32* data, int32* buffer, size_t* bytes)
{
size_t byte = 0;
bool bytesAreReady = (*bytes > 0);
while (bytesAreReady) {
float gain = *ramp->value;
data[byte] = int32(float(buffer[byte]) * gain);
if (ChangeRamp(ramp)) {
*bytes = byte;
return true;
}
byte++;
bytesAreReady = (byte >= *bytes);
}
return false;
}
bool
FillBuffer(_gs_ramp* ramp, float* data, float* buffer, size_t* bytes)
{
size_t byte = 0;
bool bytesAreReady = (*bytes > 0);
while (bytesAreReady) {
float gain = *ramp->value;
data[byte] = buffer[byte] * gain;
if (ChangeRamp(ramp)) {
*bytes = byte;
return true;
}
byte++;
bytesAreReady = (byte >= *bytes);
}
return false;
}
BFileGameSound::BFileGameSound(const entry_ref* file, bool looping,
BGameSoundDevice* device)
:
BStreamingGameSound(device),
fAudioStream(NULL),
fStopping(false),
fLooping(looping),
fBuffer(NULL),
fPlayPosition(0),
fPausing(NULL),
fPaused(false),
fPauseGain(1.0)
{
if (InitCheck() == B_OK)
SetInitError(Init(file));
}
BFileGameSound::BFileGameSound(const char* file, bool looping,
BGameSoundDevice* device)
:
BStreamingGameSound(device),
fAudioStream(NULL),
fStopping(false),
fLooping(looping),
fBuffer(NULL),
fPlayPosition(0),
fPausing(NULL),
fPaused(false),
fPauseGain(1.0)
{
if (InitCheck() == B_OK) {
entry_ref node;
if (get_ref_for_path(file, &node) != B_OK)
SetInitError(B_ENTRY_NOT_FOUND);
else
SetInitError(Init(&node));
}
}
BFileGameSound::~BFileGameSound()
{
if (fReadThread >= 0) {
kill_thread(fReadThread);
}
if (fAudioStream) {
if (fAudioStream->stream)
fAudioStream->file->ReleaseTrack(fAudioStream->stream);
delete fAudioStream->file;
}
delete [] fBuffer;
delete fAudioStream;
}
BGameSound*
BFileGameSound::Clone() const
{
return NULL;
}
status_t
BFileGameSound::StartPlaying()
{
if (IsPlaying())
StopPlaying();
return BStreamingGameSound::StartPlaying();
}
status_t
BFileGameSound::StopPlaying()
{
status_t error = BStreamingGameSound::StopPlaying();
if (!fAudioStream || !fAudioStream->stream)
return B_OK;
int64 frame = 0;
fAudioStream->stream->SeekToFrame(&frame);
fStopping = false;
fAudioStream->position = 0;
fPlayPosition = 0;
return error;
}
status_t
BFileGameSound::Preload()
{
if (!IsPlaying())
Load();
return B_OK;
}
void
BFileGameSound::FillBuffer(void* inBuffer, size_t inByteCount)
{
size_t out_offset = 0;
while (inByteCount > 0 && !fPaused) {
if (!fPaused || fPausing) {
printf("mixout %ld, inByteCount %ld, decin %ld, BufferSize %ld\n",
out_offset, inByteCount, fPlayPosition, fBufferSize);
if (fPlayPosition == 0 || fPlayPosition >= fBufferSize) {
Load();
}
if (fPausing) {
Lock();
bool rampDone = false;
size_t bytes = fBufferSize - fPlayPosition;
if (bytes > inByteCount) {
bytes = inByteCount;
}
char* buffer = (char*)inBuffer;
switch(Format().format) {
case gs_audio_format::B_GS_U8:
rampDone = ::FillBuffer(fPausing,
(uint8*)&buffer[out_offset],
(uint8*)&fBuffer[fPlayPosition], &bytes);
break;
case gs_audio_format::B_GS_S16:
rampDone = ::FillBuffer(fPausing,
(int16*)&buffer[out_offset],
(int16*)&fBuffer[fPlayPosition], &bytes);
break;
case gs_audio_format::B_GS_S32:
rampDone = ::FillBuffer(fPausing,
(int32*)&buffer[out_offset],
(int32*)&fBuffer[fPlayPosition], &bytes);
break;
case gs_audio_format::B_GS_F:
rampDone = ::FillBuffer(fPausing,
(float*)&buffer[out_offset],
(float*)&fBuffer[fPlayPosition], &bytes);
break;
}
inByteCount -= bytes;
out_offset += bytes;
fPlayPosition += bytes;
if (rampDone) {
while (fPlayPosition < fBufferSize && (inByteCount > 0)) {
buffer[out_offset++] = fBuffer[fPlayPosition++];
inByteCount--;
}
delete fPausing;
fPausing = NULL;
}
Unlock();
} else {
char* buffer = (char*)inBuffer;
while (fPlayPosition < fBufferSize && (!fPaused || fPausing)
&& (inByteCount > 0)) {
buffer[out_offset++] = fBuffer[fPlayPosition++];
inByteCount--;
}
}
}
}
}
status_t
BFileGameSound::Perform(int32 selector, void* data)
{
return B_ERROR;
}
status_t
BFileGameSound::SetPaused(bool isPaused, bigtime_t rampTime)
{
if (fPaused == isPaused)
return EALREADY;
Lock();
delete fPausing;
fPausing = NULL;
if (rampTime > 100000) {
if (isPaused) {
fPausing = InitRamp(&fPauseGain, 0.0,
Format().frame_rate, rampTime);
} else {
fPausing = InitRamp(&fPauseGain, 1.0,
Format().frame_rate, rampTime);
}
}
fPaused = isPaused;
Unlock();
return B_OK;
}
int32
BFileGameSound::IsPaused()
{
if (fPausing)
return B_PAUSE_IN_PROGRESS;
if (fPaused)
return B_PAUSED;
return B_NOT_PAUSED;
}
status_t
BFileGameSound::Init(const entry_ref* file)
{
fAudioStream = new(std::nothrow) _gs_media_tracker;
if (!fAudioStream)
return B_NO_MEMORY;
memset(fAudioStream, 0, sizeof(_gs_media_tracker));
fAudioStream->file = new(std::nothrow) BMediaFile(file);
if (!fAudioStream->file) {
delete fAudioStream;
fAudioStream = NULL;
return B_NO_MEMORY;
}
status_t error = fAudioStream->file->InitCheck();
if (error != B_OK)
return error;
fAudioStream->stream = fAudioStream->file->TrackAt(0);
media_format playFormat;
if ((error = fAudioStream->stream->EncodedFormat(&playFormat)) != B_OK) {
fAudioStream->file->ReleaseTrack(fAudioStream->stream);
fAudioStream->stream = NULL;
return error;
}
if (!playFormat.IsAudio()) {
fAudioStream->file->ReleaseTrack(fAudioStream->stream);
fAudioStream->stream = NULL;
return B_MEDIA_BAD_FORMAT;
}
gs_audio_format dformat = Device()->Format();
memset(&playFormat, 0, sizeof(media_format));
playFormat.type = B_MEDIA_RAW_AUDIO;
if (fAudioStream->stream->DecodedFormat(&playFormat) != B_OK) {
fAudioStream->file->ReleaseTrack(fAudioStream->stream);
fAudioStream->stream = NULL;
return B_MEDIA_BAD_FORMAT;
}
gs_audio_format gsformat;
media_to_gs_format(&gsformat, &playFormat.u.raw_audio);
fBufferSize = gsformat.buffer_size;
if (fBufferSize < dformat.buffer_size)
fBufferSize = dformat.buffer_size;
fBuffer = new char[fBufferSize * 2];
memset(fBuffer, 0, fBufferSize * 2);
fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format);
fAudioStream->frames = fAudioStream->stream->CountFrames();
gs_id sound;
error = Device()->CreateBuffer(&sound, this, &gsformat);
if (error != B_OK)
return error;
return BGameSound::Init(sound);
}
bool
BFileGameSound::Load()
{
if (!fAudioStream || !fAudioStream->stream)
return false;
int64 frames = 0;
fAudioStream->stream->ReadFrames(fBuffer, &frames);
fBufferSize = frames * fFrameSize;
fPlayPosition = 0;
if (fBufferSize <= 0) {
if (fLooping) {
int64 frame = 0;
fAudioStream->stream->SeekToFrame(&frame);
} else {
StopPlaying();
}
}
return true;
}
bool
BFileGameSound::Read(void* buffer, size_t bytes)
{
return false;
}
*
* BFileGameSound::BFileGameSound()
* BFileGameSound::BFileGameSound(const BFileGameSound &)
* BFileGameSound &BFileGameSound::operator=(const BFileGameSound &)
*/
status_t
BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...)
{
return B_ERROR;
}
status_t
BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...)
{
return B_ERROR;
}