/*
 * Copyright 2002-2009, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Marcus Overhagen
 *		Jérôme Duval
 */


#include <SoundPlayer.h>

#include <math.h>
#include <string.h>

#include <Autolock.h>
#if 0  // not HoG_GENODE
#include <MediaRoster.h>
#include <ParameterWeb.h>
#include <Sound.h>
#include <TimeSource.h>

#include "SoundPlayNode.h"

#else  // HoG_GENODE:

// genode/base.a
#include <audio_out_session/connection.h>

// haiku.lib.so
#include <MediaNode.h>
#include <MediaFile.h>

#include "debug.h"
#define ENTER CALLED
//static int debug = 0;
#endif  // ~HoG_GENODE



// Flags used internally in BSoundPlayer
enum {
	F_NODES_CONNECTED	= (1 << 0),
	F_HAS_DATA			= (1 << 1),
	F_IS_STARTED		= (1 << 2),
	F_MUST_RELEASE_MIXER = (1 << 3),
};


#if 0  // HoG_GENODE
static BSoundPlayer::play_id sCurrentPlayID = 1;


BSoundPlayer::BSoundPlayer(const char* name, BufferPlayerFunc playerFunction,
	EventNotifierFunc eventNotifierFunction, void* cookie)
{
	CALLED();

	TRACE("BSoundPlayer::BSoundPlayer: default constructor used\n");

	media_multi_audio_format format = media_multi_audio_format::wildcard;

	_Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction,
		cookie);
}
#endif  // HoG_GENODE


BSoundPlayer::BSoundPlayer(const media_raw_audio_format* _format,
	const char* name, BufferPlayerFunc playerFunction,
	EventNotifierFunc eventNotifierFunction, void* cookie)
#if defined( HoG_GENODE )
:	mediaFile( NULL )
	,playerNode( NULL )
	,hasData( false )
	,fVolumeDB( 0. )
	,fInitStatus( 0 )
	,fLastVolumeUpdate( 0 )
#endif
{
	CALLED();

	TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used\n");

	media_multi_audio_format format = media_multi_audio_format::wildcard;
	*(media_raw_audio_format*)&format = *_format;

#if DEBUG > 0
	char buf[100];
	media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = format;
	string_for_format(tmp, buf, sizeof(buf));
	TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
#endif

	_Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction,
		cookie);
}


BSoundPlayer::BSoundPlayer(const media_node& toNode,
	const media_multi_audio_format* format, const char* name,
	const media_input* input, BufferPlayerFunc playerFunction,
	EventNotifierFunc eventNotifierFunction, void* cookie)
#if defined( HoG_GENODE )
:	mediaFile( NULL )
	,playerNode( NULL )
	,hasData( false )
	,fVolumeDB( 0. )
	,fInitStatus( 0 )
	,fLastVolumeUpdate( 0 )
#endif
{
	CALLED();

	TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used\n");

	if ((toNode.kind & B_BUFFER_CONSUMER) == 0)
		debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n");

#if DEBUG > 0
	char buf[100];
	media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = *format;
	string_for_format(tmp, buf, sizeof(buf));
	TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
#endif

	_Init(&toNode, format, name, input, playerFunction, eventNotifierFunction,
		cookie);
}


#if 0  // HoG_GENODE
BSoundPlayer::~BSoundPlayer()
{
	CALLED();

	if ((fFlags & F_IS_STARTED) != 0) {
		// block, but don't flush
		Stop(true, false);
	}

	status_t err;
	BMediaRoster* roster = BMediaRoster::Roster();
	if (roster == NULL) {
		TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n");
		goto cleanup;
	}

	if ((fFlags & F_NODES_CONNECTED) != 0) {
		// Ordinarily we'd stop *all* of the nodes in the chain before
		// disconnecting. However, our node is already stopped, and we can't
		// stop the System Mixer.
		// So, we just disconnect from it, and release our references to the
		// nodes that we're using. We *are* supposed to do that even for global
		// nodes like the Mixer.
		err = roster->Disconnect(fMediaOutput, fMediaInput);
		if (err != B_OK) {
			TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: "
				"%" B_PRId32 " (%s)\n", err, strerror(err));
		}
	}

	if ((fFlags & F_MUST_RELEASE_MIXER) != 0) {
		// Release the mixer as it was acquired
		// through BMediaRoster::GetAudioMixer()
		err = roster->ReleaseNode(fMediaInput.node);
		if (err != B_OK) {
			TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: "
				"%" B_PRId32 " (%s)\n", err, strerror(err));
		}
	}

