// // libvgmDecoder.mm // libvgmPlayer // // Created by Christopher Snowhill on 1/02/22. // Copyright 2022 __LoSnoCo__. All rights reserved. // #import "libvgmDecoder.h" #import "Logging.h" #import "PlaylistController.h" #import #import #import #import #import #import #import #import #import @implementation libvgmDecoder #ifdef _DEBUG const int logLevel = DEVLOG_DEBUG; #else const int logLevel = DEVLOG_INFO; #endif static UINT8 FilePlayCallback(PlayerBase* player, void* userParam, UINT8 evtType, void* evtParam) { libvgmDecoder* decoder = (__bridge libvgmDecoder*)(userParam); switch(evtType) { case PLREVT_START: // printf("Playback started.\n"); break; case PLREVT_STOP: // printf("Playback stopped.\n"); break; case PLREVT_LOOP: { // UINT32* curLoop = (UINT32*)evtParam; // if (player->GetState() & PLAYSTATE_SEEK) // break; } break; case PLREVT_END: if([decoder trackEnded]) break; [decoder setTrackEnded:YES]; break; } return 0x00; } #include "yrw801.h" static DATA_LOADER* RequestFileCallback(void* userParam, PlayerBase* player, const char* fileName) { DATA_LOADER* dLoad; if(strcmp(fileName, "yrw801.rom") == 0) { dLoad = MemoryLoader_Init(yrw801_rom, sizeof(yrw801_rom)); } else { dLoad = FileLoader_Init(fileName); } UINT8 retVal = DataLoader_Load(dLoad); if(!retVal) return dLoad; DataLoader_Deinit(dLoad); return NULL; } static const char* LogLevel2Str(UINT8 level) { static const char* LVL_NAMES[6] = { " ??? ", "Error", "Warn ", "Info ", "Debug", "Trace" }; if(level >= (sizeof(LVL_NAMES) / sizeof(LVL_NAMES[0]))) level = 0; return LVL_NAMES[level]; } static void PlayerLogCallback(void* userParam, PlayerBase* player, UINT8 level, UINT8 srcType, const char* srcTag, const char* message) { if(level > logLevel) return; // don't print messages with higher verbosity than current log level if(srcType == PLRLOGSRC_PLR) { ALog(@"[%s] %s: %s", LogLevel2Str(level), player->GetPlayerName(), message); } else { ALog(@"[%s] %s %s: %s", LogLevel2Str(level), player->GetPlayerName(), srcTag, message); } return; } const int numChannels = 2; const int numBitsPerSample = 24; const int smplAlloc = 2048; const int masterVol = 0x10000; // Fixed point 16.16 - (id)init { self = [super init]; if(self) { fileData = NULL; dLoad = NULL; mainPlr = NULL; } return self; } - (BOOL)open:(id)s { [self setSource:s]; // We need file-size to use GME if(![source seekable]) { return NO; } sampleRate = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] valueForKey:@"synthSampleRate"] doubleValue]; if(sampleRate < 8000.0) { sampleRate = 44100.0; } else if(sampleRate > 192000.0) { sampleRate = 192000.0; } loopCount = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] valueForKey:@"synthDefaultLoopCount"] longValue]; if(loopCount < 1) { loopCount = 1; } else if(loopCount > 10) { loopCount = 10; } fadeTime = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] valueForKey:@"synthDefaultFadeSeconds"] doubleValue]; BOOL repeatOne = IsRepeatOneSet(); uint32_t maxLoops = repeatOne ? 0 : (uint32_t)loopCount; mainPlr = new PlayerA; mainPlr->RegisterPlayerEngine(new VGMPlayer); mainPlr->RegisterPlayerEngine(new S98Player); mainPlr->RegisterPlayerEngine(new DROPlayer); mainPlr->RegisterPlayerEngine(new GYMPlayer); mainPlr->SetEventCallback(FilePlayCallback, (__bridge void*)(self)); mainPlr->SetFileReqCallback(RequestFileCallback, NULL); mainPlr->SetLogCallback(PlayerLogCallback, NULL); { PlayerA::Config pCfg = mainPlr->GetConfiguration(); pCfg.masterVol = masterVol; pCfg.loopCount = maxLoops; pCfg.fadeSmpls = (int)ceil(sampleRate * fadeTime); // fade over configured duration pCfg.endSilenceSmpls = sampleRate / 2; // 0.5 seconds of silence at the end pCfg.pbSpeed = 1.0; mainPlr->SetConfiguration(pCfg); } mainPlr->SetOutputSettings((int)ceil(sampleRate), numChannels, numBitsPerSample, smplAlloc); [source seek:0 whence:SEEK_END]; size_t size = [source tell]; [source seek:0 whence:SEEK_SET]; fileData = (UINT8*)malloc(size); 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(player); mainPlr->SetLoopCount(vgmplay->GetModifiedLoopCount(maxLoops)); } length = player->Tick2Second(player->GetTotalTicks()) * sampleRate; [self setTrackEnded:NO]; mainPlr->Start(); [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; return YES; } - (NSDictionary*)properties { return @{ @"bitrate": @(0), @"sampleRate": @(sampleRate), @"totalFrames": @(length), @"bitsPerSample": @(numBitsPerSample), @"channels": @(numChannels), @"seekable": @(YES), @"endian": @"host", @"encoding": @"synthesized" }; } - (NSDictionary *)metadata { return @{}; } - (int)readAudio:(void*)buf frames:(UInt32)frames { if([self trackEnded]) return 0; BOOL repeatOne = IsRepeatOneSet(); uint32_t maxLoops = repeatOne ? 0 : (uint32_t)loopCount; PlayerBase* player = mainPlr->GetPlayer(); mainPlr->SetLoopCount(maxLoops); if(player->GetPlayerType() == FCC_VGM) { VGMPlayer* vgmplay = dynamic_cast(player); mainPlr->SetLoopCount(vgmplay->GetModifiedLoopCount(maxLoops)); } UInt32 framesDone = 0; while(framesDone < frames) { UInt32 framesToDo = frames - framesDone; if(framesToDo > smplAlloc) framesToDo = smplAlloc; int numSamples = framesToDo * numChannels * (numBitsPerSample / 8); mainPlr->Render(numSamples, buf); buf = (void*)(((uint8_t*)buf) + numSamples); framesDone += framesToDo; } return framesDone; } - (long)seek:(long)frame { [self setTrackEnded:NO]; mainPlr->Seek(PLAYPOS_SAMPLE, (unsigned int)frame); return frame; } - (void)close { if(mainPlr) { mainPlr->Stop(); mainPlr->UnloadFile(); delete mainPlr; mainPlr = NULL; } if(dLoad) { DataLoader_Deinit(dLoad); dLoad = NULL; } if(fileData) { free(fileData); fileData = NULL; } } - (void)dealloc { [self close]; } + (NSArray*)fileTypes { return @[@"vgm", @"vgz", @"s98", @"dro", @"gym"]; } + (NSArray*)mimeTypes { return nil; } + (float)priority { return 1.25; } + (NSArray*)fileTypeAssociations { NSMutableArray* ret = [[NSMutableArray alloc] init]; [ret addObject:@"libvgm Files"]; [ret addObject:@"vg.icns"]; [ret addObjectsFromArray:[self fileTypes]]; return @[[NSArray arrayWithArray:ret]]; } - (void)setSource:(id)s { source = s; } - (id)source { return source; } - (BOOL)trackEnded { return trackEnded; } - (void)setTrackEnded:(BOOL)ended { trackEnded = ended; } @end