Basic audio support

This covers enough of the music/sound functions to get the few games I
tried working.

Looping is not properly implemented here. Unfortunately, loop offsets
are stored in a "bgi" file which appears to be encypted. There are two
options for fixing this: (1) the proper solution, crack the bgi
encryption and read the offsets from that file, or (2) read the loop
offsets from the "smpl" chunk in the wav file. (2) is easier but it will
only work for wav files (not ogg).
This commit is contained in:
Nunuhara Cabbage
2019-12-29 12:05:09 -08:00
parent 88395ada63
commit ccc367ec12
4 changed files with 296 additions and 19 deletions
+3
View File
@@ -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;
+216
View File
@@ -0,0 +1,216 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <http://gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <SDL_mixer.h>
#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;
}
+58
View File
@@ -0,0 +1,58 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <http://gnu.org/licenses/>.
*/
#ifndef SYSTEM4_AUDIO_H
#define SYSTEM4_AUDIO_H
#include <stdbool.h>
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 */
+19 -19
View File
@@ -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)