cleanup:
	// Dispose of the player node

	// We do not call BMediaRoster::ReleaseNode(), since
	// the player was created by using "new". We could
	// call BMediaRoster::UnregisterNode(), but this is
	// supposed to be done by BMediaNode destructor automatically.

	// The node is deleted by the Release() when ref count reach 0.
	// Since we are the sole owners, and no one acquired it
	// this should be the case. The Quit() synchronization
	// is handled by the DeleteHook inheritance.
	// NOTE: this might be crucial when using a BMediaEventLooper.
	if (fPlayerNode->Release() != NULL) {
		TRACE("BSoundPlayer::~BSoundPlayer: Error the producer node "
			"appears to be acquired by someone else than us!");
	}

	// do not delete fVolumeSlider, it belongs to the parameter web
	delete fParameterWeb;
}


status_t
BSoundPlayer::InitCheck()
{
	CALLED();
	return fInitStatus;
}


media_raw_audio_format
BSoundPlayer::Format() const
{
	CALLED();

	if ((fFlags & F_NODES_CONNECTED) == 0)
		return media_raw_audio_format::wildcard;

	return fPlayerNode->Format();
}


status_t
BSoundPlayer::Start()
{
	CALLED();

	if ((fFlags & F_NODES_CONNECTED) == 0)
		return B_NO_INIT;

	if ((fFlags & F_IS_STARTED) != 0)
		return B_OK;

	BMediaRoster* roster = BMediaRoster::Roster();
	if (!roster) {
		TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
		return B_ERROR;
	}

	if (!fPlayerNode->TimeSource()->IsRunning()) {
		roster->StartTimeSource(fPlayerNode->TimeSource()->Node(),
			fPlayerNode->TimeSource()->RealTime());
	}

	// Add latency and a few ms to the nodes current time to
	// make sure that we give the producer enough time to run
	// buffers through the node chain, otherwise it'll start
	// up already late

	status_t err = roster->StartNode(fPlayerNode->Node(),
		fPlayerNode->TimeSource()->Now() + Latency() + 5000);
	if (err != B_OK) {
		TRACE("BSoundPlayer::Start: StartNode failed, %" B_PRId32, err);
		return err;
	}

	if (fNotifierFunc != NULL)
		fNotifierFunc(fCookie, B_STARTED, this);

	SetHasData(true);
	atomic_or(&fFlags, F_IS_STARTED);

	return B_OK;
}


void
BSoundPlayer::Stop(bool block, bool flush)
{
	CALLED();

	TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);

	if ((fFlags & F_NODES_CONNECTED) == 0)
		return;

	// TODO: flush is ignored

	if ((fFlags & F_IS_STARTED) != 0) {
		BMediaRoster* roster = BMediaRoster::Roster();
		if (roster == NULL) {
			TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n");
			return;
		}

		roster->StopNode(fPlayerNode->Node(), 0, true);

		atomic_and(&fFlags, ~F_IS_STARTED);
	}

	if (block) {
		// wait until the node is stopped
		int tries;
		for (tries = 250; fPlayerNode->IsPlaying() && tries != 0; tries--)
			snooze(2000);

		DEBUG_ONLY(if (tries == 0)
			TRACE("BSoundPlayer::Stop: waiting for node stop failed\n"));

		// Wait until all buffers on the way to the physical output have been
		// played
		snooze(Latency() + 2000);
	}

	if (fNotifierFunc)
		fNotifierFunc(fCookie, B_STOPPED, this);

}


