Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
490
Src/replicant/nswasabi/PlaybackBase2.cpp
Normal file
490
Src/replicant/nswasabi/PlaybackBase2.cpp
Normal file
|
@ -0,0 +1,490 @@
|
|||
#include "PlaybackBase2.h"
|
||||
|
||||
PlaybackImpl::PlaybackImpl()
|
||||
{
|
||||
playback=0;
|
||||
playback_parameters=0;
|
||||
}
|
||||
|
||||
PlaybackImpl::~PlaybackImpl()
|
||||
{
|
||||
// TODO: decide if we need playback->Release() or not
|
||||
if (playback_parameters)
|
||||
playback_parameters->Release();
|
||||
}
|
||||
|
||||
void PlaybackImpl::Connect(PlaybackBase2 *playback, ifc_playback_parameters *playback_parameters)
|
||||
{
|
||||
this->playback = playback;
|
||||
// TODO: decide if we need playback->Retain() or not
|
||||
|
||||
this->playback_parameters = playback_parameters;
|
||||
if (playback_parameters)
|
||||
playback_parameters->Retain();
|
||||
|
||||
}
|
||||
|
||||
/* ---------- */
|
||||
PlaybackBase2::PlaybackBase2()
|
||||
{
|
||||
out=0;
|
||||
implementation=0;
|
||||
filelocker=0;
|
||||
paused=false;
|
||||
last_position=0;
|
||||
output_pointers=0;
|
||||
exact_length=false;
|
||||
memset(¶meters, 0, sizeof(parameters));
|
||||
}
|
||||
|
||||
PlaybackBase2::~PlaybackBase2()
|
||||
{
|
||||
/* out should have hopefully been close already. just in case */
|
||||
if (out)
|
||||
out->Release();
|
||||
out=0;
|
||||
|
||||
if (filelocker)
|
||||
filelocker->Release();
|
||||
delete implementation;
|
||||
free(output_pointers);
|
||||
|
||||
}
|
||||
|
||||
ns_error_t PlaybackBase2::Initialize(api_service *service_manager, PlaybackImpl *implementation, nx_uri_t filename, ifc_player *player)
|
||||
{
|
||||
service_manager->GetService(&filelocker);
|
||||
|
||||
this->implementation = implementation;
|
||||
ns_error_t ret = PlaybackBase::Initialize(filename, player);
|
||||
if (ret != NErr_Success)
|
||||
return ret;
|
||||
|
||||
implementation->Connect(this, secondary_parameters);
|
||||
|
||||
this->ifc_playback::Retain(); /* the thread needs to hold a reference to this object so that it doesn't disappear out from under us */
|
||||
NXThreadCreate(&playback_thread, PlayerThreadFunction, this);
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
nx_thread_return_t PlaybackBase2::PlayerThreadFunction(nx_thread_parameter_t param)
|
||||
{
|
||||
PlaybackBase2 *playback = (PlaybackBase2 *)param;
|
||||
NXThreadCurrentSetPriority(NX_THREAD_PRIORITY_PLAYBACK);
|
||||
nx_thread_return_t ret = playback->DecodeLoop();
|
||||
playback->ifc_playback::Release(); /* give up the reference that was acquired before spawning the thread */
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PlaybackBase2::Init()
|
||||
{
|
||||
if (filelocker)
|
||||
filelocker->WaitForReadInterruptable(filename, this);
|
||||
|
||||
ns_error_t ret = implementation->Open(filename);
|
||||
if (ret != NErr_Success)
|
||||
return ret;
|
||||
|
||||
ifc_metadata *metadata;
|
||||
if (implementation->GetMetadata(&metadata) == NErr_Success)
|
||||
{
|
||||
player->SetMetadata(metadata);
|
||||
metadata->Release();
|
||||
}
|
||||
else
|
||||
player->SetMetadata(0);
|
||||
|
||||
player->SetSeekable(implementation->IsSeekable()?1:0);
|
||||
|
||||
double length;
|
||||
ret = implementation->GetLength(&length, &exact_length);
|
||||
if (ret == NErr_Success)
|
||||
player->SetLength(length);
|
||||
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
nx_thread_return_t PlaybackBase2::DecodeLoop()
|
||||
{
|
||||
player->OnLoaded(filename);
|
||||
|
||||
int ret = Init();
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
implementation->Close();
|
||||
if (filelocker)
|
||||
filelocker->UnlockFile(filename);
|
||||
player->OnError(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
player->OnReady();
|
||||
|
||||
/* wait for Play (or Stop to abort) */
|
||||
for (;;)
|
||||
{
|
||||
ns_error_t ret = Wake(WAKE_PLAY|WAKE_STOP|WAKE_INTERRUPT);
|
||||
if (ret == WAKE_PLAY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (ret == WAKE_STOP)
|
||||
{
|
||||
player->OnStopped();
|
||||
goto cleanup;
|
||||
}
|
||||
else if (ret == WAKE_INTERRUPT)
|
||||
{
|
||||
ns_error_t ret = Internal_Interrupt();
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
implementation->Close();
|
||||
player->OnError(ret);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* at this point, we know that PLAY is on */
|
||||
for (;;)
|
||||
{
|
||||
int ret = Check(WAKE_STOP|WAKE_PAUSE|WAKE_INTERRUPT);
|
||||
if (ret == WAKE_PAUSE)
|
||||
{
|
||||
if (out)
|
||||
out->Pause(1);
|
||||
paused=true;
|
||||
continue; /* continue in case there's another wake reason */
|
||||
}
|
||||
else if (ret== WAKE_UNPAUSE)
|
||||
{
|
||||
if (out)
|
||||
out->Pause(0);
|
||||
paused=false;
|
||||
continue; /* continue in case there's another wake reason */
|
||||
}
|
||||
else if (ret == WAKE_STOP)
|
||||
{
|
||||
if (out)
|
||||
{
|
||||
out->Stop();
|
||||
out->Release();
|
||||
out=0;
|
||||
}
|
||||
player->OnStopped();
|
||||
goto cleanup;
|
||||
}
|
||||
else if (ret == WAKE_INTERRUPT)
|
||||
{
|
||||
ns_error_t ret = Internal_Interrupt();
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
implementation->Close();
|
||||
player->OnError(ret);
|
||||
goto cleanup;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Agave_Seek *seek = PlaybackBase::GetSeek();
|
||||
if (seek)
|
||||
{
|
||||
ns_error_t seek_error;
|
||||
double new_position;
|
||||
ns_error_t ret = implementation->Seek(seek, &seek_error, &new_position);
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
player->OnError(ret);
|
||||
goto cleanup;
|
||||
}
|
||||
if (out)
|
||||
out->Flush(new_position);
|
||||
player->OnSeekComplete(seek_error, new_position);
|
||||
PlaybackBase::FreeSeek(seek);
|
||||
}
|
||||
|
||||
ret = implementation->DecodeStep();
|
||||
if (ret == NErr_EndOfFile)
|
||||
{
|
||||
if (out)
|
||||
out->Done();
|
||||
|
||||
PlaybackBase::OnStopPlaying();
|
||||
player->OnEndOfFile();
|
||||
|
||||
ret = WaitForClose();
|
||||
if (out)
|
||||
out->Release();
|
||||
out=0;
|
||||
|
||||
if (ret != NErr_True)
|
||||
goto cleanup;
|
||||
}
|
||||
else if (ret != NErr_Success)
|
||||
{
|
||||
if (out)
|
||||
{
|
||||
out->Done();
|
||||
out->Release();
|
||||
out=0;
|
||||
}
|
||||
if (ret != NErr_False)
|
||||
player->OnError(NErr_Error); // TODO: find better error code
|
||||
goto cleanup;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!exact_length)
|
||||
{
|
||||
double length;
|
||||
ret = implementation->GetLength(&length, &exact_length);
|
||||
if (ret == NErr_Success)
|
||||
player->SetLength(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
implementation->Close();
|
||||
|
||||
if (filelocker)
|
||||
filelocker->UnlockFile(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ns_error_t PlaybackBase2::WaitForClose()
|
||||
{
|
||||
if (!out)
|
||||
{
|
||||
player->OnClosed();
|
||||
return NErr_False;
|
||||
}
|
||||
else for (;;)
|
||||
{
|
||||
int ret = Wait(10, WAKE_PLAY|WAKE_KILL|WAKE_STOP);
|
||||
if (ret == WAKE_KILL)
|
||||
{
|
||||
player->OnClosed();
|
||||
return NErr_False;
|
||||
}
|
||||
else if (ret == WAKE_PLAY)
|
||||
{
|
||||
return NErr_True;
|
||||
}
|
||||
else if (ret == WAKE_STOP)
|
||||
{
|
||||
player->OnStopped();
|
||||
return NErr_False;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out->Playing() == NErr_True)
|
||||
player->SetPosition(last_position - out->Latency());
|
||||
else
|
||||
{
|
||||
player->SetPosition(last_position);
|
||||
player->OnClosed();
|
||||
return NErr_False;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ns_error_t PlaybackBase2::OpenOutput(const ifc_audioout::Parameters *_parameters)
|
||||
{
|
||||
// if out is already set, it means that there was a change in parameters, so we'll start a new stream
|
||||
if (out)
|
||||
{
|
||||
// check to see that the parameters actually changed
|
||||
if (!memcmp(¶meters, _parameters, sizeof(parameters)))
|
||||
return NErr_Success;
|
||||
|
||||
out->Done();
|
||||
out=0;
|
||||
}
|
||||
|
||||
parameters = *_parameters;
|
||||
|
||||
free(output_pointers);
|
||||
output_pointers = (const uint8_t **)malloc(parameters.audio.number_of_channels * sizeof(const uint8_t *));
|
||||
if (!output_pointers)
|
||||
return NErr_OutOfMemory;
|
||||
|
||||
ns_error_t ret = output_service->AudioOpen(¶meters, player, secondary_parameters, &out);
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
player->OnError(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (paused)
|
||||
out->Pause(1);
|
||||
else
|
||||
out->Pause(0);
|
||||
|
||||
|
||||
return NErr_True;
|
||||
}
|
||||
|
||||
int PlaybackBase2::OutputNonInterleaved(const void *decode_buffer, size_t decoded, double start_position)
|
||||
{
|
||||
int ret;
|
||||
size_t frames_written=0;
|
||||
const uint8_t **buffer = (const uint8_t **)decode_buffer;
|
||||
|
||||
for (size_t c=0;c<parameters.audio.number_of_channels;c++)
|
||||
{
|
||||
output_pointers[c] = buffer[c];
|
||||
}
|
||||
|
||||
while (decoded)
|
||||
{
|
||||
size_t to_write = out->CanWrite();
|
||||
if (to_write)
|
||||
{
|
||||
if (decoded < to_write)
|
||||
to_write = decoded;
|
||||
|
||||
ret = out->Output(output_pointers, to_write);
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
out->Release();
|
||||
out=0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
decoded -= to_write;
|
||||
for (size_t c=0;c<parameters.audio.number_of_channels;c++)
|
||||
{
|
||||
output_pointers[c] += to_write/parameters.audio.number_of_channels;
|
||||
}
|
||||
frames_written += to_write/parameters.audio.number_of_channels;
|
||||
player->SetPosition(start_position + (double)frames_written/parameters.audio.sample_rate - out->Latency());
|
||||
}
|
||||
else
|
||||
{
|
||||
ns_error_t ret = OutputWait();
|
||||
if (ret != NErr_Success)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return NErr_True;
|
||||
}
|
||||
|
||||
int PlaybackBase2::Output(const void *decode_buffer, size_t decoded, double start_position)
|
||||
{
|
||||
int ret;
|
||||
size_t frames_written=0;
|
||||
const uint8_t *decode8 = (const uint8_t *)decode_buffer;
|
||||
size_t buffer_position=0;
|
||||
while (decoded)
|
||||
{
|
||||
size_t to_write = out->CanWrite();
|
||||
if (to_write)
|
||||
{
|
||||
if (decoded < to_write)
|
||||
to_write = decoded;
|
||||
|
||||
ret = out->Output(&decode8[buffer_position], to_write);
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
out->Release();
|
||||
out=0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
decoded -= to_write;
|
||||
buffer_position += to_write;
|
||||
frames_written += to_write/parameters.audio.number_of_channels;
|
||||
player->SetPosition(start_position + (double)frames_written/parameters.audio.sample_rate - out->Latency());
|
||||
}
|
||||
else
|
||||
{
|
||||
ns_error_t ret = OutputWait();
|
||||
if (ret != NErr_Success)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return NErr_True;
|
||||
}
|
||||
|
||||
ns_error_t PlaybackBase2::OutputWait()
|
||||
{
|
||||
if (paused)
|
||||
{
|
||||
/* if we're paused, we need to sit and wait until we're eiter unpaused or stopped */
|
||||
for (;;)
|
||||
{
|
||||
int ret = Wake(WAKE_STOP|WAKE_PAUSE|WAKE_INTERRUPT);
|
||||
if (ret == WAKE_STOP)
|
||||
{
|
||||
out->Stop();
|
||||
out->Release();
|
||||
out=0;
|
||||
player->OnStopped();
|
||||
return NErr_False;
|
||||
}
|
||||
else if (ret == WAKE_UNPAUSE)
|
||||
{
|
||||
out->Pause(0);
|
||||
paused=false;
|
||||
break;
|
||||
}
|
||||
else if (ret == WAKE_PAUSE)
|
||||
{
|
||||
out->Pause(1);
|
||||
paused=true;
|
||||
}
|
||||
else if (PlaybackBase::PendingSeek())
|
||||
{
|
||||
return NErr_True;
|
||||
}
|
||||
else if (ret == WAKE_INTERRUPT)
|
||||
{
|
||||
ns_error_t ret = Internal_Interrupt();
|
||||
if (ret != NErr_Success)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = Wait(10, WAKE_STOP);
|
||||
if (ret == WAKE_STOP)
|
||||
{
|
||||
out->Stop();
|
||||
out->Release();
|
||||
out=0;
|
||||
player->OnStopped();
|
||||
return NErr_False;
|
||||
}
|
||||
}
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
ns_error_t PlaybackBase2::Internal_Interrupt()
|
||||
{
|
||||
Agave_Seek resume_information;
|
||||
implementation->Interrupt(&resume_information);
|
||||
ns_error_t ret = filelocker->UnlockFile(filename);
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
implementation->Close();
|
||||
return ret;
|
||||
}
|
||||
PlaybackBase::OnInterrupted();
|
||||
if (filelocker)
|
||||
filelocker->WaitForReadInterruptable(filename, this);
|
||||
ret = implementation->Resume(&resume_information);
|
||||
|
||||
|
||||
if (ret != NErr_Success)
|
||||
return ret;
|
||||
ifc_metadata *metadata;
|
||||
if (implementation->GetMetadata(&metadata) == NErr_Success)
|
||||
{
|
||||
player->SetMetadata(metadata);
|
||||
metadata->Release();
|
||||
}
|
||||
return NErr_Success;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue