winamp/Src/replicant/nswasabi/PlaybackBase.cpp
2024-09-24 14:54:57 +02:00

409 lines
9.1 KiB
C++

#include "PlaybackBase.h"
#include <stdlib.h>
#include <assert.h>
#ifdef __ANDROID__
#include <android/log.h> // TODO: replace with generic logging API
#else
#define ANDROID_LOG_INFO 0
#define ANDROID_LOG_ERROR 1
static void __android_log_print(int, const char *, const char *, ...)
{
}
#endif
PlaybackBase::PlaybackBase()
{
wake_flags=0;
last_wake_flags=0;
playback_thread=0;
secondary_parameters=0;
filename=0;
player=0;
queued_seek=0;
output_service=0;
}
int PlaybackBase::Initialize(nx_uri_t filename, ifc_player *player)
{
this->player = player;
this->filename = NXURIRetain(filename);
return NErr_Success;
}
PlaybackBase::~PlaybackBase()
{
if (secondary_parameters)
secondary_parameters->Release();
if (filename)
NXURIRelease(filename);
if (queued_seek)
free(queued_seek);
if (playback_thread)
NXThreadJoin(playback_thread, 0);
}
int PlaybackBase::Playback_Play(svc_output *output, ifc_playback_parameters *secondary_parameters)
{
if (!playback_thread)
return 1;
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Play");
output_service = output;
threadloop_node_t *apc = thread_loop.GetAPC();
if (apc)
{
this->secondary_parameters = secondary_parameters;
if (secondary_parameters)
secondary_parameters->Retain();
apc->func = APC_Play;
apc->param1 = this;
thread_loop.Schedule(apc);
return NErr_Success;
}
else
return NErr_OutOfMemory;
}
int PlaybackBase::Playback_SeekSeconds(double seconds)
{
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Seek (%f seconds)", seconds);
Agave_Seek *seek = (Agave_Seek *)malloc(sizeof(Agave_Seek));
if (seek)
{
seek->position_type = AGAVE_PLAYPOSITION_SECONDS;
seek->position.seconds = seconds;
threadloop_node_t *apc = thread_loop.GetAPC();
if (apc)
{
apc->func = APC_Seek;
apc->param1 = this;
apc->param2 = seek;
thread_loop.Schedule(apc);
return NErr_Success;
}
}
free(seek);
return NErr_OutOfMemory;
}
int PlaybackBase::Playback_Pause()
{
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Pause");
threadloop_node_t *apc = thread_loop.GetAPC();
if (apc)
{
apc->func = APC_Pause;
apc->param1 = this;
thread_loop.Schedule(apc);
return NErr_Success;
}
else
return NErr_OutOfMemory;
}
int PlaybackBase::Playback_Unpause()
{
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Unpause");
threadloop_node_t *apc = thread_loop.GetAPC();
if (apc)
{
apc->func = APC_Unpause;
apc->param1 = this;
thread_loop.Schedule(apc);
return NErr_Success;
}
else
return NErr_OutOfMemory;
}
int PlaybackBase::Playback_Stop()
{
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Stop");
threadloop_node_t *apc = thread_loop.GetAPC();
if (apc)
{
apc->func = APC_Stop;
apc->param1 = this;
thread_loop.Schedule(apc);
return NErr_Success;
}
else
return NErr_OutOfMemory;
}
int PlaybackBase::Playback_Close()
{
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Close");
threadloop_node_t *apc = thread_loop.GetAPC();
if (apc)
{
apc->func = APC_Close;
apc->param1 = this;
thread_loop.Schedule(apc);
return NErr_Success;
}
else
return NErr_OutOfMemory;
}
int PlaybackBase::FileLockCallback_Interrupt()
{
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Interrupt");
threadloop_node_t *apc = thread_loop.GetAPC();
if (apc)
{
apc->func = APC_Interrupt;
apc->param1 = this;
thread_loop.Schedule(apc);
return NErr_Success;
}
else
return NErr_OutOfMemory;
}
void PlaybackBase::ToggleFlags(int wake_reason)
{
switch(wake_reason)
{
case WAKE_KILL:
last_wake_flags ^= WAKE_KILL; /* toggle kill flag */
break;
case WAKE_STOP:
last_wake_flags ^= WAKE_STOP; /* toggle stop flag */
break;
case WAKE_PLAY:
last_wake_flags ^= WAKE_PLAY; /* toggle play flag */
break;
case WAKE_PAUSE:
case WAKE_UNPAUSE:
last_wake_flags ^= WAKE_PAUSE; /* toggle pause flag */
break;
case WAKE_INTERRUPT:
last_wake_flags ^= WAKE_INTERRUPT; /* toggle interrupt flag */
break;
}
}
int PlaybackBase::WakeReason(int mask) const
{
int reason_awoken = last_wake_flags ^ wake_flags;
reason_awoken = reason_awoken & mask;
if (reason_awoken & WAKE_INTERRUPT)
{
if (wake_flags & WAKE_INTERRUPT)
return WAKE_INTERRUPT;
else
return WAKE_RESUME;
}
if (reason_awoken & WAKE_STOP)
{
return WAKE_STOP;
}
if (reason_awoken & WAKE_KILL)
{
return WAKE_KILL;
}
if (reason_awoken & WAKE_PLAY)
{
if (wake_flags & WAKE_PLAY)
return WAKE_PLAY;
else /* if someone cleared the play flag for whatever reason, just treat it as a 0 */
return 0;
}
if (reason_awoken & WAKE_PAUSE)
{
if (wake_flags & WAKE_PAUSE)
return WAKE_PAUSE;
else
return WAKE_UNPAUSE;
}
return 0;
}
int PlaybackBase::Wake(int mask)
{
assert(mask != 0); /* make sure they didn't specify a 0 mask (which would make this function potentially never return */
assert((mask & WAKE_ALL_MASK) != 0); /* make sure it's a valid mask */
for (;;)
{
int reason_awoken = last_wake_flags ^ wake_flags;
reason_awoken = reason_awoken & mask;
if (reason_awoken)
{
int ret = WakeReason(mask);
ToggleFlags(ret); // mark the last-known-state of the wake flags
return ret;
}
if (((mask & WAKE_PLAY) && !(wake_flags & WAKE_PLAY))/* if we're stopped and they asked to be woken up for play */
|| ((mask & WAKE_PAUSE) && (wake_flags & WAKE_PAUSE)) /* or waiting to be woken up for unpause */
|| ((mask & WAKE_INTERRUPT) && (wake_flags & WAKE_INTERRUPT))) /* or waiting to be woken up for resume */
{
thread_loop.Step();
int ret = WakeReason(mask);
if (ret) /* if ret is !0, it means we gotten woken up for a reason we care about */
{
ToggleFlags(ret); // mark the last-known-state of the wake flags
return ret;
}
}
else /* no reason to sleep, so just return 0 (no change) */
{
return 0;
}
}
}
int PlaybackBase::Check(int mask)
{
assert(mask != 0); /* make sure they didn't specify a 0 mask (which would make this function potentially never return */
assert((mask & WAKE_ALL_MASK) != 0); /* make sure it's a valid mask */
int reason_awoken = last_wake_flags ^ wake_flags;
reason_awoken = reason_awoken & mask;
int ret = 0;
if (reason_awoken)
{
ret = WakeReason(mask);
ToggleFlags(ret); // mark the last-known-state of the wake flags
}
return ret;
}
int PlaybackBase::Wait(unsigned int milliseconds, int mask)
{
int reason_awoken = last_wake_flags ^ wake_flags;
reason_awoken = reason_awoken & mask;
if (reason_awoken)
{
int ret = WakeReason(mask);
ToggleFlags(ret); // mark the last-known-state of the wake flags
return ret;
}
thread_loop.Step(milliseconds);
int ret = WakeReason(mask);
ToggleFlags(ret); // mark the last-known-state of the wake flags
return ret;
}
int PlaybackBase::Sleep(unsigned int milliseconds, int mask)
{
int reason_awoken = last_wake_flags ^ wake_flags;
reason_awoken = reason_awoken & mask;
if (reason_awoken)
{
int ret = WakeReason(mask);
assert(ret != 0);
return ret;
}
thread_loop.Step(milliseconds);
int ret = WakeReason(mask);
return ret;
}
void PlaybackBase::OnStopPlaying()
{
// turn off the play flag (also adjust old wake flags so we don't trigger a WAKE_STOP)
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] OnStopPlaying");
wake_flags &= ~WAKE_PLAY;
last_wake_flags &= ~WAKE_PLAY;
}
void PlaybackBase::OnInterrupted()
{
__android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] OnInterrupted");
wake_flags &= ~WAKE_INTERRUPT;
last_wake_flags &= ~WAKE_INTERRUPT;
}
Agave_Seek *PlaybackBase::GetSeek()
{
Agave_Seek *seek = queued_seek;
queued_seek=0;
return seek;
}
void PlaybackBase::FreeSeek(Agave_Seek *seek)
{
free(seek);
}
bool PlaybackBase::PendingSeek()
{
return !!queued_seek;
}
void PlaybackBase::APC_Play(void *_playback_base, void *param2, double real_value)
{
PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
playback_base->wake_flags |= WAKE_PLAY;
}
void PlaybackBase::APC_Seek(void *_playback_base, void *_seek, double real_value)
{
PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
Agave_Seek *seek = (Agave_Seek *)_seek;
free(playback_base->queued_seek);
playback_base->queued_seek = seek;
}
void PlaybackBase::APC_Pause(void *_playback_base, void *param2, double real_value)
{
PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
playback_base->wake_flags |= WAKE_PAUSE;
}
void PlaybackBase::APC_Unpause(void *_playback_base, void *param2, double real_value)
{
PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
playback_base->wake_flags &= ~WAKE_PAUSE;
}
void PlaybackBase::APC_Stop(void *_playback_base, void *param2, double real_value)
{
PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
playback_base->wake_flags |= WAKE_STOP;
}
void PlaybackBase::APC_Close(void *_playback_base, void *param2, double real_value)
{
PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
playback_base->wake_flags |= WAKE_KILL;
}
void PlaybackBase::APC_Interrupt(void *_playback_base, void *param2, double real_value)
{
PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
playback_base->wake_flags |= WAKE_INTERRUPT;
}