bigtime_t
BSoundPlayer::Latency()
{
	CALLED();

	if ((fFlags & F_NODES_CONNECTED) == 0)
		return 0;

	BMediaRoster *roster = BMediaRoster::Roster();
	if (!roster) {
		TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n");
		return 0;
	}

	bigtime_t latency;
	status_t err = roster->GetLatencyFor(fMediaOutput.node, &latency);
	if (err != B_OK) {
		TRACE("BSoundPlayer::Latency: GetLatencyFor failed %" B_PRId32
			" (%s)\n", err, strerror(err));
		return 0;
	}

	TRACE("BSoundPlayer::Latency: latency is %" B_PRId64 "\n", latency);

	return latency;
}


void
BSoundPlayer::SetHasData(bool hasData)
{
	CALLED();
	if (hasData)
		atomic_or(&fFlags, F_HAS_DATA);
	else
		atomic_and(&fFlags, ~F_HAS_DATA);
}


bool
BSoundPlayer::HasData()
{
	CALLED();
	return (atomic_get(&fFlags) & F_HAS_DATA) != 0;
}


BSoundPlayer::BufferPlayerFunc
BSoundPlayer::BufferPlayer() const
{
	CALLED();
	return fPlayBufferFunc;
}


void
BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction)
{
	CALLED();
	BAutolock _(fLocker);

	fPlayBufferFunc = playerFunction;
}


BSoundPlayer::EventNotifierFunc
BSoundPlayer::EventNotifier() const
{
	CALLED();
	return fNotifierFunc;
}


void
BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction)
{
	CALLED();
	BAutolock _(fLocker);

	fNotifierFunc = eventNotifierFunction;
}


void*
BSoundPlayer::Cookie() const
{
	CALLED();
	return fCookie;
}


void
BSoundPlayer::SetCookie(void *cookie)
{
	CALLED();
	BAutolock _(fLocker);

	fCookie = cookie;
}


void
BSoundPlayer::SetCallbacks(BufferPlayerFunc playerFunction,
	EventNotifierFunc eventNotifierFunction, void* cookie)
{
	CALLED();
	BAutolock _(fLocker);

	SetBufferPlayer(playerFunction);
	SetNotifier(eventNotifierFunction);
	SetCookie(cookie);
}


/*!	The BeBook is inaccurate about the meaning of this function.
	The probably best interpretation is to return the time that
	has elapsed since playing was started, whichs seems to match
	"CurrentTime() returns the current media time"
*/
bigtime_t
BSoundPlayer::CurrentTime()
{
	if ((fFlags & F_NODES_CONNECTED) == 0)
		return 0;

	return fPlayerNode->CurrentTime();
}


/*!	Returns the current performance time of the sound player node
	being used by the BSoundPlayer. Will return B_ERROR if the
	BSoundPlayer object hasn't been properly initialized.
*/
bigtime_t
BSoundPlayer::PerformanceTime()
{
	if ((fFlags & F_NODES_CONNECTED) == 0)
		return (bigtime_t) B_ERROR;

	return fPlayerNode->TimeSource()->Now();
}


status_t
BSoundPlayer::Preroll()
{
	CALLED();

	if ((fFlags & F_NODES_CONNECTED) == 0)
		return B_NO_INIT;

	BMediaRoster* roster = BMediaRoster::Roster();
	if (roster == NULL) {
		TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n");
		return B_ERROR;
	}

	status_t err = roster->PrerollNode(fMediaOutput.node);
	if (err != B_OK) {
		TRACE("BSoundPlayer::Preroll: Error while PrerollNode: %"
			B_PRId32 " (%s)\n", err, strerror(err));
		return err;
	}

	return B_OK;
}


BSoundPlayer::play_id
BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime)
{
	return StartPlaying(sound, atTime, 1.0);
}


