Archived
1
0
Fork 0
This repository has been archived on 2025-09-02. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
skcblitz/sound.h

310 lines
7.1 KiB
C
Raw Permalink Normal View History

2025-09-02 14:43:36 +02:00
typedef struct {
const char *devname;
uint samplerate;
uint frames;
uint bufsize;
uint width;
} snd_devconfig;
enum snd_cmd {
SND_CMD_STOP,
SND_CMD_FLUSH,
SND_CMD_QUERY,
SND_CMD_RESET,
SND_CMD_MKOPL,
SND_CMD_VOICES,
// SND_CMD_BANK,
SND_CMD_PAUSE,
SND_CMD_LOAD,
SND_CMD_OFF,
SND_CMD_DEBUG,
SND_CMD_WAVECAP,
SND_CMD_VOLUME
};
enum snd_stat {
SND_STAT_RUN,
SND_STAT_STOPPED
};
ulong snd_process() {
ulong elapsed = 1000ULL;
if(!sgt.paused) {
if(sgt.mid.track && (sgt.playing ^ 1) && (sgt.nowait || !(sgt.chip) || !OPL3_Playing(sgt.chip))) {
// flush ?
if(sgt.mid.track) {
free(sgt.mid.track);
sgt.mid.track = NULL;
}
if(sgt.chip) {
OPL3_Reset(sgt.chip);
bank_reset(sgt.chip, sgt.bank);
}
sgt.mid_tick = 0;
sgt.mid_time = 0ULL;
sgt.stopped = 1;
}
else if(sgt.mid.track && sgt.playing && mid_tick(&sgt.mid, sgt.bank, sgt.chip)) {
elapsed = ((ulong)sgt.mid.ticktime) * 1000ULL;
sgt.mid_tick += 1;
sgt.mid_time += ((ulong)sgt.mid.ticktime);
}
else if(sgt.playing) {
sgt.playing = 0;
if(sgt.bank)
bank_alloff(sgt.bank);
}
}
// sampling ...
return elapsed;
}
void snd_generate(short *sample) {
int mix[2];
if(!sgt.paused && sgt.chip)
OPL3_GenerateResampled(sgt.chip, &sample[SND_VOL_MUSIC*2]);
else
sample[SND_VOL_MUSIC*2+0] = sample[SND_VOL_MUSIC*2+1] = 0;
// sampling ...
sample[SND_VOL_SFX*2+0] = sample[SND_VOL_SFX*2+1] = 0;
sample[SND_VOL_GUI*2+0] = sample[SND_VOL_GUI*2+1] = 0;
mix[0] = mix[1] = 0;
for(int z = 1; z < SND_VOLUMES; z++) {
mix[0] += (short)(int)(((int)sample[z*2+0] * (int)sgt.volumes[z]) / 32767);
mix[1] += (short)(int)(((int)sample[z*2+1] * (int)sgt.volumes[z]) / 32767);
}
sample[0] = (short)(int)((mix[0] * (int)sgt.volumes[0]) / 32767);
sample[1] = (short)(int)((mix[1] * (int)sgt.volumes[0]) / 32767);
}
void snd_dispatch(void *buf, short op, short addr, uint param, uint rate) {
switch(op) {
case SND_CMD_PAUSE:
sgt.paused = (byte)param;
break;
case SND_CMD_LOAD:
// mid_cmlog();
sgt.playing = 0;
if(sgt.mid.track) {
free(sgt.mid.track);
sgt.mid.track = NULL;
}
if(buf && sgt.chip) {
sgt.mid.tracks = param & 0xffff;
sgt.mid.tpqn = (param >> 16) & 0x7fff;
mid_settempo(&sgt.mid, MID_DEFTEMPO);
sgt.mid.track = (mid_track *)buf;
buf += (sizeof(mid_track) * sgt.mid.tracks);
for(int z = 0; z < sgt.mid.tracks; z++) {
sgt.mid.track[z].buffer = buf;
buf += sgt.mid.track[z].size;
}
sgt.playing = 1;
sgt.nowait = (byte)(param >> 31);
}
else if(buf) {
free(buf);
}
case SND_CMD_RESET:
if(sgt.chip) {
OPL3_Reset(sgt.chip);
bank_reset(sgt.chip, sgt.bank);
}
sgt.mid_tick = 0;
sgt.mid_time = 0ULL;
break;
case SND_CMD_OFF:
if(sgt.mid.track)
bank_alloff(sgt.bank);
break;
case SND_CMD_MKOPL:
if(sgt.chip) {
free(sgt.bank);
free(sgt.chip);
}
sgt.chip = param ? OPL3_Alloc(rate, param & 0xff) : NULL;
sgt.bank = param ? bank_alloc(sgt.chip, sgt.ibank, (param >> 16) & 1, (param >> 17) & 1, (param >> 8) & 0xff) : NULL;
sgt.stopped = param ? 1 : 0;
break;
case SND_CMD_VOICES:
if(buf) {
memcpy(sgt.ibank, buf, sizeof(bank_instr) * 256);
free(buf);
}
// else {
// memcpy(sgt.ibank, snd_banks[addr], sizeof(bank_instr) * 256);
// }
break;
// case SND_CMD_BANK:
// sgt.instr = addr >= 31 ? sgt.ibank : (bank_instr *)snd_banks[addr];
// break;
case SND_CMD_DEBUG:
sgt.log_debug = (byte)param;
break;
case SND_CMD_WAVECAP:
if(sgt.capture)
wav_close(&sgt.wav);
sgt.capture = buf && wav_open(&sgt.wav, (char *)buf, rate, 2, 2);
if(buf)
free(buf);
break;
case SND_CMD_VOLUME:
sgt.volumes[addr] = (ushort)param;
// snd_logd("V %d = %d", addr, param);
break;
}
}
uint snd_status(short addr, uint param) {
switch(addr) {
case SND_STAT_STOPPED:
param = sgt.stopped;
sgt.stopped = 0;
return param;
default:
return 0;
}
}
void *snd_loop(void *arg) {
snd_devconfig *cfg = (snd_devconfig*)arg;
aud_handle handle;
uint pop = 0;
short sample[2 * SND_VOLUMES];
uint rate = strcmp(cfg->devname, "none") ? aud_open_dev(&handle, cfg->devname, cfg->samplerate, 2, (byte)cfg->width, cfg->frames, cfg->bufsize) : 0;
ulong sampletime = rate ? (1000000000ULL / (ulong)rate) : 100000000000000000ULL;
byte running = 1;
ulong elapsed = 0;
ulong samples = 0;
sample[0] = sample[1] = 0;
while(running) {
pthread_mutex_lock(&sgt.lock);
while(sgt.cmd_queue) {
snd_cmd_t *cmd = &sgt.cmds[pop];
switch(cmd->command) {
case SND_CMD_STOP:
running = 0;
break;
case SND_CMD_FLUSH:
elapsed = 0;
if(rate)
aud_flush(&handle);
break;
case SND_CMD_QUERY:
switch(cmd->address) {
case SND_STAT_RUN:
sgt.query = rate;
break;
default:
sgt.query = snd_status(cmd->address, cmd->param);
break;
}
sgt.waiting = 0;
break;
default:
snd_dispatch(cmd->buffer, cmd->command, cmd->address, cmd->param, rate);
break;
}
if(++pop == SND_QUEUE)
pop = 0;
sgt.cmd_queue -= 1;
}
pthread_mutex_unlock(&sgt.lock);
if(sgt.interrupt)
sgt.interrupt = 0;
else
elapsed += snd_process();
// if((elapsed < sampletime) || !rate)
// usleep(1000);
while((elapsed >= sampletime) && !sgt.interrupt) {
snd_generate(sample);
if(rate)
aud_push(&handle, sample);
if(sgt.capture)
wav_write16(&sgt.wav, sample, 1);
samples++;
elapsed -= sampletime;
}
}
if(rate) {
aud_flush(&handle);
aud_close(&handle);
}
if(sgt.mid.track)
free(sgt.mid.track);
if(sgt.chip) {
free(sgt.bank);
free(sgt.chip);
}
sgt.query = rate;
sgt.waiting = 1;
}
void snd_instruct(const void *buf, uint size, short op, short addr, uint param) {
if(sgt.disabled)
return;
sgt.interrupt = 1;
while(sgt.cmd_queue == SND_QUEUE) {
;
}
pthread_mutex_lock(&sgt.lock);
sgt.interrupt = 1;
snd_cmd_t *cmd = &sgt.cmds[sgt.cmd_push];
cmd->command = op;
cmd->address = addr;
cmd->param = param;
if(buf) {
cmd->buffer = malloc(size);
memcpy(cmd->buffer, buf, size);
}
else {
cmd->buffer = NULL;
}
if(++(sgt.cmd_push) == SND_QUEUE)
sgt.cmd_push = 0;
sgt.cmd_queue += 1;
pthread_mutex_unlock(&sgt.lock);
}
uint snd_query(short addr, uint param) {
if(sgt.disabled || sgt.waiting)
return 0;
sgt.waiting = 1;
snd_instruct(NULL, 0, SND_CMD_QUERY, addr, param);
while(sgt.waiting) {
;
}
return sgt.query;
}
uint snd_init(const char *devname, uint samplerate, uint width, uint frames, uint bufsize, byte disabled) {
if(disabled) {
sgt.disabled = 1;
return 0;
}
snd_devconfig snd_cfg;
snd_cfg.devname = devname;
snd_cfg.samplerate = samplerate;
snd_cfg.width = ((width == 32) ? 32 : 16) / 8;
snd_cfg.frames = frames;
snd_cfg.bufsize = bufsize;
pthread_mutex_init(&sgt.lock, NULL);
pthread_mutex_init(&sgt.log_lock, NULL);
pthread_create(&sgt.thread, NULL, snd_loop, &snd_cfg);
return snd_query(SND_STAT_RUN, 0);
}
uint snd_end() {
if(sgt.disabled)
return 0;
snd_instruct(NULL, 0, SND_CMD_STOP, 0, 0);
while(sgt.waiting ^ 1) {
;
}
pthread_mutex_destroy(&sgt.lock);
pthread_mutex_destroy(&sgt.log_lock);
return sgt.query;
}