libvgm: Add exception handling, and move buffer

Move the player buffer off the stack, as well as adding exception
handlers to the init, playback, and shutdown code.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-07-15 06:50:03 -07:00
parent 0263367639
commit 5b51e664e2
2 changed files with 111 additions and 85 deletions

View file

@ -19,6 +19,7 @@
DATA_LOADER* dLoad; DATA_LOADER* dLoad;
PlayerA* mainPlr; PlayerA* mainPlr;
id<CogSource> source; id<CogSource> source;
UINT8* sampleBuffer;
double sampleRate; double sampleRate;
long loopCount; long loopCount;
double fadeTime; double fadeTime;

View file

@ -24,6 +24,8 @@
@implementation libvgmDecoder @implementation libvgmDecoder
enum { bufferSize = 1024 };
#ifdef _DEBUG #ifdef _DEBUG
const int logLevel = DEVLOG_DEBUG; const int logLevel = DEVLOG_DEBUG;
#else #else
@ -99,6 +101,7 @@ const int masterVol = 0x10000; // Fixed point 16.16
fileData = NULL; fileData = NULL;
dLoad = NULL; dLoad = NULL;
mainPlr = NULL; mainPlr = NULL;
sampleBuffer = NULL;
} }
return self; return self;
} }
@ -130,62 +133,71 @@ const int masterVol = 0x10000; // Fixed point 16.16
BOOL repeatOne = IsRepeatOneSet(); BOOL repeatOne = IsRepeatOneSet();
uint32_t maxLoops = repeatOne ? 0 : (uint32_t)loopCount; uint32_t maxLoops = repeatOne ? 0 : (uint32_t)loopCount;
mainPlr = new PlayerA; try {
mainPlr->RegisterPlayerEngine(new VGMPlayer); mainPlr = new PlayerA;
mainPlr->RegisterPlayerEngine(new S98Player); mainPlr->RegisterPlayerEngine(new VGMPlayer);
mainPlr->RegisterPlayerEngine(new DROPlayer); mainPlr->RegisterPlayerEngine(new S98Player);
mainPlr->RegisterPlayerEngine(new GYMPlayer); mainPlr->RegisterPlayerEngine(new DROPlayer);
mainPlr->SetEventCallback(FilePlayCallback, (__bridge void*)(self)); mainPlr->RegisterPlayerEngine(new GYMPlayer);
mainPlr->SetFileReqCallback(RequestFileCallback, NULL); mainPlr->SetEventCallback(FilePlayCallback, (__bridge void*)(self));
mainPlr->SetLogCallback(PlayerLogCallback, NULL); mainPlr->SetFileReqCallback(RequestFileCallback, NULL);
{ mainPlr->SetLogCallback(PlayerLogCallback, NULL);
PlayerA::Config pCfg = mainPlr->GetConfiguration(); {
pCfg.masterVol = masterVol; PlayerA::Config pCfg = mainPlr->GetConfiguration();
pCfg.loopCount = maxLoops; pCfg.masterVol = masterVol;
pCfg.fadeSmpls = (int)ceil(sampleRate * fadeTime); // fade over configured duration pCfg.loopCount = maxLoops;
pCfg.endSilenceSmpls = sampleRate / 2; // 0.5 seconds of silence at the end pCfg.fadeSmpls = (int)ceil(sampleRate * fadeTime); // fade over configured duration
pCfg.pbSpeed = 1.0; pCfg.endSilenceSmpls = sampleRate / 2; // 0.5 seconds of silence at the end
mainPlr->SetConfiguration(pCfg); pCfg.pbSpeed = 1.0;
} mainPlr->SetConfiguration(pCfg);
mainPlr->SetOutputSettings((int)ceil(sampleRate), numChannels, numBitsPerSample, smplAlloc); }
mainPlr->SetOutputSettings((int)ceil(sampleRate), numChannels, numBitsPerSample, smplAlloc);
[source seek:0 whence:SEEK_END]; [source seek:0 whence:SEEK_END];
size_t size = [source tell]; size_t size = [source tell];
[source seek:0 whence:SEEK_SET]; [source seek:0 whence:SEEK_SET];
fileData = (UINT8*)malloc(size); fileData = (UINT8*)malloc(size);
if(!fileData) if(!fileData)
return NO;
size_t bytesRead = [source read:fileData amount:size];
if(bytesRead != size)
return NO;
dLoad = MemoryLoader_Init(fileData, (unsigned int)size);
if(!dLoad)
return NO;
DataLoader_SetPreloadBytes(dLoad, 0x100);
if(DataLoader_Load(dLoad))
return NO;
if(mainPlr->LoadFile(dLoad))
return NO;
PlayerBase* player = mainPlr->GetPlayer();
mainPlr->SetLoopCount(maxLoops);
if(player->GetPlayerType() == FCC_VGM) {
VGMPlayer* vgmplay = dynamic_cast<VGMPlayer*>(player);
mainPlr->SetLoopCount(vgmplay->GetModifiedLoopCount(maxLoops));
}
length = player->Tick2Second(player->GetTotalTicks()) * sampleRate;
[self setTrackEnded:NO];
mainPlr->Start();
} catch(std::exception& e) {
ALog(@"Exception caught opening file: %s\n", e.what());
return NO; return NO;
size_t bytesRead = [source read:fileData amount:size];
if(bytesRead != size)
return NO;
dLoad = MemoryLoader_Init(fileData, (unsigned int)size);
if(!dLoad)
return NO;
DataLoader_SetPreloadBytes(dLoad, 0x100);
if(DataLoader_Load(dLoad))
return NO;
if(mainPlr->LoadFile(dLoad))
return NO;
PlayerBase* player = mainPlr->GetPlayer();
mainPlr->SetLoopCount(maxLoops);
if(player->GetPlayerType() == FCC_VGM) {
VGMPlayer* vgmplay = dynamic_cast<VGMPlayer*>(player);
mainPlr->SetLoopCount(vgmplay->GetModifiedLoopCount(maxLoops));
} }
length = player->Tick2Second(player->GetTotalTicks()) * sampleRate; sampleBuffer = (UINT8*) malloc(bufferSize * numChannels * (numBitsPerSample / 8));
if(!sampleBuffer)
[self setTrackEnded:NO]; return NO;
mainPlr->Start();
[self willChangeValueForKey:@"properties"]; [self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"];
@ -215,47 +227,52 @@ const int masterVol = 0x10000; // Fixed point 16.16
id audioChunkClass = NSClassFromString(@"AudioChunk"); id audioChunkClass = NSClassFromString(@"AudioChunk");
AudioChunk* chunk = [[audioChunkClass alloc] initWithProperties:[self properties]]; AudioChunk* chunk = [[audioChunkClass alloc] initWithProperties:[self properties]];
int frames = 1024; int frames = bufferSize;
const size_t bytesPerFrame = [chunk format].mBytesPerFrame; const int bytesPerFrame = [chunk format].mBytesPerFrame;
uint8_t buffer[frames * bytesPerFrame];
void* buf = (void*)buffer; void* buf = (void*)sampleBuffer;
BOOL repeatOne = IsRepeatOneSet(); BOOL repeatOne = IsRepeatOneSet();
uint32_t maxLoops = repeatOne ? 0 : (uint32_t)loopCount; uint32_t maxLoops = repeatOne ? 0 : (uint32_t)loopCount;
PlayerBase* player = mainPlr->GetPlayer(); double streamTimestamp;
mainPlr->SetLoopCount(maxLoops);
if(player->GetPlayerType() == FCC_VGM) {
VGMPlayer* vgmplay = dynamic_cast<VGMPlayer*>(player);
mainPlr->SetLoopCount(vgmplay->GetModifiedLoopCount(maxLoops));
}
double streamTimestamp = mainPlr->GetCurTime(0);
UInt32 framesDone = 0; UInt32 framesDone = 0;
while(framesDone < frames) { try {
UInt32 framesToDo = frames - framesDone; PlayerBase* player = mainPlr->GetPlayer();
if(framesToDo > smplAlloc) mainPlr->SetLoopCount(maxLoops);
framesToDo = smplAlloc; if(player->GetPlayerType() == FCC_VGM) {
VGMPlayer* vgmplay = dynamic_cast<VGMPlayer*>(player);
mainPlr->SetLoopCount(vgmplay->GetModifiedLoopCount(maxLoops));
}
int numSamples = framesToDo * numChannels * (numBitsPerSample / 8); streamTimestamp = mainPlr->GetCurTime(0);
UINT32 numRendered = mainPlr->Render(numSamples, buf); while(framesDone < frames) {
UInt32 framesToDo = frames - framesDone;
if(framesToDo > smplAlloc)
framesToDo = smplAlloc;
buf = (void*)(((uint8_t*)buf) + numRendered); int numSamples = framesToDo * bytesPerFrame;
UINT32 framesRendered = numRendered / (numChannels * (numBitsPerSample / 8)); UINT32 numRendered = mainPlr->Render(numSamples, buf);
framesDone += framesRendered; buf = (void*)(((uint8_t*)buf) + numRendered);
if(framesRendered < framesToDo) UINT32 framesRendered = numRendered / bytesPerFrame;
break;
framesDone += framesRendered;
if(framesRendered < framesToDo)
break;
}
} catch(std::exception& e) {
ALog(@"Exception caught while playing track: %s\n", e.what());
return nil;
} }
[chunk setStreamTimestamp:streamTimestamp]; [chunk setStreamTimestamp:streamTimestamp];
[chunk assignSamples:buffer frameCount:framesDone]; [chunk assignSamples:sampleBuffer frameCount:framesDone];
return chunk; return chunk;
} }
@ -269,16 +286,24 @@ const int masterVol = 0x10000; // Fixed point 16.16
} }
- (void)close { - (void)close {
if(mainPlr) { if(sampleBuffer) {
mainPlr->Stop(); free(sampleBuffer);
mainPlr->UnloadFile(); sampleBuffer = NULL;
delete mainPlr;
mainPlr = NULL;
} }
if(dLoad) { try {
DataLoader_Deinit(dLoad); if(mainPlr) {
dLoad = NULL; mainPlr->Stop();
mainPlr->UnloadFile();
delete mainPlr;
mainPlr = NULL;
}
if(dLoad) {
DataLoader_Deinit(dLoad);
dLoad = NULL;
}
} catch(std::exception& e) {
ALog(@"Exception caught cleaning up player: %s\n", e.what());
} }
if(fileData) { if(fileData) {
free(fileData); free(fileData);