BSoundPlayer::play_id
BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime, float withVolume)
{
	CALLED();

	// TODO: support the at_time and with_volume parameters
	playing_sound* item = (playing_sound*)malloc(sizeof(playing_sound));
	if (item == NULL)
		return B_NO_MEMORY;

	item->current_offset = 0;
	item->sound = sound;
	item->id = atomic_add(&sCurrentPlayID, 1);
	item->delta = 0;
	item->rate = 0;
	item->volume = withVolume;

	if (!fLocker.Lock()) {
		free(item);
		return B_ERROR;
	}

	sound->AcquireRef();
	item->next = fPlayingSounds;
	fPlayingSounds = item;
	fLocker.Unlock();

	SetHasData(true);
	return item->id;
}


status_t
BSoundPlayer::SetSoundVolume(play_id id, float newVolume)
{
	CALLED();
	if (!fLocker.Lock())
		return B_ERROR;

	playing_sound *item = fPlayingSounds;
	while (item) {
		if (item->id == id) {
			item->volume = newVolume;
			fLocker.Unlock();
			return B_OK;
		}

		item = item->next;
	}

	fLocker.Unlock();
	return B_ENTRY_NOT_FOUND;
}


bool
BSoundPlayer::IsPlaying(play_id id)
{
	CALLED();
	if (!fLocker.Lock())
		return B_ERROR;

	playing_sound *item = fPlayingSounds;
	while (item) {
		if (item->id == id) {
			fLocker.Unlock();
			return true;
		}

		item = item->next;
	}

	fLocker.Unlock();
	return false;
}


status_t
BSoundPlayer::StopPlaying(play_id id)
{
	CALLED();
	if (!fLocker.Lock())
		return B_ERROR;

	playing_sound** link = &fPlayingSounds;
	playing_sound* item = fPlayingSounds;

	while (item != NULL) {
		if (item->id == id) {
			*link = item->next;
			sem_id waitSem = item->wait_sem;
			item->sound->ReleaseRef();
			free(item);
			fLocker.Unlock();

			_NotifySoundDone(id, true);
			if (waitSem >= 0)
				release_sem(waitSem);

			return B_OK;
		}

		link = &item->next;
		item = item->next;
	}

	fLocker.Unlock();
	return B_ENTRY_NOT_FOUND;
}


status_t
BSoundPlayer::WaitForSound(play_id id)
{
	CALLED();
	if (!fLocker.Lock())
		return B_ERROR;

	playing_sound* item = fPlayingSounds;
	while (item != NULL) {
		if (item->id == id) {
			sem_id waitSem = item->wait_sem;
			if (waitSem < 0)
				waitSem = item->wait_sem = create_sem(0, "wait for sound");

			fLocker.Unlock();
			return acquire_sem(waitSem);
		}

		item = item->next;
	}

	fLocker.Unlock();
	return B_ENTRY_NOT_FOUND;
}


float
BSoundPlayer::Volume()
{
	CALLED();
	return pow(10.0, VolumeDB(true) / 20.0);
}


void
BSoundPlayer::SetVolume(float newVolume)
{
	CALLED();
	SetVolumeDB(20.0 * log10(newVolume));
}


float
BSoundPlayer::VolumeDB(bool forcePoll)
{
	CALLED();
	if (!fVolumeSlider)
		return -94.0f; // silence

	if (!forcePoll && system_time() - fLastVolumeUpdate < 500000)
		return fVolumeDB;

	int32 count = fVolumeSlider->CountChannels();
	float values[count];
	size_t size = count * sizeof(float);
	fVolumeSlider->GetValue(&values, &size, NULL);
	fLastVolumeUpdate = system_time();
	fVolumeDB = values[0];

	return values[0];
}


void
BSoundPlayer::SetVolumeDB(float volumeDB)
{
	CALLED();
	if (!fVolumeSlider)
		return;

	float minDB = fVolumeSlider->MinValue();
	float maxDB = fVolumeSlider->MaxValue();
	if (volumeDB < minDB)
		volumeDB = minDB;
	if (volumeDB > maxDB)
		volumeDB = maxDB;

	int count = fVolumeSlider->CountChannels();
	float values[count];
	for (int i = 0; i < count; i++)
		values[i] = volumeDB;
	fVolumeSlider->SetValue(values, sizeof(float) * count, 0);

	fVolumeDB = volumeDB;
	fLastVolumeUpdate = system_time();
}


