Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/audio/miniaudio/ma_audio_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,9 @@ static int32_t maPlaySound(AudioSystem* audio, int32_t soundIndex, int32_t prior
return -1;
}

AudioEntry* entry = &ma->base.audioGroups[sound->audioGroup]->audo.entries[sound->audioFile];
DataWin* audioGroup = ma->base.audioGroups[sound->audioGroup];
DataWin_loadAudoIfNeeded(audioGroup, (uint32_t)sound->audioFile);
AudioEntry* entry = &audioGroup->audo.entries[sound->audioFile];

ma_decoder_config decoderConfig = ma_decoder_config_init_default();
result = ma_decoder_init_memory(entry->data, entry->dataSize, &decoderConfig, &slot->decoder);
Expand Down Expand Up @@ -684,7 +686,9 @@ static float maGetSoundLength(AudioSystem* audio, int32_t soundOrInstance) {
ma_result decResult;
if (inAudo) {
if (0 > sound->audioFile || (uint32_t) sound->audioFile >= ma->base.audioGroups[sound->audioGroup]->audo.count) return 0.0f;
AudioEntry* entry = &ma->base.audioGroups[sound->audioGroup]->audo.entries[sound->audioFile];
DataWin* audioGroup = ma->base.audioGroups[sound->audioGroup];
DataWin_loadAudoIfNeeded(audioGroup, (uint32_t)sound->audioFile);
AudioEntry* entry = &audioGroup->audo.entries[sound->audioFile];
ma_decoder_config decoderConfig = ma_decoder_config_init_default();
decResult = ma_decoder_init_memory(entry->data, entry->dataSize, &decoderConfig, &decoder);
} else {
Expand Down Expand Up @@ -750,6 +754,7 @@ static void maGroupLoad(AudioSystem* audio, int32_t groupIndex) {

DataWinParserOptions options = {0};
options.parseAudo = true;
options.lazyLoadAudio = audio->dw->lazyLoadAudio;
if (audio->dw->mappedFile)
options.loadType = DATAWINLOADTYPE_MAP_FILE;
DataWin *audioGroup = DataWin_parse(((MaAudioSystem*)audio)->fileSystem->vtable->resolvePath(((MaAudioSystem*)audio)->fileSystem, buf), options);
Expand Down
3 changes: 3 additions & 0 deletions src/audio/openal/al_audio_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ static int32_t maPlaySound(AudioSystem* audio, int32_t soundIndex, int32_t prior
return -1;
}

DataWin_loadAudoIfNeeded(ma->base.audioGroups[sound->audioGroup], (uint32_t)sound->audioFile);
AudioEntry* entry = &ma->base.audioGroups[sound->audioGroup]->audo.entries[sound->audioFile];

uint32_t channels = 0;
Expand Down Expand Up @@ -889,6 +890,7 @@ static float maGetSoundLength(AudioSystem* audio, int32_t soundOrInstance) {
bool inAudo = !isRegular || isEmbedded || isCompressed;
if (inAudo) {
if (0 > sound->audioFile || (uint32_t) sound->audioFile >= ma->base.audioGroups[sound->audioGroup]->audo.count) return 0.0f;
DataWin_loadAudoIfNeeded(ma->base.audioGroups[sound->audioGroup], (uint32_t)sound->audioFile);
AudioEntry* entry = &ma->base.audioGroups[sound->audioGroup]->audo.entries[sound->audioFile];

uint32_t channels, sampleRate, bitsPerSample, audioDataLen;
Expand Down Expand Up @@ -946,6 +948,7 @@ static void maGroupLoad(AudioSystem* audio, int32_t groupIndex) {

DataWinParserOptions options = {0};
options.parseAudo = true;
options.lazyLoadAudio = audio->dw->lazyLoadAudio;
if (audio->dw->mappedFile)
options.loadType = DATAWINLOADTYPE_MAP_FILE;
DataWin *audioGroup = DataWin_parse(((AlAudioSystem*)audio)->fileSystem->vtable->resolvePath(((AlAudioSystem*)audio)->fileSystem, buf), options);
Expand Down
9 changes: 7 additions & 2 deletions src/audio/web/web_audio_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ static int32_t webPlaySound(AudioSystem* audio, int32_t soundIndex, int32_t prio
return -1;
}

AudioEntry* entry = &ma->base.audioGroups[sound->audioGroup]->audo.entries[sound->audioFile];
DataWin* audioGroup = ma->base.audioGroups[sound->audioGroup];
DataWin_loadAudoIfNeeded(audioGroup, (uint32_t)sound->audioFile);
AudioEntry* entry = &audioGroup->audo.entries[sound->audioFile];

ma_decoder_config decoderConfig = ma_decoder_config_init_default();
result = ma_decoder_init_memory(entry->data, entry->dataSize, &decoderConfig, &slot->decoder);
Expand Down Expand Up @@ -547,7 +549,9 @@ static float webGetSoundLength(AudioSystem* audio, int32_t soundOrInstance) {
ma_result decResult;
if (inAudo) {
if (0 > sound->audioFile || (uint32_t) sound->audioFile >= ma->base.audioGroups[sound->audioGroup]->audo.count) return 0.0f;
AudioEntry* entry = &ma->base.audioGroups[sound->audioGroup]->audo.entries[sound->audioFile];
DataWin* audioGroup = ma->base.audioGroups[sound->audioGroup];
DataWin_loadAudoIfNeeded(audioGroup, (uint32_t)sound->audioFile);
AudioEntry* entry = &audioGroup->audo.entries[sound->audioFile];
ma_decoder_config decoderConfig = ma_decoder_config_init_default();
decResult = ma_decoder_init_memory(entry->data, entry->dataSize, &decoderConfig, &decoder);
} else {
Expand Down Expand Up @@ -612,6 +616,7 @@ static void webGroupLoad(AudioSystem* audio, int32_t groupIndex) {

DataWinParserOptions options = {0};
options.parseAudo = true;
options.lazyLoadAudio = audio->dw->lazyLoadAudio;
DataWin *audioGroup = DataWin_parse(((WebAudioSystem*)audio)->fileSystem->vtable->resolvePath(((WebAudioSystem*)audio)->fileSystem, buf), options);
arrput(audio->audioGroups, audioGroup);
}
Expand Down
36 changes: 32 additions & 4 deletions src/data_win.c
Original file line number Diff line number Diff line change
Expand Up @@ -2573,7 +2573,7 @@ void DataWin_loadTxtrIfNeeded(DataWin* dw, uint32_t textureId) {
}
}

static void parseAUDO(BinaryReader* reader, DataWin* dw) {
static void parseAUDO(BinaryReader* reader, DataWin* dw, bool loadAudioDataLazily) {
Audo* a = &dw->audo;

uint32_t count;
Expand All @@ -2590,7 +2590,9 @@ static void parseAUDO(BinaryReader* reader, DataWin* dw) {
a->entries[i].dataSize = BinaryReader_readUint32(reader);
a->entries[i].dataOffset = (uint32_t)BinaryReader_getPosition(reader);
// Load audio data into owned buffer
if (dw->mappedFile) {
if (loadAudioDataLazily) {
a->entries[i].data = nullptr;
} else if (dw->mappedFile) {
a->entries[i].data = dw->mappedFile + a->entries[i].dataOffset;
} else if (a->entries[i].dataSize > 0) {
a->entries[i].data = (uint8_t *)safeMalloc(a->entries[i].dataSize);
Expand All @@ -2602,6 +2604,31 @@ static void parseAUDO(BinaryReader* reader, DataWin* dw) {
free(ptrs);
}

void DataWin_loadAudoIfNeeded(DataWin* dw, uint32_t audioEntryId) {
Audo* a = &dw->audo;
AudioEntry* entry = &a->entries[audioEntryId];

if (!entry->present || entry->dataSize == 0) return;
if (entry->data != nullptr) return;

if (!dw->lazyLoadFile) {
fprintf(stderr, "loadAudoIfNeeded: called without a lazy load file.\n");
return;
}

entry->data = (uint8_t *)safeMalloc(entry->dataSize);

memset(entry->data, 0, entry->dataSize);
long old_seek = ftell(dw->lazyLoadFile);
fseek(dw->lazyLoadFile, entry->dataOffset, SEEK_SET);
size_t read = fread(entry->data, 1, entry->dataSize, dw->lazyLoadFile);
fseek(dw->lazyLoadFile, old_seek, SEEK_SET);

if (read != entry->dataSize) {
fprintf(stderr, "loadAudoIfNeeded: couldn't read %u bytes to load audio entry %u.\n", entry->dataSize, audioEntryId);
}
}

// ===[ MAIN PARSE FUNCTION ]===

DataWin* DataWin_parse(const char* filePath, DataWinParserOptions options) {
Expand Down Expand Up @@ -2842,7 +2869,7 @@ DataWin* DataWin_parse(const char* filePath, DataWinParserOptions options) {
} else if (options.parseTxtr && memcmp(chunkName, "TXTR", 4) == 0) {
parseTXTR(&reader, dw, chunkEnd, options.lazyLoadTextures);
} else if (options.parseAudo && memcmp(chunkName, "AUDO", 4) == 0) {
parseAUDO(&reader, dw);
parseAUDO(&reader, dw, options.lazyLoadAudio);
} else {
printf("Unknown chunk: %.4s (length %u at offset 0x%zX)\n", chunkName, chunkLength, chunkDataStart - 8);
}
Expand Down Expand Up @@ -2874,7 +2901,8 @@ DataWin* DataWin_parse(const char* filePath, DataWinParserOptions options) {
// If lazy-loading rooms, keep the file handle open for DataWin_loadRoomPayload, otherwise close it now
dw->lazyLoadRooms = options.lazyLoadRooms;
dw->lazyLoadTextures = options.lazyLoadTextures;
if (options.lazyLoadRooms || options.lazyLoadTextures) {
dw->lazyLoadAudio = options.lazyLoadAudio;
if (options.lazyLoadRooms || options.lazyLoadTextures || options.lazyLoadAudio) {
dw->lazyLoadFile = file;
dw->lazyLoadFilePath = safeStrdup(filePath);
dw->fileSize = (size_t) fileSize;
Expand Down
4 changes: 4 additions & 0 deletions src/data_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ typedef struct {
bool lazyLoadRooms;
// If true, TXTR objects will be loaded on demand via DataWin_loadTxtrIfNeeded, and unloaded if memory is tight.
bool lazyLoadTextures;
// If true, AUDO objects will be loaded on demand via DataWin_loadAudoIfNeeded.
bool lazyLoadAudio;

// When lazyLoadRooms is true, this list indicates which rooms should be loaded during load time instead of demand. They will also not be freed.
StringBooleanEntry* eagerlyLoadedRooms;
Expand Down Expand Up @@ -936,6 +938,7 @@ struct DataWin {
size_t fileSize; // cached size of the DataWin, captured at parse time. Used for platforms where fseek(SEEK_END)+ftell is unreliable due to buffering (like the PlayStation 2).
bool lazyLoadRooms; // mirrors the parser option so Runner can branch without re-reading options
bool lazyLoadTextures; // ditto, but with TXTR pages
bool lazyLoadAudio; // ditto, but with AUDO entries
};

DataWin* DataWin_parse(const char* filePath, DataWinParserOptions options);
Expand All @@ -956,5 +959,6 @@ void DataWin_bumpVersionTo(DataWin* dw, uint32_t major, uint32_t minor, uint32_t
void GamePath_computeInternal(GamePath* path);
PathPositionResult GamePath_getPosition(GamePath* path, float t);
void DataWin_loadTxtrIfNeeded(DataWin* dw, uint32_t textureId);
void DataWin_loadAudoIfNeeded(DataWin* dw, uint32_t audioEntryId);

#endif /* _BS_DATA_WIN_H_ */
7 changes: 7 additions & 0 deletions src/desktop/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ typedef struct {
bool lazyRooms;
StringBooleanEntry* eagerRooms; // stb_ds string-keyed set of room names
bool lazyTextures;
bool lazyAudio;
DataWinLoadType loadType;
int profilerFramesBetween; // 0 = disabled
#ifdef ENABLE_VM_OPCODE_PROFILER
Expand Down Expand Up @@ -420,6 +421,7 @@ static void printUsage(const char *argv0) {
" --save-folder <directory> - Set the directory will save files will be stored\n"
" --game-args <args> - Arguments to pass to the game\n"
" --lazy-textures - Load textures into VRAM on first use, improving startup times\n"
" --lazy-audio - Load audio into RAM on first use, reducing memory usage\n"
" --load-type <type> - Specify how data.win is loaded, per-chunk or all at once\n"
#ifdef EABLE_VM_OPCODE_PROFILER
" --profile-opcodes - Rank which GML opcodes were executed the most\n"
Expand Down Expand Up @@ -479,6 +481,7 @@ static void parseCommandLineArgs(CommandLineArgs* args, int argc, char* argv[])
{"save-folder", required_argument, nullptr, 'B'},
{"game-args", required_argument, nullptr, 'N'},
{"lazy-textures", no_argument, nullptr, 'L'},
{"lazy-audio", no_argument, nullptr, 'K'},
{"load-type", required_argument, nullptr, 999},
#ifdef ENABLE_VM_OPCODE_PROFILER
{"profile-opcodes", no_argument, nullptr, 'Q'},
Expand Down Expand Up @@ -574,6 +577,9 @@ static void parseCommandLineArgs(CommandLineArgs* args, int argc, char* argv[])
case 'L':
args->lazyTextures = true;
break;
case 'K':
args->lazyAudio = true;
break;
case 'e':
shput(args->eventsToBeTraced, optarg, true);
break;
Expand Down Expand Up @@ -1038,6 +1044,7 @@ int main(int argc, char* argv[]) {
options.loadType = args.loadType;
options.lazyLoadRooms = args.lazyRooms;
options.lazyLoadTextures = args.lazyTextures;
options.lazyLoadAudio = args.lazyAudio;
options.eagerlyLoadedRooms = args.eagerRooms;
DataWin* dataWin = DataWin_parse(currentDataWinPath, options);

Expand Down
Loading