diff --git a/ain.c b/ain.c index 4809010..8d6f2c0 100644 --- a/ain.c +++ b/ain.c @@ -430,6 +430,9 @@ static struct ain_function_type *read_function_types(struct ain_reader *r, int c static bool read_tag(struct ain_reader *r, struct ain *ain) { + if (r->index + 4 >= r->size) + return false; + uint8_t *tag_loc = r->buf + r->index; r->index += 4; diff --git a/audio.c b/audio.c new file mode 100644 index 0000000..851e7e0 --- /dev/null +++ b/audio.c @@ -0,0 +1,216 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include "ald.h" +#include "audio.h" +#include "system4.h" + +#define AUDIO_SLOT_ALLOC_STEP 256 + +struct audio_slot { + Mix_Chunk *chunk; + int channel; + int no; +}; + +static struct { + struct audio_slot *slots; + int nr_slots; +} audio[2]; + +void audio_init(void) +{ + Mix_Init(MIX_INIT_MP3 | MIX_INIT_OGG); + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { + WARNING("Audio initialization failed"); + } + + audio[AUDIO_MUSIC].slots = xcalloc(16, sizeof(struct audio_slot)); + audio[AUDIO_MUSIC].nr_slots = 16; + + audio[AUDIO_SOUND].slots = xcalloc(16, sizeof(struct audio_slot)); + audio[AUDIO_SOUND].nr_slots = 16; +} + +void audio_fini(void) +{ + Mix_CloseAudio(); + Mix_Quit(); +} + +static void audio_realloc(enum audio_type type, int new_size) +{ + audio[type].slots = xrealloc(audio[type].slots, new_size * sizeof(struct audio_slot)); + for (int i = audio[type].nr_slots; i < new_size; i++) { + audio[type].slots[i].chunk = NULL; + audio[type].slots[i].channel = -1; + } + audio[type].nr_slots = new_size; +} + +static struct audio_slot *audio_alloc_channel(enum audio_type type, int ch) +{ + if (ch >= audio[type].nr_slots) + audio_realloc(type, ch + AUDIO_SLOT_ALLOC_STEP); + return &audio[type].slots[ch]; +} + +int audio_get_unused_channel(enum audio_type type) +{ + for (int i = 0; i < audio[type].nr_slots; i++) { + if (!audio[type].slots[i].chunk) + return i; + } + + int slot = audio[type].nr_slots; + audio_realloc(type, slot + AUDIO_SLOT_ALLOC_STEP); + return slot; +} + +static struct audio_slot *get_slot(enum audio_type type, int ch) +{ + if (ch < 0 || ch >= audio[type].nr_slots) + return NULL; + return &audio[type].slots[ch]; +} + +static int audio_type_to_archive_type(enum audio_type type) +{ + switch (type) { + case AUDIO_MUSIC: return ALDFILE_BGM; + case AUDIO_SOUND: return ALDFILE_WAVE; + } + ERROR("Unknown audio type: %d", type); +} + +bool audio_exists(enum audio_type type, int no) +{ + int archive = audio_type_to_archive_type(type); + return ald[archive] && ald_data_exists(ald[archive], no); +} + +static Mix_Chunk *load_chunk(enum audio_type type, int no) +{ + struct archive_data *dfile; + int archive = audio_type_to_archive_type(type); + + if (!(dfile = ald_get(ald[archive], no))) { + WARNING("Failed to load %s %d", archive == ALDFILE_BGM ? "BGM" : "WAV", no); + return NULL; + } + + Mix_Chunk *chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(dfile->data, dfile->size), 1); + ald_free_data(dfile); + + if (!chunk) { + WARNING("%s %d: not a valid audio file", archive == ALDFILE_BGM ? "BGM" : "WAV", no); + return NULL; + } + + return chunk; +} + +static void unload_slot(struct audio_slot *slot) +{ + if (slot->chunk) { + Mix_HaltChannel(slot->channel); + Mix_FreeChunk(slot->chunk); + slot->chunk = NULL; + } + slot->channel = -1; +} + +int audio_prepare(enum audio_type type, int ch, int no) +{ + if (ch < 0) + return 0; + + struct audio_slot *slot = audio_alloc_channel(type, ch); + unload_slot(slot); + + audio[type].slots[ch].chunk = load_chunk(type, no); + audio[type].slots[ch].no = no; + return !!audio[type].slots[ch].chunk; +} + +int audio_unprepare(enum audio_type type, int ch) +{ + struct audio_slot *slot = get_slot(type, ch); + if (!slot) + return 0; + + unload_slot(slot); + return 1; +} + +int audio_play(enum audio_type type, int ch) +{ + struct audio_slot *slot = get_slot(type, ch); + if (!slot || !slot->chunk) + return 0; + + if (slot->channel >= 0 && Mix_Paused(slot->channel)) { + Mix_Resume(slot->channel); + return 1; + } + + slot->channel = Mix_PlayChannel(-1, slot->chunk, type == AUDIO_MUSIC ? -1 : 0); + return slot->channel >= 0; +} + +int audio_stop(enum audio_type type, int ch) +{ + struct audio_slot *slot = get_slot(type, ch); + if (!slot) + return 0; + + if (slot->channel >= 0) + Mix_HaltChannel(slot->channel); + return 1; +} + +bool audio_is_playing(enum audio_type type, int ch) +{ + struct audio_slot *slot = get_slot(type, ch); + return slot && slot->channel >= 0 && Mix_Playing(slot->channel); +} + +int audio_fade(enum audio_type type, int ch, possibly_unused int time, possibly_unused int volume, bool stop) +{ + // TODO: fade + if (stop) + audio_stop(type, ch); + return 1; +} + +int audio_get_time_length(enum audio_type type, int ch) +{ + struct audio_slot *slot = get_slot(type, ch); + if (!slot || !slot->chunk) + return 0; + + int freq = 0; + uint16_t fmt = 0; + int chans = 0; + + Mix_QuerySpec(&freq, &fmt, &chans); + + uint32_t points = slot->chunk->alen / ((fmt & 0xFF) / 8); + uint32_t frames = points / chans; + return (frames * 1000) / freq; +} diff --git a/audio.h b/audio.h new file mode 100644 index 0000000..01f79ed --- /dev/null +++ b/audio.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef SYSTEM4_AUDIO_H +#define SYSTEM4_AUDIO_H + +#include + +enum audio_type { + AUDIO_MUSIC, + AUDIO_SOUND +}; + +void audio_init(void); +void audio_fini(void); + +bool audio_exists(enum audio_type type, int no); +int audio_prepare(enum audio_type type, int channel, int no); +int audio_unprepare(enum audio_type type, int channel); +int audio_play(enum audio_type type, int channel); +int audio_stop(enum audio_type type, int channel); +bool audio_is_playing(enum audio_type type, int channel); +int audio_set_loop_count(enum audio_type type, int channel, int count); +int audio_get_loop_count(enum audio_type type, int channel); +int audio_set_loop_start_pos(enum audio_type type, int channel, int pos); +int audio_set_loop_end_pos(enum audio_type type, int channel, int pos); +int audio_fade(enum audio_type type, int channel, int time, int volume, bool stop); +int audio_stop_fade(enum audio_type type, int channel); +bool audio_is_fading(enum audio_type type, int channel); +int audio_pause(enum audio_type type, int channel); +int audio_restart(enum audio_type type, int channel); +bool audio_is_paused(enum audio_type type, int channel); +int audio_get_pos(enum audio_type type, int channel); +int audio_get_length(enum audio_type type, int channel); +int audio_get_sample_pos(enum audio_type type, int channel); +int audio_get_sample_length(enum audio_type type, int channel); +int audio_seek(enum audio_type type, int channel, int pos); +int audio_get_unused_channel(enum audio_type type); +int audio_reverse_LR(enum audio_type type, int channel); +int audio_get_volume(enum audio_type type, int channel); +int audio_get_time_length(enum audio_type type, int channel); +int audio_get_group_num(enum audio_type type, int channel); +int audio_prepare_from_file(enum audio_type type, int channel, char *filename); + +#endif /* SYSTEM4_AUDIO_H */ diff --git a/hll/SACT2.c b/hll/SACT2.c index e331fcc..6472d20 100644 --- a/hll/SACT2.c +++ b/hll/SACT2.c @@ -437,27 +437,27 @@ hll_unimplemented(SACT2, CSV_SetInt); // void CSV_Realloc(int nLines, int nColumns) hll_unimplemented(SACT2, CSV_Realloc); // int Music_IsExist(int nNum) -hll_defun_inline(Music_IsExist, music_exists(a[0].i - 1)); +hll_defun_inline(Music_IsExist, audio_exists(AUDIO_MUSIC, a[0].i - 1)); // int Music_Prepare(int nCh, int nNum) -hll_warn_unimplemented(SACT2, Music_Prepare, 1); +hll_defun_inline(Music_Prepare, audio_prepare(AUDIO_MUSIC, a[0].i, a[1].i - 1)); // int Music_Unprepare(int nCh) -hll_warn_unimplemented(SACT2, Music_Unprepare, 1); +hll_defun_inline(Music_Unprepare, audio_unprepare(AUDIO_MUSIC, a[0].i)); // int Music_Play(int nCh) -hll_warn_unimplemented(SACT2, Music_Play, 1); +hll_defun_inline(Music_Play, audio_play(AUDIO_MUSIC, a[0].i)); // int Music_Stop(int nCh) -hll_warn_unimplemented(SACT2, Music_Stop, 1); +hll_defun_inline(Music_Stop, audio_stop(AUDIO_MUSIC, a[0].i)); // int Music_IsPlay(int nCh) -hll_warn_unimplemented(SACT2, Music_IsPlay, 1); +hll_defun_inline(Music_IsPlay, audio_is_playing(AUDIO_MUSIC, a[0].i)); // int Music_SetLoopCount(int nCh, int nCount) hll_warn_unimplemented(SACT2, Music_SetLoopCount, 1); // int Music_GetLoopCount(int nCh) hll_warn_unimplemented(SACT2, Music_GetLoopCount, 1); // int Music_SetLoopStartPos(int nCh, int dwPos) -hll_warn_unimplemented(SACT2, Music_SetLoopStartPos, 1); +hll_unimplemented(SACT2, Music_SetLoopStartPos); // int Music_SetLoopEndPos(int nCh, int dwPos) -hll_warn_unimplemented(SACT2, Music_SetLoopEndPos, 1); +hll_unimplemented(SACT2, Music_SetLoopEndPos); // int Music_Fade(int nCh, int nTime, int nVolume, int bStop) -hll_warn_unimplemented(SACT2, Music_Fade, 1); +hll_defun_inline(Music_Fade, audio_fade(AUDIO_MUSIC, a[0].i, a[1].i, a[2].i, a[3].i)); // int Music_StopFade(int nCh) hll_warn_unimplemented(SACT2, Music_StopFade, 1); // int Music_IsFade(int nCh) @@ -479,25 +479,25 @@ hll_warn_unimplemented(SACT2, Music_GetSampleLength, 1); // int Music_Seek(int nCh, int dwPos) hll_warn_unimplemented(SACT2, Music_Seek, 1); // int Sound_IsExist(int nNum) -hll_defun_inline(Sound_IsExist, sound_exists(a[0].i - 1)); +hll_defun_inline(Sound_IsExist, audio_exists(AUDIO_SOUND, a[0].i - 1)); // int Sound_GetUnuseChannel(void) -hll_unimplemented(SACT2, Sound_GetUnuseChannel); +hll_defun_inline(Sound_GetUnuseChannel, audio_get_unused_channel(AUDIO_SOUND)); // int Sound_Prepare(int nCh, int nNum) -hll_unimplemented(SACT2, Sound_Prepare); +hll_defun_inline(Sound_Prepare, audio_prepare(AUDIO_SOUND, a[0].i, a[1].i - 1)); // int Sound_Unprepare(int nCh) -hll_warn_unimplemented(SACT2, Sound_Unprepare, 1); +hll_defun_inline(Sound_Unprepare, audio_unprepare(AUDIO_SOUND, a[0].i)); // int Sound_Play(int nCh) -hll_unimplemented(SACT2, Sound_Play); +hll_defun_inline(Sound_Play, audio_play(AUDIO_SOUND, a[0].i)); // int Sound_Stop(int nCh) -hll_unimplemented(SACT2, Sound_Stop); +hll_defun_inline(Sound_Stop, audio_stop(AUDIO_SOUND, a[0].i)); // int Sound_IsPlay(int nCh) -hll_warn_unimplemented(SACT2, Sound_IsPlay, 0); +hll_defun_inline(Sound_IsPlay, audio_is_playing(AUDIO_SOUND, a[0].i)); // int Sound_SetLoopCount(int nCh, int nCount) hll_unimplemented(SACT2, Sound_SetLoopCount); // int Sound_GetLoopCount(int nCh) hll_unimplemented(SACT2, Sound_GetLoopCount); // int Sound_Fade(int nCh, int nTime, int nVolume, int bStop) -hll_unimplemented(SACT2, Sound_Fade); +hll_defun_inline(Sound_Fade, audio_fade(AUDIO_SOUND, a[0].i, a[1].i, a[2].i, a[3].i)); // int Sound_StopFade(int nCh) hll_unimplemented(SACT2, Sound_StopFade); // int Sound_IsFade(int nCh) @@ -507,11 +507,11 @@ hll_unimplemented(SACT2, Sound_GetPos); // int Sound_GetLength(int nCh) hll_unimplemented(SACT2, Sound_GetLength); // int Sound_ReverseLR(int nCh) -hll_unimplemented(SACT2, Sound_ReverseLR); +hll_warn_unimplemented(SACT2, Sound_ReverseLR, 1); // int Sound_GetVolume(int nCh) hll_unimplemented(SACT2, Sound_GetVolume); // int Sound_GetTimeLength(int nCh) -hll_unimplemented(SACT2, Sound_GetTimeLength); +hll_defun_inline(Sound_GetTimeLength, audio_get_time_length(AUDIO_SOUND, a[0].i)); // int Sound_GetGroupNum(int nCh) hll_unimplemented(SACT2, Sound_GetGroupNum); // bool Sound_PrepareFromFile(int nCh, string szFileName)