status_t
BSoundPlayer::GetVolumeInfo(media_node* _node, int32* _parameterID,
	float* _minDB, float* _maxDB)
{
	CALLED();
	if (fVolumeSlider == NULL)
		return B_NO_INIT;

	if (_node != NULL)
		*_node = fMediaInput.node;
	if (_parameterID != NULL)
		*_parameterID = fVolumeSlider->ID();
	if (_minDB != NULL)
		*_minDB = fVolumeSlider->MinValue();
	if (_maxDB != NULL)
		*_maxDB = fVolumeSlider->MaxValue();

	return B_OK;
}


// #pragma mark - protected BSoundPlayer


void
BSoundPlayer::SetInitError(status_t error)
{
	CALLED();
	fInitStatus = error;
}


// #pragma mark - private BSoundPlayer


void
BSoundPlayer::_SoundPlayBufferFunc(void *cookie, void *buffer, size_t size,
	const media_raw_audio_format &format)
{
	// TODO: support more than one sound and make use of the format parameter
	BSoundPlayer *player = (BSoundPlayer *)cookie;
	if (!player->fLocker.Lock()) {
		memset(buffer, 0, size);
		return;
	}

	playing_sound *sound = player->fPlayingSounds;
	if (sound == NULL) {
		player->SetHasData(false);
		player->fLocker.Unlock();
		memset(buffer, 0, size);
		return;
	}

	size_t used = 0;
	if (!sound->sound->GetDataAt(sound->current_offset, buffer, size, &used)) {
		// will take care of removing the item and notifying others
		player->StopPlaying(sound->id);
		player->fLocker.Unlock();
		memset(buffer, 0, size);
		return;
	}

	sound->current_offset += used;
	player->fLocker.Unlock();

	if (used < size)
		memset((uint8 *)buffer + used, 0, size - used);
}


