mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-08 14:58:14 +03:00
Replace SDL_mixer with libsndfile + sts_mixer
Replace SDL_mixer with an audio implementation using libsndfile, sts_mixer (a single-file header library) and SDL's basic audio interface. This allows for much more flexibility in controlling audio playback. Fading is now properly implemented, as well as various other functions from the SACT2/KiwiSoundEngine interface. sts_mixer can support recursive mixing, so it will be possible to implement the "VolumeValancer" mixer channels specified in System40.ini in the future (once I figure out how the mapping of audio files to mixer channels is supposed to work).
This commit is contained in:
+232
-372
@@ -14,133 +14,39 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/archive.h"
|
||||
#include "system4/file.h"
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "audio.h"
|
||||
#include "id_pool.h"
|
||||
#include "mixer.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define AUDIO_SLOT_ALLOC_STEP 256
|
||||
/*
|
||||
* Common audio interface for SACT2/KiwiSoundEngine.
|
||||
* This is just a wrapper over the generic audio interface in audio_mixer.c.
|
||||
*/
|
||||
|
||||
struct wav_slot {
|
||||
Mix_Chunk *chunk;
|
||||
struct archive_data *dfile;
|
||||
int channel;
|
||||
int no;
|
||||
bool reverse;
|
||||
};
|
||||
|
||||
struct bgm_slot {
|
||||
Mix_Music *music;
|
||||
struct archive_data *dfile;
|
||||
int no;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct wav_slot *slots;
|
||||
int nr_slots;
|
||||
} wav;
|
||||
|
||||
struct {
|
||||
struct bgm_slot *slots;
|
||||
int nr_slots;
|
||||
int playing;
|
||||
} bgm;
|
||||
static struct id_pool wav;
|
||||
static struct id_pool bgm;
|
||||
|
||||
// Anonymous channels for playing sounds in-engine. audio_update must be called
|
||||
// periodically to clean up channels that have finished playing.
|
||||
#define NR_ANONYMOUS_CHANNELS 64
|
||||
static int anonymous_channels[NR_ANONYMOUS_CHANNELS];
|
||||
|
||||
#define BGI_MAX 100
|
||||
|
||||
struct bgi {
|
||||
int no;
|
||||
int loopno;
|
||||
int looptop;
|
||||
int len;
|
||||
};
|
||||
|
||||
static int bgi_nfile;
|
||||
static struct bgi bgi_data[BGI_MAX];
|
||||
|
||||
static char *bgi_gets(char *buf, int n, FILE *fp) {
|
||||
char *s = buf;
|
||||
int c;
|
||||
while (--n > 0 && (c = fgetc(fp)) != EOF) {
|
||||
c = c >> 4 | (c & 0xf) << 4; // decrypt
|
||||
*s++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
}
|
||||
if (s == buf && c == EOF)
|
||||
return NULL;
|
||||
*s = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void bgi_read(const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open bgi file: %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[100];
|
||||
while (bgi_nfile < BGI_MAX && bgi_gets(buf, sizeof(buf), fp)) {
|
||||
int terminator;
|
||||
if (sscanf(buf, " %d, %d, %d, %d, %d",
|
||||
&bgi_data[bgi_nfile].no,
|
||||
&bgi_data[bgi_nfile].loopno,
|
||||
&bgi_data[bgi_nfile].looptop,
|
||||
&bgi_data[bgi_nfile].len,
|
||||
&terminator) != 5
|
||||
|| terminator != -1) {
|
||||
continue;
|
||||
}
|
||||
bgi_nfile++;
|
||||
}
|
||||
}
|
||||
|
||||
possibly_unused static struct bgi *bgi_find(int no) {
|
||||
for (int i = 0; i < bgi_nfile; i++) {
|
||||
if (bgi_data[i].no == no)
|
||||
return &bgi_data[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
wav.slots = xcalloc(16, sizeof(struct wav_slot));
|
||||
wav.nr_slots = 16;
|
||||
|
||||
bgm.slots = xcalloc(16, sizeof(struct bgm_slot));
|
||||
bgm.nr_slots = 16;
|
||||
bgm.playing = -1;
|
||||
|
||||
if (config.bgi_path)
|
||||
bgi_read(config.bgi_path);
|
||||
id_pool_init(&wav);
|
||||
id_pool_init(&bgm);
|
||||
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
anonymous_channels[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_fini(void)
|
||||
{
|
||||
Mix_CloseAudio();
|
||||
Mix_Quit();
|
||||
mixer_init();
|
||||
}
|
||||
|
||||
void audio_update(void)
|
||||
@@ -174,298 +80,252 @@ bool audio_play_sound(int sound_no)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void wav_realloc(int new_size)
|
||||
bool wav_exists(int no) { return asset_exists(ASSET_SOUND, no); }
|
||||
bool bgm_exists(int no) { return asset_exists(ASSET_BGM, no); }
|
||||
|
||||
static int audio_prepare(struct id_pool *pool, int id, enum asset_type type, int no)
|
||||
{
|
||||
wav.slots = xrealloc(wav.slots, new_size * sizeof(struct wav_slot));
|
||||
for (int i = wav.nr_slots; i < new_size; i++) {
|
||||
wav.slots[i].chunk = NULL;
|
||||
wav.slots[i].channel = -1;
|
||||
wav.slots[i].reverse = false;
|
||||
}
|
||||
wav.nr_slots = new_size;
|
||||
}
|
||||
|
||||
static void bgm_realloc(int new_size)
|
||||
{
|
||||
bgm.slots = xrealloc(bgm.slots, new_size * sizeof(struct bgm_slot));
|
||||
for (int i = bgm.nr_slots; i < new_size; i++) {
|
||||
bgm.slots[i].music = NULL;
|
||||
}
|
||||
bgm.nr_slots = new_size;
|
||||
}
|
||||
|
||||
static struct wav_slot *wav_alloc_channel(int ch)
|
||||
{
|
||||
if (ch >= wav.nr_slots)
|
||||
wav_realloc(ch + AUDIO_SLOT_ALLOC_STEP);
|
||||
return &wav.slots[ch];
|
||||
}
|
||||
|
||||
static struct bgm_slot *bgm_alloc_channel(int ch)
|
||||
{
|
||||
if (ch >= bgm.nr_slots)
|
||||
bgm_realloc(ch + AUDIO_SLOT_ALLOC_STEP);
|
||||
return &bgm.slots[ch];
|
||||
}
|
||||
|
||||
int wav_get_unused_channel(void)
|
||||
{
|
||||
for (int i = 0; i < wav.nr_slots; i++) {
|
||||
if (!wav.slots[i].chunk)
|
||||
return i;
|
||||
}
|
||||
|
||||
int slot = wav.nr_slots;
|
||||
wav_realloc(slot + AUDIO_SLOT_ALLOC_STEP);
|
||||
return slot;
|
||||
}
|
||||
|
||||
int bgm_get_unused_channel(void)
|
||||
{
|
||||
for (int i = 0; i < bgm.nr_slots; i++) {
|
||||
if (!bgm.slots[i].music)
|
||||
return i;
|
||||
}
|
||||
|
||||
int slot = bgm.nr_slots;
|
||||
bgm_realloc(slot + AUDIO_SLOT_ALLOC_STEP);
|
||||
return slot;
|
||||
}
|
||||
|
||||
static struct wav_slot *wav_get_slot(int ch)
|
||||
{
|
||||
if (ch < 0 || ch >= wav.nr_slots)
|
||||
return NULL;
|
||||
return &wav.slots[ch];
|
||||
}
|
||||
|
||||
static struct bgm_slot *bgm_get_slot(int ch)
|
||||
{
|
||||
if (ch < 0 || ch >= bgm.nr_slots)
|
||||
return NULL;
|
||||
return &bgm.slots[ch];
|
||||
}
|
||||
|
||||
bool wav_exists(int no)
|
||||
{
|
||||
return asset_exists(ASSET_SOUND, no);
|
||||
}
|
||||
|
||||
bool bgm_exists(int no)
|
||||
{
|
||||
return asset_exists(ASSET_BGM, no);
|
||||
}
|
||||
|
||||
static bool wav_load_chunk(int no, struct wav_slot *slot)
|
||||
{
|
||||
struct archive_data *dfile = asset_get(ASSET_SOUND, no);
|
||||
if (!dfile) {
|
||||
WARNING("Failed to load WAV %d", no);
|
||||
return false;
|
||||
}
|
||||
|
||||
Mix_Chunk *chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(dfile->data, dfile->size), 1);
|
||||
if (!chunk) {
|
||||
WARNING("WAV %d: not a valid audio file", no);
|
||||
return false;
|
||||
}
|
||||
|
||||
slot->chunk = chunk;
|
||||
slot->dfile = dfile;
|
||||
slot->no = no;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool bgm_load_music(int no, struct bgm_slot *slot)
|
||||
{
|
||||
struct archive_data *dfile = asset_get(ASSET_BGM, no);
|
||||
if (!dfile) {
|
||||
WARNING("Failed to load WAV %d", no);
|
||||
return false;
|
||||
}
|
||||
|
||||
Mix_MusicType type = MUS_WAV;
|
||||
const char *ext = file_extension(dfile->name);
|
||||
if (!strcmp(ext, "ogg")) {
|
||||
type = MUS_OGG;
|
||||
}
|
||||
|
||||
Mix_Music *music = Mix_LoadMUSType_RW(SDL_RWFromConstMem(dfile->data, dfile->size), type, SDL_TRUE);
|
||||
if (!music) {
|
||||
WARNING("BGM %d: not a valid audio file", no);
|
||||
return false;
|
||||
}
|
||||
|
||||
slot->music = music;
|
||||
slot->dfile = dfile;
|
||||
slot->no = no;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void wav_unload_slot(struct wav_slot *slot)
|
||||
{
|
||||
if (slot->chunk) {
|
||||
if (slot->channel >= 0)
|
||||
Mix_HaltChannel(slot->channel);
|
||||
Mix_FreeChunk(slot->chunk);
|
||||
slot->chunk = NULL;
|
||||
archive_free_data(slot->dfile);
|
||||
slot->dfile = NULL;
|
||||
}
|
||||
slot->channel = -1;
|
||||
}
|
||||
|
||||
static void bgm_unload_slot(struct bgm_slot *slot)
|
||||
{
|
||||
if (slot->music) {
|
||||
Mix_FreeMusic(slot->music);
|
||||
slot->music = NULL;
|
||||
archive_free_data(slot->dfile);
|
||||
slot->dfile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int wav_prepare(int ch, int no)
|
||||
{
|
||||
if (ch < 0)
|
||||
if (id < 0)
|
||||
return 0;
|
||||
|
||||
struct wav_slot *slot = wav_alloc_channel(ch);
|
||||
wav_unload_slot(slot);
|
||||
return wav_load_chunk(no, slot);
|
||||
struct channel *ch = channel_open(type, no);
|
||||
struct channel *old = id_pool_set(pool, id, ch);
|
||||
if (old)
|
||||
channel_close(old);
|
||||
return !!ch;
|
||||
}
|
||||
|
||||
int bgm_prepare(int ch, int no)
|
||||
int wav_prepare(int id, int no) { return audio_prepare(&wav, id, ASSET_SOUND, no); }
|
||||
int bgm_prepare(int id, int no) { return audio_prepare(&bgm, id, ASSET_BGM, no); }
|
||||
|
||||
static int audio_unprepare(struct id_pool *pool, int id)
|
||||
{
|
||||
if (ch < 0)
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
|
||||
struct bgm_slot *slot = bgm_alloc_channel(ch);
|
||||
bgm_unload_slot(slot);
|
||||
return bgm_load_music(no, slot);
|
||||
}
|
||||
|
||||
int wav_unprepare(int ch)
|
||||
{
|
||||
struct wav_slot *slot = wav_get_slot(ch);
|
||||
if (!slot)
|
||||
return 0;
|
||||
|
||||
wav_unload_slot(slot);
|
||||
channel_close(ch);
|
||||
id_pool_release(pool, id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bgm_unprepare(int ch)
|
||||
int wav_unprepare(int id) { return audio_unprepare(&wav, id); }
|
||||
int bgm_unprepare(int id) { return audio_unprepare(&bgm, id); }
|
||||
|
||||
static int audio_play(struct id_pool *pool, int id)
|
||||
{
|
||||
struct bgm_slot *slot = bgm_get_slot(ch);
|
||||
if (!slot)
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch) {
|
||||
// NOTE: SACT2 brings up error GUI here
|
||||
WARNING("Tried to play from unprepared audio channel");
|
||||
return 0;
|
||||
|
||||
bgm_unload_slot(slot);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wav_play(int ch)
|
||||
{
|
||||
struct wav_slot *slot = wav_get_slot(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, 0);
|
||||
if (slot->channel < 0)
|
||||
return 0;
|
||||
|
||||
Mix_SetReverseStereo(slot->channel, slot->reverse);
|
||||
return 1;
|
||||
return channel_play(ch);
|
||||
}
|
||||
int wav_play(int id) { return audio_play(&wav, id); }
|
||||
int bgm_play(int id) { return audio_play(&bgm, id); }
|
||||
|
||||
int bgm_play(int ch)
|
||||
static int audio_stop(struct id_pool *pool, int id)
|
||||
{
|
||||
struct bgm_slot *slot = bgm_get_slot(ch);
|
||||
if (!slot || !slot->music)
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch) {
|
||||
return 0;
|
||||
|
||||
bgm.playing = slot - bgm.slots;
|
||||
return !Mix_PlayMusic(slot->music, -1);
|
||||
}
|
||||
|
||||
int wav_stop(int ch)
|
||||
{
|
||||
struct wav_slot *slot = wav_get_slot(ch);
|
||||
if (!slot)
|
||||
return 0;
|
||||
|
||||
if (slot->channel >= 0)
|
||||
Mix_HaltChannel(slot->channel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bgm_stop(int ch)
|
||||
{
|
||||
// FIXME: what if channel isn't playing?
|
||||
if (bgm.playing == ch) {
|
||||
Mix_FadeOutMusic(10);
|
||||
bgm.playing = -1;
|
||||
}
|
||||
return 1;
|
||||
return channel_stop(ch);
|
||||
}
|
||||
int wav_stop(int id) { return audio_stop(&wav, id); }
|
||||
int bgm_stop(int id) { return audio_stop(&bgm, id); }
|
||||
|
||||
bool wav_is_playing(int ch)
|
||||
static int audio_is_playing(struct id_pool *pool, int id)
|
||||
{
|
||||
struct wav_slot *slot = wav_get_slot(ch);
|
||||
return slot && slot->channel >= 0 && Mix_Playing(slot->channel);
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
return ch && channel_is_playing(ch);
|
||||
}
|
||||
bool wav_is_playing(int id) { return audio_is_playing(&wav, id); }
|
||||
bool bgm_is_playing(int id) { return audio_is_playing(&bgm, id); }
|
||||
|
||||
bool bgm_is_playing(int ch)
|
||||
static int audio_set_loop_count(struct id_pool *pool, int id, int count)
|
||||
{
|
||||
return bgm.playing == ch && Mix_PlayingMusic();
|
||||
}
|
||||
|
||||
int wav_fade(int ch, possibly_unused int time, possibly_unused int volume, bool stop)
|
||||
{
|
||||
// TODO: fade
|
||||
if (stop)
|
||||
wav_stop(ch);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bgm_fade(int ch, possibly_unused int time, possibly_unused int volume, bool stop)
|
||||
{
|
||||
// TODO: fade
|
||||
if (stop)
|
||||
bgm_stop(ch);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wav_reverse_LR(int ch)
|
||||
{
|
||||
struct wav_slot *slot = wav_get_slot(ch);
|
||||
if (!slot)
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
slot->reverse = true;
|
||||
if (slot->channel >= 0) {
|
||||
Mix_SetReverseStereo(slot->channel, 1);
|
||||
}
|
||||
return 1;
|
||||
return channel_set_loop_count(ch, count);
|
||||
}
|
||||
int wav_set_loop_count(int id, int count) { return audio_set_loop_count(&wav, id, count); }
|
||||
int bgm_set_loop_count(int id, int count) { return audio_set_loop_count(&bgm, id, count); }
|
||||
|
||||
int wav_get_time_length(int ch)
|
||||
static int audio_get_loop_count(struct id_pool *pool, int id)
|
||||
{
|
||||
struct wav_slot *slot = wav_get_slot(ch);
|
||||
if (!slot || !slot->chunk)
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
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;
|
||||
return channel_get_loop_count(ch);
|
||||
}
|
||||
int wav_get_loop_count(int id) { return audio_get_loop_count(&wav, id); }
|
||||
int bgm_get_loop_count(int id) { return audio_get_loop_count(&bgm, id); }
|
||||
|
||||
static int audio_set_loop_start_pos(struct id_pool *pool, int id, int pos)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_set_loop_start_pos(ch, pos);
|
||||
}
|
||||
int wav_set_loop_start_pos(int id, int pos) { return audio_set_loop_start_pos(&wav, id, pos); }
|
||||
int bgm_set_loop_start_pos(int id, int pos) { return audio_set_loop_start_pos(&bgm, id, pos); }
|
||||
|
||||
static int audio_set_loop_end_pos(struct id_pool *pool, int id, int pos)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_set_loop_end_pos(ch, pos);
|
||||
}
|
||||
int wav_set_loop_end_pos(int id, int pos) { return audio_set_loop_end_pos(&wav, id, pos); }
|
||||
int bgm_set_loop_end_pos(int id, int pos) { return audio_set_loop_end_pos(&bgm, id, pos); }
|
||||
|
||||
static int audio_fade(struct id_pool *pool, int id, int time, int volume, bool stop)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_fade(ch, time, volume, stop);
|
||||
}
|
||||
int wav_fade(int id, int time, int volume, bool stop) { return audio_fade(&wav, id, time, volume, stop); }
|
||||
int bgm_fade(int id, int time, int volume, bool stop) { return audio_fade(&bgm, id, time, volume, stop); }
|
||||
|
||||
static int audio_stop_fade(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_stop_fade(ch);
|
||||
}
|
||||
int wav_stop_fade(int id) { return audio_stop_fade(&wav, id); }
|
||||
int bgm_stop_fade(int id) { return audio_stop_fade(&bgm, id); }
|
||||
|
||||
static bool audio_is_fading(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_is_fading(ch);
|
||||
}
|
||||
bool wav_is_fading(int id) { return audio_is_fading(&wav, id); }
|
||||
bool bgm_is_fading(int id) { return audio_is_fading(&bgm, id); }
|
||||
|
||||
static int audio_pause(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_pause(ch);
|
||||
}
|
||||
int wav_pause(int id) { return audio_pause(&wav, id); }
|
||||
int bgm_pause(int id) { return audio_pause(&bgm, id); }
|
||||
|
||||
static int audio_restart(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_restart(ch);
|
||||
}
|
||||
int wav_restart(int id) { return audio_restart(&wav, id); }
|
||||
int bgm_restart(int id) { return audio_restart(&bgm, id); }
|
||||
|
||||
static bool audio_is_paused(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_is_paused(ch);
|
||||
}
|
||||
bool wav_is_paused(int id) { return audio_is_paused(&wav, id); }
|
||||
bool bgm_is_paused(int id) { return audio_is_paused(&bgm, id); }
|
||||
|
||||
static int audio_get_pos(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_get_pos(ch);
|
||||
}
|
||||
int wav_get_pos(int id) { return audio_get_pos(&wav, id); }
|
||||
int bgm_get_pos(int id) { return audio_get_pos(&bgm, id); }
|
||||
|
||||
static int audio_get_length(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_get_length(ch);
|
||||
}
|
||||
int wav_get_length(int id) { return audio_get_length(&wav, id); }
|
||||
int bgm_get_length(int id) { return audio_get_length(&bgm, id); }
|
||||
|
||||
static int audio_get_sample_pos(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_get_sample_pos(ch);
|
||||
}
|
||||
int wav_get_sample_pos(int id) { return audio_get_sample_pos(&wav, id); }
|
||||
int bgm_get_sample_pos(int id) { return audio_get_sample_pos(&bgm, id); }
|
||||
|
||||
static int audio_get_sample_length(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_get_sample_length(ch);
|
||||
}
|
||||
int wav_get_sample_length(int id) { return audio_get_sample_length(&wav, id); }
|
||||
int bgm_get_sample_length(int id) { return audio_get_sample_length(&bgm, id); }
|
||||
|
||||
static int audio_seek(struct id_pool *pool, int id, int pos)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_seek(ch, pos);
|
||||
}
|
||||
int wav_seek(int id, int pos) { return audio_seek(&wav, id, pos); }
|
||||
int bgm_seek(int id, int pos) { return audio_seek(&bgm, id, pos); }
|
||||
|
||||
int wav_get_unused_channel(void) { return id_pool_get_unused(&wav); }
|
||||
int bgm_get_unused_channel(void) { return id_pool_get_unused(&bgm); }
|
||||
|
||||
static int audio_reverse_LR(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_reverse_LR(ch);
|
||||
}
|
||||
int wav_reverse_LR(int id) { return audio_reverse_LR(&wav, id); }
|
||||
int bgm_reverse_LR(int id) { return audio_reverse_LR(&bgm, id); }
|
||||
|
||||
static int audio_get_volume(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_get_volume(ch);
|
||||
}
|
||||
int wav_get_volume(int id) { return audio_get_volume(&wav, id); }
|
||||
int bgm_get_volume(int id) { return audio_get_volume(&bgm, id); }
|
||||
|
||||
static int audio_get_time_length(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
return channel_get_time_length(ch);
|
||||
}
|
||||
int wav_get_time_length(int id) { return audio_get_time_length(&wav, id); }
|
||||
int bgm_get_time_length(int id) { return audio_get_time_length(&bgm, id); }
|
||||
|
||||
//int wav_get_group_num(int channel);
|
||||
//int bgm_get_group_num(int channel);
|
||||
//int wav_prepare_from_file(int channel, char *filename);
|
||||
//int bgm_prepare_from_file(int channel, char *filename);
|
||||
|
||||
@@ -0,0 +1,501 @@
|
||||
/* 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 <stdatomic.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sndfile.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/archive.h"
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "audio.h"
|
||||
#include "mixer.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
/*
|
||||
* The actual mixer implementation is contained in this header.
|
||||
* The rest of this file implements a high level interface for managing mixer
|
||||
* channels (loading audio, starting/stopping, etc.)
|
||||
*/
|
||||
#define STS_MIXER_IMPLEMENTATION
|
||||
#include "sts_mixer.h"
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
struct fade {
|
||||
atomic_bool fading;
|
||||
bool stop;
|
||||
uint_least32_t start_pos;
|
||||
uint_least32_t end_pos;
|
||||
float start_volume;
|
||||
float end_volume;
|
||||
};
|
||||
|
||||
struct channel {
|
||||
// archive data
|
||||
struct archive_data *dfile;
|
||||
int no;
|
||||
|
||||
// audio file data
|
||||
SNDFILE *file;
|
||||
SF_INFO info;
|
||||
sf_count_t offset;
|
||||
|
||||
// stream data
|
||||
atomic_int voice;
|
||||
sts_mixer_stream_t stream;
|
||||
float data[CHUNK_SIZE * 2];
|
||||
|
||||
// main thread read-only
|
||||
atomic_uint_least32_t frame;
|
||||
atomic_uint volume;
|
||||
|
||||
atomic_bool swapped;
|
||||
uint_least32_t loop_start;
|
||||
uint_least32_t loop_end;
|
||||
atomic_uint loop_count;
|
||||
struct fade fade;
|
||||
};
|
||||
|
||||
static SDL_AudioDeviceID audio_device = 0;
|
||||
static sts_mixer_t mixer;
|
||||
|
||||
/*
|
||||
* The SDL2 audio callback.
|
||||
*/
|
||||
static void audio_callback(possibly_unused void *data, Uint8 *stream, int len)
|
||||
{
|
||||
sts_mixer_mix_audio(&mixer, stream, len / (sizeof(float) * 2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Seek to the specified position in the stream.
|
||||
* Returns true if the seek succeeded, otherwise returns false.
|
||||
*/
|
||||
static bool cb_seek(struct channel *ch, uint_least32_t pos)
|
||||
{
|
||||
if (pos > ch->info.frames) {
|
||||
pos = ch->info.frames;
|
||||
}
|
||||
sf_count_t r = sf_seek(ch->file, pos, SEEK_SET);
|
||||
if (r < 0) {
|
||||
WARNING("sf_seek failed");
|
||||
return false;
|
||||
}
|
||||
ch->frame = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Seek to loop start, if the stream should loop.
|
||||
* Returns true if the stream should loop, false if it should stop.
|
||||
*/
|
||||
static bool cb_loop(struct channel *ch)
|
||||
{
|
||||
if (!cb_seek(ch, ch->loop_start) || ch->loop_count == 1) {
|
||||
ch->voice = -1;
|
||||
return false;
|
||||
}
|
||||
if (ch->loop_count > 1) {
|
||||
ch->loop_count--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to refill the stream's audio data.
|
||||
* Called from sys_mixer_mix_audio.
|
||||
*/
|
||||
//static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count)
|
||||
{
|
||||
sf_count_t num_read = 0;
|
||||
|
||||
// handle case where chunk crosses loop point (seamless)
|
||||
// NOTE: it's assumed that the length of the loop is greater than the chunk length
|
||||
if (ch->frame + frame_count >= ch->loop_end) {
|
||||
// read frames up to loop_end
|
||||
num_read = sf_readf_float(ch->file, out, ch->loop_end - ch->frame);
|
||||
// adjust parameters for later
|
||||
ch->frame += num_read;
|
||||
out += num_read;
|
||||
frame_count -= num_read;
|
||||
// seek to loop_start
|
||||
if (!cb_loop(ch))
|
||||
return STS_STREAM_COMPLETE;
|
||||
} else if (ch->frame >= ch->loop_end) {
|
||||
// seek to loop_start
|
||||
if (!cb_loop(ch))
|
||||
return STS_STREAM_COMPLETE;
|
||||
}
|
||||
|
||||
// read remaining data
|
||||
sf_count_t n = sf_readf_float(ch->file, out, frame_count);
|
||||
num_read += n;
|
||||
ch->frame += n;
|
||||
out += num_read;
|
||||
frame_count -= num_read;
|
||||
|
||||
// XXX: This *shouldn't* be necessary, but sometimes libsndfile seems to stop reading
|
||||
// just before the end of file (i.e. ch->frame + frame_count is < ch->info.frames,
|
||||
// yet sf_readf_float returns less that the requested amount of frames).
|
||||
// Not sure if this is a bug in xsystem4 or libsndfile
|
||||
if (frame_count > 0) {
|
||||
if (!cb_loop(ch))
|
||||
return STS_STREAM_COMPLETE;
|
||||
num_read += sf_readf_float(ch->file, out, frame_count);
|
||||
}
|
||||
|
||||
return STS_STREAM_CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the gain for a fade at a given sample.
|
||||
*/
|
||||
static float cb_calc_fade(struct fade *fade, uint_least32_t sample)
|
||||
{
|
||||
if (sample >= fade->end_pos) {
|
||||
return fade->stop ? 0.0 : fade->end_volume;
|
||||
}
|
||||
|
||||
float progress = (float)(sample - fade->start_pos) / (float)(fade->end_pos - fade->start_pos);
|
||||
float delta_v = fade->end_volume - fade->start_volume;
|
||||
float gain = fade->start_volume + delta_v * progress;
|
||||
if (gain < 0.0) return 0.0;
|
||||
if (gain > 1.0) return 1.0;
|
||||
return gain;
|
||||
}
|
||||
|
||||
static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
{
|
||||
struct channel *ch = data;
|
||||
memset(ch->data, 0, sizeof(float) * sample->length);
|
||||
|
||||
// read audio data from file
|
||||
int r = cb_read_frames(ch, ch->data, CHUNK_SIZE);
|
||||
|
||||
// convert mono to stereo
|
||||
if (ch->info.channels == 1) {
|
||||
for (int i = CHUNK_SIZE-1; i >= 0; i--) {
|
||||
ch->data[i*2+1] = ch->data[i];
|
||||
ch->data[i*2] = ch->data[i];
|
||||
}
|
||||
}
|
||||
|
||||
// reverse LR channels
|
||||
else if (ch->swapped) {
|
||||
for (int i = 0; i < CHUNK_SIZE; i++) {
|
||||
float tmp = ch->data[i*2];
|
||||
ch->data[i*2] = ch->data[i*2+1];
|
||||
ch->data[i*2+1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// set gain for fade
|
||||
if (ch->fade.fading) {
|
||||
float gain = cb_calc_fade(&ch->fade, ch->frame);
|
||||
mixer.voices[ch->voice].gain = gain;
|
||||
ch->volume = gain * 100.0;
|
||||
|
||||
if (ch->frame >= ch->fade.end_pos) {
|
||||
ch->fade.fading = false;
|
||||
if (ch->fade.stop) {
|
||||
r = STS_STREAM_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int channel_play(struct channel *ch)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
if (ch->voice >= 0) {
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 0;
|
||||
}
|
||||
ch->voice = sts_mixer_play_stream(&mixer, &ch->stream, 1.0f);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_stop(struct channel *ch)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
if (ch->voice < 0) {
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 0;
|
||||
}
|
||||
sts_mixer_stop_voice(&mixer, ch->voice);
|
||||
ch->voice = -1;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_is_playing(struct channel *ch)
|
||||
{
|
||||
return ch->voice >= 0;
|
||||
}
|
||||
|
||||
int channel_set_loop_count(struct channel *ch, int count)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
ch->loop_count = count;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_get_loop_count(struct channel *ch)
|
||||
{
|
||||
return ch->loop_count;
|
||||
}
|
||||
|
||||
int channel_set_loop_start_pos(struct channel *ch, int pos)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
ch->loop_start = pos;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_set_loop_end_pos(struct channel *ch, int pos)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
ch->loop_end = pos;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_fade(struct channel *ch, int time, int volume, bool stop)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
ch->fade.fading = true;
|
||||
ch->fade.stop = stop;
|
||||
ch->fade.start_pos = ch->frame;
|
||||
ch->fade.start_volume = (float)ch->volume / 100.0;
|
||||
ch->fade.end_pos = ch->fade.start_pos + ((time * ch->info.samplerate) / 1000);
|
||||
ch->fade.end_volume = min(1.0, max(0.0, (float)volume / 100.0));
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_stop_fade(struct channel *ch)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
// XXX: we need to set the volume to end_volume and potentially stop the
|
||||
// stream here; better to let the callback do it
|
||||
ch->fade.end_pos = ch->frame;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_is_fading(struct channel *ch)
|
||||
{
|
||||
return ch->fade.fading;
|
||||
}
|
||||
|
||||
int channel_pause(possibly_unused struct channel *ch)
|
||||
{
|
||||
// NOTE: not implemented in Sengoku Rance's SACT2.dll
|
||||
WARNING("channel_pause not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int channel_restart(possibly_unused struct channel *ch)
|
||||
{
|
||||
// NOTE: not implemented in Sengoku Rance's SACT2.dll
|
||||
WARNING("channel_restart not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int channel_is_paused(possibly_unused struct channel *ch)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int channel_get_pos(struct channel *ch)
|
||||
{
|
||||
return (ch->frame * 1000) / ch->info.samplerate;
|
||||
}
|
||||
|
||||
int channel_get_length(struct channel *ch)
|
||||
{
|
||||
// FIXME: how is this different than channel_get_time_length?
|
||||
return (ch->info.frames * 1000) / ch->info.samplerate;
|
||||
}
|
||||
|
||||
int channel_get_sample_pos(struct channel *ch)
|
||||
{
|
||||
return ch->frame;
|
||||
}
|
||||
|
||||
int channel_get_sample_length(struct channel *ch)
|
||||
{
|
||||
return ch->info.frames;
|
||||
}
|
||||
|
||||
int channel_seek(struct channel *ch, int pos)
|
||||
{
|
||||
// NOTE: SACT2.Music_Seek doesn't seem to do anything in Sengoku Rance...
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
int r = cb_seek(ch, (pos * ch->info.samplerate) / 1000);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return r;
|
||||
}
|
||||
|
||||
int channel_reverse_LR(struct channel *ch)
|
||||
{
|
||||
ch->swapped = !ch->swapped;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel_get_volume(struct channel *ch)
|
||||
{
|
||||
return ch->volume;
|
||||
}
|
||||
|
||||
int channel_get_time_length(struct channel *ch)
|
||||
{
|
||||
return (ch->info.frames * 1000) / ch->info.samplerate;
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_get_filelen(void *data)
|
||||
{
|
||||
return ((struct channel*)data)->dfile->size;
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_seek(sf_count_t offset, int whence, void *data)
|
||||
{
|
||||
struct channel *ch = data;
|
||||
switch (whence) {
|
||||
case SEEK_CUR:
|
||||
ch->offset += offset;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
ch->offset = offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
ch->offset = ch->dfile->size + offset;
|
||||
break;
|
||||
}
|
||||
ch->offset = min((sf_count_t)ch->dfile->size, max(0, ch->offset));
|
||||
return ch->offset;
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_read(void *ptr, sf_count_t count, void *data)
|
||||
{
|
||||
struct channel *ch = data;
|
||||
sf_count_t c = min(count, (sf_count_t)ch->dfile->size - ch->offset);
|
||||
memcpy(ptr, ch->dfile->data + ch->offset, c);
|
||||
ch->offset += c;
|
||||
return c;
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_write(possibly_unused const void *ptr,
|
||||
possibly_unused sf_count_t count,
|
||||
possibly_unused void *user_data)
|
||||
{
|
||||
ERROR("sndfile vio write not supported");
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_tell(void *data)
|
||||
{
|
||||
return ((struct channel*)data)->offset;
|
||||
}
|
||||
|
||||
static SF_VIRTUAL_IO channel_vio = {
|
||||
.get_filelen = channel_vio_get_filelen,
|
||||
.seek = channel_vio_seek,
|
||||
.read = channel_vio_read,
|
||||
.write = channel_vio_write,
|
||||
.tell = channel_vio_tell
|
||||
};
|
||||
|
||||
struct channel *channel_open(enum asset_type type, int no)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
|
||||
// get file from archive
|
||||
ch->dfile = asset_get(type, no);
|
||||
if (!ch->dfile) {
|
||||
WARNING("Failed to load %s %d", type == ASSET_SOUND ? "WAV" : "BGM", no);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// open file
|
||||
ch->file = sf_open_virtual(&channel_vio, SFM_READ, &ch->info, ch);
|
||||
if (sf_error(ch->file) != SF_ERR_NO_ERROR) {
|
||||
WARNING("Failed to open %s %d: %s", type == ASSET_SOUND ? "WAV" : "BGM", no, sf_strerror(ch->file));
|
||||
goto error;
|
||||
}
|
||||
if (ch->info.channels > 2) {
|
||||
WARNING("Audio file has more than 2 channels: %s %d", type == ASSET_SOUND ? "WAV" : "BGM", no);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// create stream
|
||||
ch->stream.userdata = ch;
|
||||
ch->stream.callback = refill_stream;
|
||||
ch->stream.sample.frequency = ch->info.samplerate;
|
||||
ch->stream.sample.audio_format = STS_MIXER_SAMPLE_FORMAT_FLOAT;
|
||||
ch->stream.sample.length = CHUNK_SIZE * 2;
|
||||
ch->stream.sample.data = ch->data;
|
||||
//refill_stream(&ch->stream.sample, ch);
|
||||
ch->voice = -1;
|
||||
|
||||
ch->volume = 100;
|
||||
ch->loop_start = 0;
|
||||
ch->loop_end = ch->info.frames;
|
||||
ch->loop_count = type == ASSET_SOUND ? 1 : 0;
|
||||
ch->no = no;
|
||||
|
||||
return ch;
|
||||
|
||||
error:
|
||||
if (ch->dfile)
|
||||
archive_free_data(ch->dfile);
|
||||
free(ch);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void channel_close(struct channel *ch)
|
||||
{
|
||||
channel_stop(ch);
|
||||
sf_close(ch->file);
|
||||
archive_free_data(ch->dfile);
|
||||
free(ch);
|
||||
}
|
||||
|
||||
void mixer_init(void)
|
||||
{
|
||||
SDL_AudioSpec have;
|
||||
SDL_AudioSpec want = {
|
||||
.format = AUDIO_F32,
|
||||
.freq = 44100,
|
||||
.channels = 2,
|
||||
.samples = CHUNK_SIZE,
|
||||
.callback = audio_callback,
|
||||
};
|
||||
audio_device = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
|
||||
|
||||
sts_mixer_init(&mixer, 44100, STS_MIXER_SAMPLE_FORMAT_FLOAT);
|
||||
|
||||
SDL_PauseAudioDevice(audio_device, 0);
|
||||
}
|
||||
+33
-91
@@ -19,51 +19,18 @@
|
||||
#include "hll.h"
|
||||
#include "audio.h"
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, KiwiSoundEngine, SetGlobalFocus, int nNum);
|
||||
HLL_WARN_UNIMPLEMENTED( , void, KiwiSoundEngine, SetGlobalFocus, possibly_unused int nNum);
|
||||
|
||||
int KiwiSoundEngine_Music_IsExist(int nNum)
|
||||
{
|
||||
return bgm_exists(nNum-1);
|
||||
}
|
||||
|
||||
int KiwiSoundEngine_Music_Prepare(int nCh, int nNum)
|
||||
{
|
||||
return bgm_prepare(nCh, nNum-1);
|
||||
}
|
||||
int KiwiSoundEngine_Music_Unprepare(int nCh)
|
||||
{
|
||||
return bgm_unprepare(nCh);
|
||||
}
|
||||
int KiwiSoundEngine_Music_Play(int nCh)
|
||||
{
|
||||
return bgm_play(nCh);
|
||||
}
|
||||
int KiwiSoundEngine_Music_Stop(int nCh)
|
||||
{
|
||||
return bgm_stop(nCh);
|
||||
}
|
||||
int KiwiSoundEngine_Music_IsPlay(int nCh)
|
||||
{
|
||||
return bgm_is_playing(nCh);
|
||||
}
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_SetLoopCount, int nCh, int nCount);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_GetLoopCount, int nCh);
|
||||
//int KiwiSoundEngine_Music_SetLoopStartPos(int nCh, int dwPos);
|
||||
//int KiwiSoundEngine_Music_SetLoopEndPos(int nCh, int dwPos);
|
||||
int KiwiSoundEngine_Music_Fade(int nCh, int nTime, int nVolume, int bStop)
|
||||
{
|
||||
return bgm_fade(nCh, nTime, nVolume, bStop);
|
||||
}
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_StopFade, int nCh);
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, KiwiSoundEngine, Music_IsFade, int nCh);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_Pause, int nCh);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_Restart, int nCh);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_IsPause, int nCh);
|
||||
HLL_QUIET_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_GetPos, int nCh);
|
||||
HLL_QUIET_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_GetLength, int nCh);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_GetSamplePos, int nCh);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_GetSampleLength, int nCh);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, KiwiSoundEngine, Music_Seek, int nCh, int dwPos);
|
||||
//int KiwiSoundEngine_Music_MillisecondsToSamples(int nMillisec, int nSamplesPerSec);
|
||||
//int KiwiSoundEngine_Music_GetFormat(int nCh, int *pnSamplesPerSec, int *pnBitsPerSample, int *pnChannels);
|
||||
|
||||
int KiwiSoundEngine_Sound_IsExist(int nNum)
|
||||
{
|
||||
return wav_exists(nNum-1);
|
||||
@@ -72,34 +39,9 @@ int KiwiSoundEngine_Sound_Prepare(int nCh, int nNum)
|
||||
{
|
||||
return wav_prepare(nCh, nNum-1);
|
||||
}
|
||||
int KiwiSoundEngine_Sound_Unprepare(int nCh)
|
||||
{
|
||||
return wav_unprepare(nCh);
|
||||
}
|
||||
int KiwiSoundEngine_Sound_Play(int nCh)
|
||||
{
|
||||
return wav_play(nCh);
|
||||
}
|
||||
int KiwiSoundEngine_Sound_Stop(int nCh)
|
||||
{
|
||||
return wav_stop(nCh);
|
||||
}
|
||||
int KiwiSoundEngine_Sound_IsPlay(int nCh)
|
||||
{
|
||||
return wav_is_playing(nCh);
|
||||
}
|
||||
int KiwiSoundEngine_Sound_Fade(int nCh, int nTime, int nVolume, int bStop)
|
||||
{
|
||||
return wav_fade(nCh, nTime, nVolume, bStop);
|
||||
}
|
||||
//int KiwiSoundEngine_Sound_StopFade(int nCh);
|
||||
//int KiwiSoundEngine_Sound_IsFade(int nCh);
|
||||
int KiwiSoundEngine_Sound_GetTimeLength(int nCh)
|
||||
{
|
||||
return wav_get_time_length(nCh);
|
||||
}
|
||||
|
||||
//int KiwiSoundEngine_Sound_GetGroupNum(int nCh);
|
||||
static int KiwiSoundEngine_Sound_GetGroupNumFromDataNum(int no)
|
||||
static int KiwiSoundEngine_Sound_GetGroupNumFromDataNum(possibly_unused int no)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -110,37 +52,37 @@ HLL_LIBRARY(KiwiSoundEngine,
|
||||
HLL_EXPORT(SetGlobalFocus, KiwiSoundEngine_SetGlobalFocus),
|
||||
HLL_EXPORT(Music_IsExist, KiwiSoundEngine_Music_IsExist),
|
||||
HLL_EXPORT(Music_Prepare, KiwiSoundEngine_Music_Prepare),
|
||||
HLL_EXPORT(Music_Unprepare, KiwiSoundEngine_Music_Unprepare),
|
||||
HLL_EXPORT(Music_Play, KiwiSoundEngine_Music_Play),
|
||||
HLL_EXPORT(Music_Stop, KiwiSoundEngine_Music_Stop),
|
||||
HLL_EXPORT(Music_IsPlay, KiwiSoundEngine_Music_IsPlay),
|
||||
HLL_EXPORT(Music_SetLoopCount, KiwiSoundEngine_Music_SetLoopCount),
|
||||
HLL_EXPORT(Music_GetLoopCount, KiwiSoundEngine_Music_GetLoopCount),
|
||||
//HLL_EXPORT(Music_SetLoopStartPos, KiwiSoundEngine_Music_SetLoopStartPos),
|
||||
//HLL_EXPORT(Music_SetLoopEndPos, KiwiSoundEngine_Music_SetLoopEndPos),
|
||||
HLL_EXPORT(Music_Fade, KiwiSoundEngine_Music_Fade),
|
||||
HLL_EXPORT(Music_StopFade, KiwiSoundEngine_Music_StopFade),
|
||||
HLL_EXPORT(Music_IsFade, KiwiSoundEngine_Music_IsFade),
|
||||
HLL_EXPORT(Music_Pause, KiwiSoundEngine_Music_Pause),
|
||||
HLL_EXPORT(Music_Restart, KiwiSoundEngine_Music_Restart),
|
||||
HLL_EXPORT(Music_IsPause, KiwiSoundEngine_Music_IsPause),
|
||||
HLL_EXPORT(Music_GetPos, KiwiSoundEngine_Music_GetPos),
|
||||
HLL_EXPORT(Music_GetLength, KiwiSoundEngine_Music_GetLength),
|
||||
HLL_EXPORT(Music_GetSamplePos, KiwiSoundEngine_Music_GetSamplePos),
|
||||
HLL_EXPORT(Music_GetSampleLength, KiwiSoundEngine_Music_GetSampleLength),
|
||||
HLL_EXPORT(Music_Seek, KiwiSoundEngine_Music_Seek),
|
||||
HLL_EXPORT(Music_Unprepare, bgm_unprepare),
|
||||
HLL_EXPORT(Music_Play, bgm_play),
|
||||
HLL_EXPORT(Music_Stop, bgm_stop),
|
||||
HLL_EXPORT(Music_IsPlay, bgm_is_playing),
|
||||
HLL_EXPORT(Music_SetLoopCount, bgm_set_loop_count),
|
||||
HLL_EXPORT(Music_GetLoopCount, bgm_get_loop_count),
|
||||
HLL_EXPORT(Music_SetLoopStartPos, bgm_set_loop_start_pos),
|
||||
HLL_EXPORT(Music_SetLoopEndPos, bgm_set_loop_end_pos),
|
||||
HLL_EXPORT(Music_Fade, bgm_fade),
|
||||
HLL_EXPORT(Music_StopFade, bgm_stop_fade),
|
||||
HLL_EXPORT(Music_IsFade, bgm_is_fading),
|
||||
HLL_EXPORT(Music_Pause, bgm_pause),
|
||||
HLL_EXPORT(Music_Restart, bgm_restart),
|
||||
HLL_EXPORT(Music_IsPause, bgm_is_paused),
|
||||
HLL_EXPORT(Music_GetPos, bgm_get_pos),
|
||||
HLL_EXPORT(Music_GetLength, bgm_get_length),
|
||||
HLL_EXPORT(Music_GetSamplePos, bgm_get_sample_pos),
|
||||
HLL_EXPORT(Music_GetSampleLength, bgm_get_sample_length),
|
||||
HLL_EXPORT(Music_Seek, bgm_seek),
|
||||
//HLL_EXPORT(Music_MillisecondsToSamples, KiwiSoundEngine_Music_MillisecondsToSamples),
|
||||
//HLL_EXPORT(Music_GetFormat, KiwiSoundEngine_Music_GetFormat),
|
||||
HLL_EXPORT(Sound_IsExist, KiwiSoundEngine_Sound_IsExist),
|
||||
HLL_EXPORT(Sound_Prepare, KiwiSoundEngine_Sound_Prepare),
|
||||
HLL_EXPORT(Sound_Unprepare, KiwiSoundEngine_Sound_Unprepare),
|
||||
HLL_EXPORT(Sound_Play, KiwiSoundEngine_Sound_Play),
|
||||
HLL_EXPORT(Sound_Stop, KiwiSoundEngine_Sound_Stop),
|
||||
HLL_EXPORT(Sound_IsPlay, KiwiSoundEngine_Sound_IsPlay),
|
||||
HLL_EXPORT(Sound_Fade, KiwiSoundEngine_Sound_Fade),
|
||||
//HLL_EXPORT(Sound_StopFade, KiwiSoundEngine_Sound_StopFade),
|
||||
//HLL_EXPORT(Sound_IsFade, KiwiSoundEngine_Sound_IsFade),
|
||||
HLL_EXPORT(Sound_GetTimeLength, KiwiSoundEngine_Sound_GetTimeLength),
|
||||
HLL_EXPORT(Sound_Unprepare, wav_unprepare),
|
||||
HLL_EXPORT(Sound_Play, wav_play),
|
||||
HLL_EXPORT(Sound_Stop, wav_stop),
|
||||
HLL_EXPORT(Sound_IsPlay, wav_is_playing),
|
||||
HLL_EXPORT(Sound_Fade, wav_fade),
|
||||
HLL_EXPORT(Sound_StopFade, wav_stop_fade),
|
||||
HLL_EXPORT(Sound_IsFade, wav_is_fading),
|
||||
HLL_EXPORT(Sound_GetTimeLength, wav_get_time_length),
|
||||
//HLL_EXPORT(Sound_GetGroupNum, KiwiSoundEngine_Sound_GetGroupNum),
|
||||
HLL_EXPORT(Sound_GetGroupNumFromDataNum, KiwiSoundEngine_Sound_GetGroupNumFromDataNum)
|
||||
//HLL_EXPORT(Sound_PrepareFromFile, KiwiSoundEngine_Sound_PrepareFromFile)
|
||||
|
||||
+21
-37
@@ -723,22 +723,6 @@ int sact_Music_Prepare(int ch, int n)
|
||||
return bgm_prepare(ch, n - 1);
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_SetLoopCount, int ch, int count);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_GetLoopCount, int ch);
|
||||
//int SACT2_Music_SetLoopStartPos(int ch, int pos);
|
||||
//int SACT2_Music_SetLoopEndPos(int ch, int pos);
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_StopFade, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, SACT2, Music_IsFade, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_Pause, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_Restart, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_IsPause, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_GetPos, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_GetLength, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_GetSamplePos, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_GetSampleLength, int ch);
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, SACT2, Music_Seek, int ch, int pos);
|
||||
|
||||
int sact_Sound_IsExist(int n)
|
||||
{
|
||||
return wav_exists(n - 1);
|
||||
@@ -914,21 +898,21 @@ int SACT2_SP_GetBrightness(int sp_no)
|
||||
HLL_EXPORT(Music_Play, bgm_play), \
|
||||
HLL_EXPORT(Music_Stop, bgm_stop), \
|
||||
HLL_EXPORT(Music_IsPlay, bgm_is_playing), \
|
||||
HLL_EXPORT(Music_SetLoopCount, SACT2_Music_SetLoopCount), \
|
||||
HLL_EXPORT(Music_GetLoopCount, SACT2_Music_GetLoopCount), \
|
||||
HLL_TODO_EXPORT(Music_SetLoopStartPos, SACT2_Music_SetLoopStartPos), \
|
||||
HLL_TODO_EXPORT(Music_SetLoopEndPos, SACT2_Music_SetLoopEndPos), \
|
||||
HLL_EXPORT(Music_SetLoopCount, bgm_set_loop_count), \
|
||||
HLL_EXPORT(Music_GetLoopCount, bgm_get_loop_count), \
|
||||
HLL_EXPORT(Music_SetLoopStartPos, bgm_set_loop_start_pos), \
|
||||
HLL_EXPORT(Music_SetLoopEndPos, bgm_set_loop_end_pos), \
|
||||
HLL_EXPORT(Music_Fade, bgm_fade), \
|
||||
HLL_EXPORT(Music_StopFade, SACT2_Music_StopFade), \
|
||||
HLL_EXPORT(Music_IsFade, SACT2_Music_IsFade), \
|
||||
HLL_EXPORT(Music_Pause, SACT2_Music_Pause), \
|
||||
HLL_EXPORT(Music_Restart, SACT2_Music_Restart), \
|
||||
HLL_EXPORT(Music_IsPause, SACT2_Music_IsPause), \
|
||||
HLL_EXPORT(Music_GetPos, SACT2_Music_GetPos), \
|
||||
HLL_EXPORT(Music_GetLength, SACT2_Music_GetLength), \
|
||||
HLL_EXPORT(Music_GetSamplePos, SACT2_Music_GetSamplePos), \
|
||||
HLL_EXPORT(Music_GetSampleLength, SACT2_Music_GetSampleLength), \
|
||||
HLL_EXPORT(Music_Seek, SACT2_Music_Seek), \
|
||||
HLL_EXPORT(Music_StopFade, bgm_stop_fade), \
|
||||
HLL_EXPORT(Music_IsFade, bgm_is_fading), \
|
||||
HLL_EXPORT(Music_Pause, bgm_pause), \
|
||||
HLL_EXPORT(Music_Restart, bgm_restart), \
|
||||
HLL_EXPORT(Music_IsPause, bgm_is_paused), \
|
||||
HLL_EXPORT(Music_GetPos, bgm_get_pos), \
|
||||
HLL_EXPORT(Music_GetLength, bgm_get_length), \
|
||||
HLL_EXPORT(Music_GetSamplePos, bgm_get_sample_pos), \
|
||||
HLL_EXPORT(Music_GetSampleLength, bgm_get_sample_length), \
|
||||
HLL_EXPORT(Music_Seek, bgm_seek), \
|
||||
HLL_EXPORT(Sound_IsExist, sact_Sound_IsExist), \
|
||||
HLL_EXPORT(Sound_GetUnuseChannel, wav_get_unused_channel), \
|
||||
HLL_EXPORT(Sound_Prepare, sact_Sound_Prepare), \
|
||||
@@ -936,15 +920,15 @@ int SACT2_SP_GetBrightness(int sp_no)
|
||||
HLL_EXPORT(Sound_Play, wav_play), \
|
||||
HLL_EXPORT(Sound_Stop, wav_stop), \
|
||||
HLL_EXPORT(Sound_IsPlay, wav_is_playing), \
|
||||
HLL_TODO_EXPORT(Sound_SetLoopCount, SACT2_Sound_SetLoopCount), \
|
||||
HLL_TODO_EXPORT(Sound_GetLoopCount, SACT2_Sound_GetLoopCount), \
|
||||
HLL_TODO_EXPORT(Sound_SetLoopCount, wav_set_loop_count), \
|
||||
HLL_TODO_EXPORT(Sound_GetLoopCount, wav_get_loop_count), \
|
||||
HLL_EXPORT(Sound_Fade, wav_fade), \
|
||||
HLL_TODO_EXPORT(Sound_StopFade, SACT2_Sound_StopFade), \
|
||||
HLL_TODO_EXPORT(Sound_IsFade, SACT2_Sound_IsFade), \
|
||||
HLL_TODO_EXPORT(Sound_GetPos, SACT2_Sound_GetPos), \
|
||||
HLL_TODO_EXPORT(Sound_GetLength, SACT2_Sound_GetLength), \
|
||||
HLL_TODO_EXPORT(Sound_StopFade, wav_stop_fade), \
|
||||
HLL_TODO_EXPORT(Sound_IsFade, wav_is_fade), \
|
||||
HLL_TODO_EXPORT(Sound_GetPos, wav_get_pos), \
|
||||
HLL_TODO_EXPORT(Sound_GetLength, wav_get_length), \
|
||||
HLL_EXPORT(Sound_ReverseLR, wav_reverse_LR), \
|
||||
HLL_TODO_EXPORT(Sound_GetVolume, SACT2_Sound_GetVolume), \
|
||||
HLL_TODO_EXPORT(Sound_GetVolume, wav_get_volume), \
|
||||
HLL_EXPORT(Sound_GetTimeLength, wav_get_time_length), \
|
||||
HLL_TODO_EXPORT(Sound_GetGroupNum, SACT2_Sound_GetGroupNum), \
|
||||
HLL_TODO_EXPORT(Sound_PrepareFromFile, SACT2_Sound_PrepareFromFile), \
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/* 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 <assert.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "id_pool.h"
|
||||
|
||||
/*
|
||||
* Generic data structure for managing a pool of objects accessed by integer IDs.
|
||||
*/
|
||||
|
||||
#define ID_POOL_INIT_SLOTS 32
|
||||
#define ID_POOL_ALLOC_HEADROOM 256
|
||||
|
||||
void id_pool_init(struct id_pool *pool)
|
||||
{
|
||||
pool->slots = xcalloc(ID_POOL_INIT_SLOTS, sizeof(void*));
|
||||
pool->nr_slots = ID_POOL_INIT_SLOTS;
|
||||
}
|
||||
|
||||
static void id_pool_realloc(struct id_pool *pool, int new_size)
|
||||
{
|
||||
pool->slots = xrealloc_array(pool->slots, pool->nr_slots, new_size, sizeof(void*));
|
||||
pool->nr_slots = new_size;
|
||||
}
|
||||
|
||||
void id_pool_release(struct id_pool *pool, int id)
|
||||
{
|
||||
if (pool->slots[id])
|
||||
pool->slots[id] = NULL;
|
||||
}
|
||||
|
||||
int id_pool_get_unused(struct id_pool *pool)
|
||||
{
|
||||
for (int i = 0; i < pool->nr_slots; i++) {
|
||||
if (!pool->slots[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
int id = pool->nr_slots;
|
||||
id_pool_realloc(pool, id + ID_POOL_ALLOC_HEADROOM);
|
||||
return id;
|
||||
}
|
||||
|
||||
void *id_pool_get(struct id_pool *pool, int id)
|
||||
{
|
||||
if (id < 0 || id >= pool->nr_slots)
|
||||
return NULL;
|
||||
return pool->slots[id];
|
||||
}
|
||||
|
||||
void *id_pool_set(struct id_pool *pool, int id, void *data)
|
||||
{
|
||||
assert(id >= 0);
|
||||
if (id >= pool->nr_slots)
|
||||
id_pool_realloc(pool, id + ID_POOL_ALLOC_HEADROOM);
|
||||
|
||||
void *old = pool->slots[id];
|
||||
pool->slots[id] = data;
|
||||
return old;
|
||||
}
|
||||
+4
-1
@@ -1,5 +1,6 @@
|
||||
# sources for xsystem4
|
||||
xsystem4 = ['audio.c',
|
||||
'audio_mixer.c',
|
||||
'asset_manager.c',
|
||||
'cJSON.c',
|
||||
'draw.c',
|
||||
@@ -8,6 +9,7 @@ xsystem4 = ['audio.c',
|
||||
'fnl_render.c',
|
||||
'format.c',
|
||||
'heap.c',
|
||||
'id_pool.c',
|
||||
'input.c',
|
||||
'page.c',
|
||||
'resume.c',
|
||||
@@ -77,7 +79,8 @@ xsystem4 = ['audio.c',
|
||||
'hll/VSFile.c',
|
||||
]
|
||||
|
||||
xsystem4_deps = [libm, zlib, sdl2, sdl2ttf, sdl2mixer, ffi, gl, glew, cglm, libsys4_dep]
|
||||
xsystem4_deps = [libm, zlib, sdl2, sdl2ttf, ffi, gl, glew, cglm, sndfile, libsys4_dep]
|
||||
|
||||
if chibi.found()
|
||||
xsystem4_deps += chibi
|
||||
xsystem4 += 'debugger.c'
|
||||
|
||||
Reference in New Issue
Block a user