status_t BSoundPlayer::_Reserved_SoundPlayer_0(void*, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_1(void*, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_2(void*, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_3(void*, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_4(void*, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_5(void*, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_6(void*, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_7(void*, ...) { return B_ERROR; }

#endif  // HoG_GENODE


#if 0  // not-HoG_GENODE:

BSoundPlayer::_Init(const media_node* node,
	const media_multi_audio_format* format, const char* name,
	const media_input* input, BufferPlayerFunc playerFunction,
	EventNotifierFunc eventNotifierFunction, void* cookie)
{
	CALLED();
	fPlayingSounds = NULL;
	fWaitingSounds = NULL;

	fPlayerNode = NULL;
	if (playerFunction == NULL) {
		fPlayBufferFunc = _SoundPlayBufferFunc;
		fCookie = this;
	} else {
		fPlayBufferFunc = playerFunction;
		fCookie = cookie;
	}

	fNotifierFunc = eventNotifierFunction;
	fVolumeDB = 0.0f;
	fFlags = 0;
	fInitStatus = B_ERROR;
	fParameterWeb = NULL;
	fVolumeSlider = NULL;
	fLastVolumeUpdate = 0;

	BMediaRoster* roster = BMediaRoster::Roster();
	if (roster == NULL) {
		TRACE("BSoundPlayer::_Init: Couldn't get BMediaRoster\n");
		return;
	}

	// The inputNode that our player node will be
	// connected with is either supplied by the user
	// or the system audio mixer
	media_node inputNode;
	if (node) {
		inputNode = *node;
	} else {
		fInitStatus = roster->GetAudioMixer(&inputNode);
		if (fInitStatus != B_OK) {
			TRACE("BSoundPlayer::_Init: Couldn't GetAudioMixer\n");
			return;
		}
		fFlags |= F_MUST_RELEASE_MIXER;
	}

	media_output _output;
	media_input _input;
	int32 inputCount;
	int32 outputCount;
	media_format tryFormat;

	// Create the player node and register it
	fPlayerNode = new BPrivate::SoundPlayNode(name, this);
	fInitStatus = roster->RegisterNode(fPlayerNode);
	if (fInitStatus != B_OK) {
		TRACE("BSoundPlayer::_Init: Couldn't RegisterNode: %s\n",
			strerror(fInitStatus));
		return;
	}

	// set the producer's time source to be the "default" time source,
	// which the system audio mixer uses too.
	media_node timeSource;
	fInitStatus = roster->GetTimeSource(&timeSource);
	if (fInitStatus != B_OK) {
		TRACE("BSoundPlayer::_Init: Couldn't GetTimeSource: %s\n",
			strerror(fInitStatus));
		return;
	}
	fInitStatus = roster->SetTimeSourceFor(fPlayerNode->Node().node,
		timeSource.node);
	if (fInitStatus != B_OK) {
		TRACE("BSoundPlayer::_Init: Couldn't SetTimeSourceFor: %s\n",
			strerror(fInitStatus));
		return;
	}

	// find a free media_input
	if (!input) {
		fInitStatus = roster->GetFreeInputsFor(inputNode, &_input, 1,
			&inputCount, B_MEDIA_RAW_AUDIO);
		if (fInitStatus != B_OK) {
			TRACE("BSoundPlayer::_Init: Couldn't GetFreeInputsFor: %s\n",
				strerror(fInitStatus));
			return;
		}
		if (inputCount < 1) {
			TRACE("BSoundPlayer::_Init: Couldn't find a free input\n");
			fInitStatus = B_ERROR;
			return;
		}
	} else {
		_input = *input;
	}

	// find a free media_output
	fInitStatus = roster->GetFreeOutputsFor(fPlayerNode->Node(), &_output, 1,
		&outputCount, B_MEDIA_RAW_AUDIO);
	if (fInitStatus != B_OK) {
		TRACE("BSoundPlayer::_Init: Couldn't GetFreeOutputsFor: %s\n",
			strerror(fInitStatus));
		return;
	}
	if (outputCount < 1) {
		TRACE("BSoundPlayer::_Init: Couldn't find a free output\n");
		fInitStatus = B_ERROR;
		return;
	}

	// Set an appropriate run mode for the producer
	fInitStatus = roster->SetRunModeNode(fPlayerNode->Node(),
		BMediaNode::B_INCREASE_LATENCY);
	if (fInitStatus != B_OK) {
		TRACE("BSoundPlayer::_Init: Couldn't SetRunModeNode: %s\n",
			strerror(fInitStatus));
		return;
	}

	// setup our requested format (can still have many wildcards)
	tryFormat.type = B_MEDIA_RAW_AUDIO;
	tryFormat.u.raw_audio = *format;

#if DEBUG > 0
	char buf[100];
	string_for_format(tryFormat, buf, sizeof(buf));
	TRACE("BSoundPlayer::_Init: trying to connect with format %s\n", buf);
#endif

	// and connect the nodes
	fInitStatus = roster->Connect(_output.source, _input.destination,
		&tryFormat, &fMediaOutput, &fMediaInput);
	if (fInitStatus != B_OK) {
		TRACE("BSoundPlayer::_Init: Couldn't Connect: %s\n",
			strerror(fInitStatus));
		return;
	}

	fFlags |= F_NODES_CONNECTED;

	_GetVolumeSlider();

	TRACE("BSoundPlayer node %" B_PRId32 " has timesource %" B_PRId32 "\n",
		fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node);
}

#else  // HoG_GENODE:

void
BSoundPlayer::_Init(const media_node* //node,
	,const media_multi_audio_format * // format,
	,const char* //name
	,const media_input* //input
	,BufferPlayerFunc  playerFunction
	,EventNotifierFunc //eventNotifierFunction
	,void * cookie
	)
{
	//TRACE( "  _Init with playerFunction: %p and cookie: %p\n", playerFunction, cookie );
	
	mediaFile = (BMediaFile*)cookie;
	
	///later: possible issue in Qemu: we fail to get Audio services (unsupported virt-HW), but do not get an exception, remain stuck (in Audio_out::Connection _left.. caused by.. who? openbsd code ?)
}

#endif  // not-HoG_GENODE



#if 0  // not-HoG_GENODE:

void
BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay)
{
	CALLED();
	Notify(B_SOUND_DONE, id, gotToPlay);
}


void
BSoundPlayer::_GetVolumeSlider()
{
	CALLED();

	ASSERT(fVolumeSlider == NULL);

	BMediaRoster *roster = BMediaRoster::CurrentRoster();
	if (!roster) {
		TRACE("BSoundPlayer::_GetVolumeSlider failed to get BMediaRoster");
		return;
	}

	if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) {
		TRACE("BSoundPlayer::_GetVolumeSlider couldn't get parameter web");
		return;
	}

	int count = fParameterWeb->CountParameters();
	for (int i = 0; i < count; i++) {
		BParameter *parameter = fParameterWeb->ParameterAt(i);
		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
			continue;
		if ((parameter->ID() >> 16) != fMediaInput.destination.id)
			continue;
		if  (strcmp(parameter->Kind(), B_GAIN) != 0)
			continue;
		fVolumeSlider = (BContinuousParameter *)parameter;
		break;
	}

#if DEBUG >0
	if (!fVolumeSlider) {
		TRACE("BSoundPlayer::_GetVolumeSlider couldn't find volume control");
	}
#endif
}


void
BSoundPlayer::Notify(sound_player_notification what, ...)
{
	CALLED();
	if (fLocker.Lock()) {
		if (fNotifierFunc)
			(*fNotifierFunc)(fCookie, what);
		fLocker.Unlock();
	}
}


void
BSoundPlayer::PlayBuffer(void* buffer, size_t size,
	const media_raw_audio_format& format)
{
	if (fLocker.Lock()) {
		if (fPlayBufferFunc)
			(*fPlayBufferFunc)(fCookie, buffer, size, format);
		fLocker.Unlock();
	}
}


// #pragma mark - public sound_error


sound_error::sound_error(const char* string)
{
	m_str_const = string;
}


const char*
sound_error::what() const throw()
{
	return m_str_const;
}

#endif  // ~not-HoG_GENODE



//#pragma mark -


BSoundPlayer::~BSoundPlayer()
{
	ENTER();
	
	mediaFile->stop();
	// don't delete <mediaFile>, we don't own it
}


status_t
BSoundPlayer::Start()
{
	//if( debug )
	//	puts(" BSoundPlayer::Start() ========" );
	
	if( NULL == mediaFile )
		return B_ERROR;
	
	mediaFile->start();
	
	return B_OK;
}

void BSoundPlayer::Stop( bool, bool )
{
	///ToDo: implement Stop()
	Genode::error( "STOP: n/i" );
}


status_t
BSoundPlayer::Preroll()
{
	return -1;
}

void
BSoundPlayer::SetHasData( bool has_data )
{
	ENTER();
	
	hasData = has_data;
}

bool
BSoundPlayer::HasData()
{
	///ToDo: we fail to detect "stuck" playback (ogg/vorbis etc) despite this ; go directly to refactor/clean-up ? Or first repro in Qemu with detailed tracing
	return mediaFile && (false==mediaFile->IsTearingDown());
	///return hasData;
}

status_t
BSoundPlayer::InitCheck()
{
	//CALLED();
	return fInitStatus;
}

float
BSoundPlayer::Volume()
{
	CALLED();
	return pow(10.0, VolumeDB(true) / 20.0);
}

float
BSoundPlayer::VolumeDB(bool forcePoll)
{
	///ToDo: implement Volume(), VolumeDB etc
	return 0.;
}

void
BSoundPlayer::SetVolumeDB(float volumeDB)
{
}

