sound, music, mixer

This commit is contained in:
Ondřej Novák 2025-02-02 14:29:06 +01:00
parent 42087c926c
commit f8a1501289
42 changed files with 1345 additions and 157 deletions

View file

@ -1,5 +1,5 @@
#include <skeldal_win.h> #include <skeldal_win.h>
#include <libs/zvuk.h> #include <platform/sound.h>
extern "C" extern "C"
{ {

View file

@ -42,4 +42,5 @@ target_link_libraries(skeldal
skeldal_libs skeldal_libs
skeldal_platform skeldal_platform
skeldal_sdl skeldal_sdl
skeldal_libs
${SDL2_LIBRARIES} pthread) ${SDL2_LIBRARIES} pthread)

View file

@ -11,7 +11,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include "engine1.h" #include "engine1.h"
#include <libs/pcx.h> #include <libs/pcx.h>

View file

@ -11,7 +11,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include "engine1.h" #include "engine1.h"
#include <libs/pcx.h> #include <libs/pcx.h>

View file

@ -12,7 +12,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/gui.h> #include <libs/gui.h>
#include <libs/basicobj.h> #include <libs/basicobj.h>
#include "engine1.h" #include "engine1.h"

View file

@ -12,7 +12,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/gui.h> #include <libs/gui.h>
#include <libs/basicobj.h> #include <libs/basicobj.h>
#include "engine1.h" #include "engine1.h"

View file

@ -12,7 +12,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include <libs/mgifmem.h> #include <libs/mgifmem.h>
#include "engine1.h" #include "engine1.h"

View file

@ -8,7 +8,7 @@
#include <libs/event.h> #include <libs/event.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include "engine1.h" #include "engine1.h"
#include "globals.h" #include "globals.h"
#include "specproc.h" #include "specproc.h"

View file

@ -7,7 +7,7 @@
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/event.h> #include <libs/event.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include "math.h" #include "math.h"
#include "globals.h" #include "globals.h"
#include "engine1.h" #include "engine1.h"

View file

@ -14,7 +14,7 @@
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <fcntl.h> #include <fcntl.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <stdarg.h> #include <stdarg.h>
#include "globals.h" #include "globals.h"
#include "temp_storage.h" #include "temp_storage.h"

View file

@ -1428,12 +1428,12 @@ extern char **sound_table;
void init_tracks(void); void init_tracks(void);
void recalc_volumes(int sector,int side); void recalc_volumes(int sector,int side);
void play_effekt(int x,int y,int xd,int yd,int side,int sided,const TMA_SOUND *p); void play_effekt(int x,int y,int xd,int yd,int sector, int side,const TMA_SOUND *p);
void create_playlist(char *playlist); void create_playlist(char *playlist);
const char *get_next_music_from_playlist(void); const char *get_next_music_from_playlist(void);
const char * end_of_song_callback(void *ctx); const char * end_of_song_callback(void *ctx);
void purge_playlist(void); void purge_playlist(void);
void play_sample_at_sector(int sample,int sector1,int sector2,int track, char loop); void play_sample_at_sector(int sample,int listener,int source,int track, char loop);
void play_sample_at_channel(int sample,int channel,int vol); void play_sample_at_channel(int sample,int channel,int vol);
void stop_track(int track); void stop_track(int track);
char test_playing(int track); char test_playing(int track);

View file

@ -12,7 +12,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include "engine1.h" #include "engine1.h"
#include <libs/pcx.h> #include <libs/pcx.h>

View file

@ -13,7 +13,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include <ctype.h> #include <ctype.h>
#include <libs/gui.h> #include <libs/gui.h>

View file

@ -11,7 +11,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/gui.h> #include <libs/gui.h>
#include <libs/basicobj.h> #include <libs/basicobj.h>
#include "engine1.h" #include "engine1.h"

View file

@ -11,7 +11,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include "engine1.h" #include "engine1.h"
#include <libs/pcx.h> #include <libs/pcx.h>
@ -185,7 +185,7 @@ void macro_sound(const TMA_SOUND *p,int psect,int pdir,int sect,int dir)
if (sound_side_flags & SD_PRIM_FORV) up=2; if (sound_side_flags & SD_PRIM_FORV) up=2;
if (~(p->bit16) & up) { if (~(p->bit16) & up) {
if (psect) { if (psect) {
play_effekt(map_coord[sect].x,map_coord[sect].y,map_coord[psect].x,map_coord[psect].y,dir,pdir,p); play_effekt(map_coord[sect].x,map_coord[sect].y,map_coord[psect].x,map_coord[psect].y,psect,pdir,p);
} else { } else {
play_effekt(0,0,0,0,-1,-1,p); play_effekt(0,0,0,0,-1,-1,p);
} }

View file

@ -11,7 +11,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include <stdarg.h> #include <stdarg.h>
#include "engine1.h" #include "engine1.h"

View file

@ -11,7 +11,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include "engine1.h" #include "engine1.h"
#include <libs/pcx.h> #include <libs/pcx.h>
@ -185,7 +185,6 @@ int load_map(char *filename)
mob_template=NULL; mob_template=NULL;
mob_size=0; mob_size=0;
if (f==NULL) return -1; if (f==NULL) return -1;
if (snd_devnum==DEV_DAC) stop_mixing();
do do
{ {
r=load_section(f,&temp,&sect,&size); r=load_section(f,&temp,&sect,&size);
@ -329,7 +328,6 @@ int load_map(char *filename)
exit(0); exit(0);
} }
doNotLoadMapState=0; doNotLoadMapState=0;
if (snd_devnum==DEV_DAC) start_mixing();
return suc; return suc;
} }
@ -1605,45 +1603,49 @@ void step_zoom(char smer)
if (cur_mode==MD_GAME) recalc_volumes(viewsector,viewdir); if (cur_mode==MD_GAME) recalc_volumes(viewsector,viewdir);
} }
void turn_zoom(int smer) void turn_zoom(int smer) {
{ if (running_anm)
if (running_anm) return; return;
if (pass_zavora) return;else pass_zavora=1; if (pass_zavora)
if (!GlobEvent(MAGLOB_ONTURN,viewsector,viewdir)) return; return;
if (set_halucination) do_halucinace(); else
norefresh=1; pass_zavora = 1;
hold_timer(TM_BACK_MUSIC,1); if (!GlobEvent(MAGLOB_ONTURN, viewsector, viewdir))
viewdir=(viewdir+smer)&3; return;
render_scene(viewsector,viewdir); if (set_halucination)
hide_ms_at(387); do_halucinace();
OutBuffer2nd(); norefresh = 1;
other_draw(); hold_timer(TM_BACK_MUSIC, 1);
bott_draw(0); viewdir = (viewdir + smer) & 3;
if (smer==1) recalc_volumes(viewsector, viewdir);
{ render_scene(viewsector, viewdir);
anim_sipky(H_SIPKY_SV,1); hide_ms_at(387);
anim_sipky(0,255); OutBuffer2nd();
turn_left(); other_draw();
} bott_draw(0);
else if (smer == 1) {
{ anim_sipky(H_SIPKY_SV, 1);
anim_sipky(H_SIPKY_SZ,1); anim_sipky(0, 255);
anim_sipky(0,255); turn_left();
turn_right(); } else {
} anim_sipky(H_SIPKY_SZ, 1);
chod_s_postavama(0); anim_sipky(0, 255);
if (battle || (game_extras & EX_ALWAYS_MINIMAP)) draw_medium_map(); turn_right();
update_mysky(); }
ukaz_mysku(); chod_s_postavama(0);
showview(0,0,0,0); if (battle || (game_extras & EX_ALWAYS_MINIMAP))
recalc_volumes(viewsector,viewdir); draw_medium_map();
if (!battle) calc_game(); update_mysky();
norefresh=0; ukaz_mysku();
cancel_render=1; showview(0, 0, 0, 0);
hold_timer(TM_BACK_MUSIC,0); if (!battle)
mix_back_sound(0); calc_game();
pass_zavora=0; norefresh = 0;
} cancel_render = 1;
hold_timer(TM_BACK_MUSIC, 0);
mix_back_sound(0);
pass_zavora = 0;
}
int check_path(word **path,word tosect) int check_path(word **path,word tosect)
{ {

View file

@ -10,7 +10,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include "engine1.h" #include "engine1.h"
#include <libs/pcx.h> #include <libs/pcx.h>

View file

@ -8,7 +8,7 @@
#include <libs/event.h> #include <libs/event.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include <libs/gui.h> #include <libs/gui.h>
#include <libs/basicobj.h> #include <libs/basicobj.h>
@ -87,8 +87,6 @@ THUMAN postavy[POCET_POSTAV],postavy_save[POCET_POSTAV];
void (*unwire_proc)(void); void (*unwire_proc)(void);
void (*wire_proc)(void); void (*wire_proc)(void);
char cur_mode,battle_mode; char cur_mode,battle_mode;
static int init_music_vol=127;
static int init_gfx_vol=255;
static char titles_on=0; static char titles_on=0;
const void *pcx_fade_decomp(const void *p, int32_t *s); const void *pcx_fade_decomp(const void *p, int32_t *s);
@ -498,25 +496,6 @@ void set_font(int font,int c1,...)
memcpy(f_default,charcolors,sizeof(charcolors)); memcpy(f_default,charcolors,sizeof(charcolors));
} }
void music_init(void)
{
// char *path;
/* if (sound_detection)
{
if (sound_detect(&snd_devnum,&snd_parm1,&snd_parm2,&snd_parm3)) snd_devnum=DEV_NOSOUND;
}*/
SEND_LOG("(SOUND) SOUND_SET Setting Sound: Device '%s' Port: %3X",device_name(snd_devnum),snd_parm1);
SEND_LOG("(SOUND) SOUND_SET Setting Sound: IRQ: %X DMA: %X",snd_parm2,snd_parm3);
set_mixing_device(snd_devnum,snd_mixing,snd_parm1,snd_parm2,snd_parm3);
start_mixing();
set_snd_effect(SND_GFX,init_gfx_vol);
set_snd_effect(SND_MUSIC,init_music_vol);
// path=plugins_path;
}
void clrscr(void) void clrscr(void)
{ {
@ -984,7 +963,8 @@ void init_skeldal(const INI_CONFIG *cfg)
add_game_window(); add_game_window();
music_init(); game_sound_init_device(ini_section_open(cfg, "audio"));
start_mixing();
if ((verr=init_mysky())!=0) if ((verr=init_mysky())!=0)
{ {
@ -1267,7 +1247,7 @@ void play_anim(int anim_num)
TSTR_LIST titl=NULL; TSTR_LIST titl=NULL;
const char *s = build_pathname(2,gpathtable[SR_VIDEO], texty[anim_num]); const char *s = build_pathname(2,gpathtable[SR_VIDEO], texty[anim_num]);
s = local_strdup(s); s = local_strdup(s);
if (snd_devnum==DEV_NOSOUND || titles_on) if (titles_on)
{ {
concat(t,s," "); concat(t,s," ");
z=strrchr(t,'.'); z=strrchr(t,'.');
@ -1344,8 +1324,8 @@ static void game_big_circle(char enforced)
{viewdir=i;break;} {viewdir=i;break;}
} }
} }
for(r=0;r<mapsize*4;r++) call_macro(r,MC_STARTLEV);
recalc_volumes(viewsector,viewdir); recalc_volumes(viewsector,viewdir);
for(r=0;r<mapsize*4;r++) call_macro(r,MC_STARTLEV);
loadlevel.name[0]=0; loadlevel.name[0]=0;
reroll_all_shops(); reroll_all_shops();

View file

@ -3,7 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/wav_mem.h> #include <libs/wav_mem.h>
#include <libs/event.h> #include <libs/event.h>
#include "globals.h" #include "globals.h"
@ -29,13 +29,27 @@ typedef struct snd_info
{ {
const TMA_SOUND *data; //4 const TMA_SOUND *data; //4
short xpos,ypos,side; //10 short xpos,ypos,side; //10
word volume,block; //14 word volume,sample_block; //14
word sector;
}SND_INFO; }SND_INFO;
static short chan_state[CHANNELS]; static short chan_state[CHANNELS];
static short track_state[TRACKS]; static short track_state[TRACKS];
short sample_volume=255; short sample_volume=255;
typedef struct sound_side_map_dir_t {
uint32_t distance;
int32_t visit_counter;
} TSOUND_SIDE_MAP_DIR;
typedef struct sound_side_map_t {
TSOUND_SIDE_MAP_DIR parts[4];
} TSOUND_SIDE_MAP;
static TSOUND_SIDE_MAP *current_sound_sector_map = NULL;
static size_t current_sound_sector_map_size = 0;
static int32_t current_sound_sector_map_counter = 0;
//static struct t_wave wav_last_head; //static struct t_wave wav_last_head;
//static int wav_last_size; //static int wav_last_size;
static int mute_task=-1; static int mute_task=-1;
@ -140,7 +154,7 @@ void release_channel(int channel)
if (i==-1) return; if (i==-1) return;
mute_channel(channel); mute_channel(channel);
{ {
aunlock(playings[channel].block); aunlock(playings[channel].sample_block);
chan_state[channel]=-1; chan_state[channel]=-1;
track_state[i]=-1; track_state[i]=-1;
} }
@ -158,7 +172,62 @@ int calc_volume(int *x,int *y,int side)
return ds; return ds;
} }
int calcul_volume(int chan,int x,int y,int side,int volume) int set_channel_volume_from_sector(int channel,
int sound_source_sector,
int sound_source_side,
int listener_sector,
int listener_direction,
int volume) {
const int maxvolume = 32768;
volume = 255*volume/100;
int left = maxvolume;
int right = maxvolume;
if (sound_source_sector > mapsize) return 0;
if (sound_source_sector <0 || sound_source_sector == listener_sector) {
if (sound_source_side >= 0) {
int ddf = (sound_source_side + 4 - listener_direction) & 3;
switch (ddf) {
default:
case 0:
case 2: left = right = maxvolume;break;
case 1: left = maxvolume/4; right = maxvolume;break;
case 3: left = maxvolume; right = maxvolume/4;break;
}
}
} else {
const TSOUND_SIDE_MAP *p = current_sound_sector_map+sound_source_sector;
left = 0;
right = 0;
for (int i = 0; i < 4; ++i) {
if (p->parts[i].visit_counter == current_sound_sector_map_counter) {
int dist = p->parts[i].distance;
if (!dist) return 1; //error
int inc = maxvolume/(dist*dist);
switch(i) {
default:
case 2:
case 0: left += inc; right += inc; break;
case 1: right += inc;break;
case 3: left += inc;break;
}
}
}
}
left = (left * volume) >> 8;
right = (right * volume) >> 8;
if (left > maxvolume) left = maxvolume;
if (right > maxvolume) right = maxvolume;
if (left == 0 && right == 0) return 0;
set_channel_volume(channel,left,right);
return 1;
}
/*int calcul_volume(int chan,int x,int y,int side,int volume)
{ {
int lv,rv; int lv,rv;
int ds,bal,i; int ds,bal,i;
@ -195,7 +264,7 @@ int calcul_volume(int chan,int x,int y,int side,int volume)
set_channel_volume(chan,lv,rv); set_channel_volume(chan,lv,rv);
return 0; return 0;
} }
*/
const void *wav_load(const void *p, int32_t *s) const void *wav_load(const void *p, int32_t *s)
{ {
const char *sr; const char *sr;
@ -220,7 +289,7 @@ const void *wav_load(const void *p, int32_t *s)
return tgr; return tgr;
} }
void play_effekt(int x,int y,int xd,int yd,int side,int sided,const TMA_SOUND *p) void play_effekt(int x,int y,int xd,int yd,int sector,int side,const TMA_SOUND *p)
{ {
int chan; int chan;
int blockid; int blockid;
@ -228,14 +297,14 @@ void play_effekt(int x,int y,int xd,int yd,int side,int sided,const TMA_SOUND *p
const char *s; const char *s;
if (!sound_enabled) return; if (!sound_enabled) return;
side;
chan=find_free_channel(p->soundid); chan=find_free_channel(p->soundid);
release_channel(chan); release_channel(chan);
track=&tracks[p->soundid]; track=&tracks[p->soundid];
track->data=p; track->data=p;
track->xpos=xd; track->xpos=xd;
track->ypos=yd; track->ypos=yd;
track->side=sided; track->side=side;
track->sector = sector;
track_state[p->soundid]=-1; track_state[p->soundid]=-1;
if (p->bit16 & 0x8) if (p->bit16 & 0x8)
{ {
@ -244,7 +313,8 @@ void play_effekt(int x,int y,int xd,int yd,int side,int sided,const TMA_SOUND *p
else set_channel_volume(chan,vol,rnd(vol)); else set_channel_volume(chan,vol,rnd(vol));
} }
else { else {
if (calcul_volume(chan,x-xd,y-yd,/*side-*/sided,p->volume)) return; if (!set_channel_volume_from_sector(chan, sector, side, viewsector, viewdir, p->volume))
return;
} }
blockid = find_handle(p->filename, wav_load); blockid = find_handle(p->filename, wav_load);
@ -260,12 +330,75 @@ void play_effekt(int x,int y,int xd,int yd,int side,int sided,const TMA_SOUND *p
playings[chan].data=p; playings[chan].data=p;
playings[chan].xpos=xd; playings[chan].xpos=xd;
playings[chan].ypos=yd; playings[chan].ypos=yd;
playings[chan].side=sided; playings[chan].sector = sector;
playings[chan].side=side;
playings[chan].volume=p->volume; playings[chan].volume=p->volume;
playings[chan].block=blockid; playings[chan].sample_block=blockid;
chan_state[chan]=p->soundid; chan_state[chan]=p->soundid;
track_state[p->soundid]=chan; track_state[p->soundid]=chan;
} }
typedef struct _sound_map_queue_t {
int from_sector;
int to_sector;
} TSOUND_MAP_QUEUE;
static void build_dungeon_sound_map_in_dir(int sector, int side, int position, int32_t counter) {
if (map_sectors[sector].step_next[side] == 0
|| !(map_sides[sector * 4 +side].flags & SD_TRANSPARENT)) return;
TSOUND_MAP_QUEUE queue[128];
int qbeg = 0;
int qend = 0;
queue[qend].from_sector = sector;
queue[qend].to_sector = map_sectors[sector].step_next[side];
++qend;
while (qbeg != qend) {
const TSOUND_MAP_QUEUE *item = queue+(qbeg % countof(queue));
int cursect = item->to_sector;
int fromsect = item->from_sector;
++qbeg;
if (current_sound_sector_map[cursect].parts[position].visit_counter != counter) {
current_sound_sector_map[cursect].parts[position].visit_counter = counter;
int d = current_sound_sector_map[cursect].parts[position].distance =
current_sound_sector_map[fromsect].parts[position].distance + 1;
if (d<32) {
for (int i = 0; i < 4; ++i) {
int nx = map_sectors[cursect].step_next[i];
if (nx && (map_sides[cursect * 4 +i].flags & SD_TRANSPARENT)) {
if (current_sound_sector_map[nx].parts[position].visit_counter != counter) {
TSOUND_MAP_QUEUE *t = queue+(qend % countof(queue));
++qend;
t->from_sector = cursect;
t->to_sector = nx;
}
}
}
}
}
}
}
static void build_dungeon_sound_map(int sector, int side) {
if (mapsize != (int)current_sound_sector_map_size) {
free(current_sound_sector_map);
current_sound_sector_map = NewArr(TSOUND_SIDE_MAP, mapsize);
current_sound_sector_map_size = mapsize;
}
int32_t counter = ++current_sound_sector_map_counter;
if (sector > mapsize) return;
for (int i = 0; i < 4; ++i) {
current_sound_sector_map[sector].parts[i].distance = 0;
current_sound_sector_map[sector].parts[i].visit_counter = counter;
}
build_dungeon_sound_map_in_dir(sector, side, 0, counter);
build_dungeon_sound_map_in_dir(sector, (side+1)&3, 1, counter);
build_dungeon_sound_map_in_dir(sector, (side+2)&3, 2, counter);
build_dungeon_sound_map_in_dir(sector, (side+3)&3, 3, counter);
}
void recalc_volumes(int sector,int side) void recalc_volumes(int sector,int side)
{ {
int i; int i;
@ -275,16 +408,29 @@ void recalc_volumes(int sector,int side)
side; side;
SEND_LOG("(SOUND) %s","Recalculating volumes"); SEND_LOG("(SOUND) %s","Recalculating volumes");
build_dungeon_sound_map(sector, side);
newx=map_coord[sector].x; newx=map_coord[sector].x;
newy=map_coord[sector].y; newy=map_coord[sector].y;
// layer=map_coord[sector].layer; // layer=map_coord[sector].layer;
for(i=0;i<CHANNELS;i++) for(i=0;i<CHANNELS;i++) {
int volume = playings[i].volume;
if (chan_state[i]>=0 && playings[i].side>=0) if (chan_state[i]>=0 && playings[i].side>=0)
{ {
calcul_volume(i,newx-playings[i].xpos,newy-playings[i].ypos,/*side-*/playings[i].side,playings[i].volume); set_channel_volume_from_sector(
i,
playings[i].sector,
playings[i].side,
sector,
side,
volume);
//calcul_volume(i,newx-playings[i].xpos,newy-playings[i].ypos,/*side-*/playings[i].side,playings[i].volume);
if (!get_channel_state(i)) release_channel(i); if (!get_channel_state(i)) release_channel(i);
} }
else calcul_volume(i,0,0,-1,playings[i].volume); else {
int v = SND_EFF_MAXVOL*volume / 100;
set_channel_volume(i, v, v);
}
}
for(i=1;i<TRACKS;i++) if (track_state[i]<0 && tracks[i].data!=NULL) for(i=1;i<TRACKS;i++) if (track_state[i]<0 && tracks[i].data!=NULL)
{ {
if (tracks[i].side<0) if (tracks[i].side<0)
@ -296,7 +442,8 @@ void recalc_volumes(int sector,int side)
{ {
int x=newx-tracks[i].xpos, y=newy-tracks[i].ypos; int x=newx-tracks[i].xpos, y=newy-tracks[i].ypos;
if (calc_volume(&x,&y,tracks[i].side)>0) if (calc_volume(&x,&y,tracks[i].side)>0)
if (have_loop(tracks[i].data))play_effekt(newx,newy,tracks[i].xpos,tracks[i].ypos,side,tracks[i].side,tracks[i].data); if (have_loop(tracks[i].data))
play_effekt(newx,newy,tracks[i].xpos,tracks[i].ypos,tracks[i].sector,tracks[i].side,tracks[i].data);
} }
} }
mute_task=-1; mute_task=-1;
@ -360,7 +507,7 @@ const char *get_next_music_from_playlist()
if (!remain_play) if (!remain_play)
for(i=0;cur_playlist[i]!=NULL;remain_play++,i++) cur_playlist[i][0]=32; for(i=0;cur_playlist[i]!=NULL;remain_play++,i++) cur_playlist[i][0]=32;
if (play_list_mode==PL_RANDOM) if (play_list_mode==PL_RANDOM)
step=rand()*(playlist_size-1)/32768+1; step=rnd(playlist_size)+1;
else else
step=1; step=1;
i=playing_track; i=playing_track;
@ -387,24 +534,22 @@ void purge_playlist()
cur_playlist=NULL; cur_playlist=NULL;
} }
void play_sample_at_sector(int sample,int sector1,int sector2,int track, char loop) void play_sample_at_sector(int sample,int listener,int source,int track, char loop)
{ {
int x,y,xd,yd,chan; int xd,yd,chan;
const char *s; const char *s;
struct t_wave *p; struct t_wave *p;
int siz; int siz;
int oldtrack; int oldtrack;
if (!sound_enabled) return; if (!sound_enabled) return;
if (map_coord[sector1].layer!=map_coord[sector2].layer) return; if (map_coord[listener].layer!=map_coord[source].layer) return;
x=map_coord[sector1].x; xd=map_coord[source].x;
y=map_coord[sector1].y; yd=map_coord[source].y;
xd=map_coord[sector2].x;
yd=map_coord[sector2].y;
chan=find_free_channel(track); chan=find_free_channel(track);
oldtrack=track_state[track]; oldtrack=track_state[track];
if (!track || oldtrack==-1) release_channel(chan); if (!track || oldtrack==-1) release_channel(chan);
if (calcul_volume(chan,x-xd,y-yd,viewdir,100)) return; if (!set_channel_volume_from_sector(chan, source, -1, listener, -1, 100)) return;
if (!track || oldtrack==-1) if (!track || oldtrack==-1)
{ {
alock(sample); alock(sample);
@ -419,7 +564,8 @@ void play_sample_at_sector(int sample,int sector1,int sector2,int track, char lo
playings[chan].ypos=yd; playings[chan].ypos=yd;
playings[chan].side=viewdir; playings[chan].side=viewdir;
playings[chan].volume=100; playings[chan].volume=100;
playings[chan].block=sample; playings[chan].sample_block=sample;
playings[chan].sector=source;
chan_state[chan]=track; chan_state[chan]=track;
track_state[track]=chan; track_state[track]=chan;
} }
@ -519,7 +665,8 @@ char test_playing(int track)
return track_state[track]!=-1; return track_state[track]!=-1;
} }
static int flute_canal=30; static int flute_channel=30;
static int flute_channel_offset = 0;
void start_play_flute(char note) void start_play_flute(char note)
{ {
@ -534,8 +681,9 @@ void start_play_flute(char note)
q=ablock(H_FLETNA); q=ablock(H_FLETNA);
w=q;w+=sizeof(struct t_wave)+4; w=q;w+=sizeof(struct t_wave)+4;
vol*=SND_EFF_MAXVOL/100; vol*=SND_EFF_MAXVOL/100;
set_channel_volume(flute_canal,vol,vol); int ch = flute_channel+flute_channel_offset;
play_sample(flute_canal,w,0x1665,0xADE,(int)(realfrq+0.5),1); set_channel_volume(ch,vol,vol);
play_sample(ch,w,0x1665,0xADE,(int)(realfrq+0.5),1);
} }
else else
{ {
@ -552,8 +700,9 @@ void stop_play_flute()
{ {
q=ablock(H_FLETNA); q=ablock(H_FLETNA);
w=q;w+=sizeof(struct t_wave); w=q;w+=sizeof(struct t_wave);
chan_break_ext(flute_canal,w+4,*(int *)w); int ch = flute_channel+flute_channel_offset;
flute_canal^=1; chan_break_ext(ch,w+4,*(int *)w);
flute_channel_offset = (flute_channel_offset+1) & 7;
} }
else else
{ {

View file

@ -3,7 +3,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <libs/event.h> #include <libs/event.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>

View file

@ -12,7 +12,7 @@
#include <libs/devices.h> #include <libs/devices.h>
#include <libs/bmouse.h> #include <libs/bmouse.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/zvuk.h> #include <platform/sound.h>
#include <libs/strlite.h> #include <libs/strlite.h>
#include <libs/gui.h> #include <libs/gui.h>
#include <libs/basicobj.h> #include <libs/basicobj.h>

View file

@ -14,6 +14,7 @@ SET(files basicobj.c
strlite.c strlite.c
wav_mem.c wav_mem.c
strlists.c strlists.c
music.cpp
swaper.c ) swaper.c )
add_library(skeldal_libs ${files}) add_library(skeldal_libs ${files})

View file

@ -69,7 +69,7 @@
#include <libs/bgraph.h> #include <libs/bgraph.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/zvuk.h> #include <platform/sound.h>
//#include <vesa.h> //#include <vesa.h>
//#include <i86.h> //#include <i86.h>

View file

@ -5,7 +5,7 @@
#include "memman.h" #include "memman.h"
#include "mem.h" #include "mem.h"
#include "mgifmem.h" #include "mgifmem.h"
#include <libs/zvuk.h> #include <platform/sound.h>
static MGIF_HEADER_T *mgif_header; static MGIF_HEADER_T *mgif_header;

161
libs/music.cpp Normal file
View file

@ -0,0 +1,161 @@
#include "music.h"
#include <platform/platform.h>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string_view>
#include <vector>
#include <utility>
class MUSStreamParser: public IMusicStream {
public:
MUSStreamParser(const char *name) {
data = map_file_to_memory(file_icase_find(name), &sz);
if (data) {
_loaded = true;
iter = reinterpret_cast<const uint8_t *>(data);
chans = read_short();
freq = read_int();
iter += 4;
blocks = read_int();
iter += 8;
std::memcpy(btable,iter,sizeof(btable));
iter += sizeof(btable);
btable[0] = -31767;
}
}
~MUSStreamParser() {
if (data) unmap_file(data, sz);
}
MUSStreamParser(const MUSStreamParser &) = delete;
MUSStreamParser& operator=(const MUSStreamParser &) = delete;
bool is_loaded() const {return _loaded;}
virtual TMUSIC_STREAM_INFO get_info() const override {
TMUSIC_STREAM_INFO r;
r.format = 2;
r.channels = chans;
r.freq = freq;
return r;
}
virtual std::string_view read() override {
if (unprocessed_buffer.empty()) {
if (!process_next()) return {};
}
return std::exchange(unprocessed_buffer, {});
}
virtual void put_back(std::string_view data) override {
unprocessed_buffer = data;
}
protected:
bool _loaded = false;
void *data;
std::size_t sz;
std::int16_t chans;
std::int32_t freq;
std::int32_t blocks;
short btable[256];
const uint8_t *iter;
std::vector<int16_t> outbuff;
std::string_view unprocessed_buffer;
int32_t read_int() {
int32_t r = iter[0] | (iter[1] << 8) | (iter[2]<<16) | (iter[3] << 24);
iter+=4;
return r;
}
int16_t read_short() {
int16_t r = iter[0] | (iter[1] << 8);
iter+=2;
return r;
}
bool process_next() {
if (blocks == 0) {
return false;
}
--blocks;
std::int32_t p = read_int();
read_int();
outbuff.clear();
short accum[2]={0,0};
int c = 0;
for (int i = 0; i < p; ++i) {
uint8_t p = *iter++;
short val=accum[c]+btable[p];
accum[c]=val;
/*
if (p==0) //pridano jako provizorni reseni pro korekci chyby komprimacniho programu
{
val-=31767;
}
*/
c = (c + 1) % chans;
outbuff.push_back(val);
}
unprocessed_buffer = {reinterpret_cast<const char *>(outbuff.data()), outbuff.size()*2};
return true;
}
};
void music_close(TMUSIC_STREAM *stream) {
if (stream) {
IMusicStream *s = static_cast<IMusicStream *>(stream);
delete s;
}
}
TMUSIC_STREAM *music_open(const char *filename) {
MUSStreamParser *player = new MUSStreamParser(filename);
if (player->is_loaded()) return player;
delete player;
return NULL;
}
TMUSIC_STREAM_CHUNK music_read(TMUSIC_STREAM *stream) {
TMUSIC_STREAM_CHUNK r = {NULL,0};
if (stream) {
IMusicStream *s = static_cast<IMusicStream *>(stream);
auto data = s->read();
r.ptr = data.data();
r.sz = data.size();
}
return r;
}
char music_is_eof(const TMUSIC_STREAM_CHUNK *chunk) {
return chunk->sz == 0;
}
void music_put_back_chunk(TMUSIC_STREAM *stream, const TMUSIC_STREAM_CHUNK *chunk){
if (stream) {
IMusicStream *s = static_cast<IMusicStream *>(stream);
s->put_back({reinterpret_cast<const char *>(chunk->ptr), chunk->sz});
}
}
TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream) {
if (stream) {
const IMusicStream *s = static_cast<const IMusicStream *>(stream);
return s->get_info();
} else {
TMUSIC_STREAM_INFO r = {};
return r;
}
}

51
libs/music.h Normal file
View file

@ -0,0 +1,51 @@
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct music_stream_t {} TMUSIC_STREAM;
typedef struct music_stream_info_t {
int freq;
int channels;
int format; //1 - uint8_t. 2 - int16_t
} TMUSIC_STREAM_INFO;
typedef struct music_stream_chunk_t {
const void *ptr;
size_t sz;
} TMUSIC_STREAM_CHUNK;
///open music stream
TMUSIC_STREAM *music_open(const char *filename);
///retrieve information
TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream);
TMUSIC_STREAM_CHUNK music_read(TMUSIC_STREAM *stream);
char music_is_eof(const TMUSIC_STREAM_CHUNK *chunk);
void music_put_back_chunk(TMUSIC_STREAM *stream, const TMUSIC_STREAM_CHUNK *chunk);
///close music stream
void music_close(TMUSIC_STREAM *stream);
#ifdef __cplusplus
}
#include <string_view>
class IMusicStream: public TMUSIC_STREAM {
public:
virtual ~IMusicStream() = default;
virtual TMUSIC_STREAM_INFO get_info() const = 0;
virtual std::string_view read() = 0;
virtual void put_back(std::string_view data) = 0;
};
#endif

View file

@ -75,6 +75,7 @@ int stricmp(const char *a, const char *b);
void strupr(char *c); void strupr(char *c);
const char * int2ascii(int i, char *c, int radix); const char * int2ascii(int i, char *c, int radix);
int get_timer_value(void);
uint32_t get_game_tick_count(void); uint32_t get_game_tick_count(void);
void sleep_ms(uint32_t); void sleep_ms(uint32_t);

View file

@ -12,7 +12,7 @@ static uint16_t screen_pitch = 640;
char game_display_init(const INI_CONFIG_SECTION *display_section, const char *title) { char game_display_init(const INI_CONFIG_SECTION *display_section, const char *title) {
SDLContext::Config cfg = {}; SDLContext::VideoConfig cfg = {};
const char *aspect_str; const char *aspect_str;
aspect_str = ini_get_string(display_section, "aspect_ratio", "4:3"); aspect_str = ini_get_string(display_section, "aspect_ratio", "4:3");

View file

@ -1,4 +1,8 @@
add_subdirectory(tests) add_subdirectory(tests)
add_library(skeldal_sdl sdl_context.cpp BGraph2.cpp input.cpp sound.cpp) add_library(skeldal_sdl
sdl_context.cpp
BGraph2.cpp
input.cpp
sound.cpp)
set_property(TARGET skeldal_sdl PROPERTY CXX_STANDARD 20) set_property(TARGET skeldal_sdl PROPERTY CXX_STANDARD 20)

View file

@ -24,6 +24,11 @@ void SDLContext::SDL_Deleter::operator ()(SDL_Texture* texture) {
SDL_DestroyTexture(texture); SDL_DestroyTexture(texture);
} }
void SDLContext::SDL_Audio_Deleter::operator()(void *x) {
SDL_AudioDeviceID id = reinterpret_cast<std::uintptr_t>(x);
SDL_CloseAudioDevice(id);
}
struct SDL_INIT_Context { struct SDL_INIT_Context {
SDL_INIT_Context() { SDL_INIT_Context() {
@ -80,7 +85,7 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur
void SDLContext::init_video(const Config &config, const char *title) { void SDLContext::init_video(const VideoConfig &config, const char *title) {
char buff[256]; char buff[256];
static Uint32 update_request_event = SDL_RegisterEvents(1); static Uint32 update_request_event = SDL_RegisterEvents(1);
_update_request_event = update_request_event; _update_request_event = update_request_event;
@ -502,3 +507,32 @@ SDL_Rect SDLContext::to_window_rect(const SDL_Rect &winrc, const SDL_Rect &sourc
void SDLContext::set_quit_callback(std::function<void()> fn) { void SDLContext::set_quit_callback(std::function<void()> fn) {
_quit_callback = std::move(fn); _quit_callback = std::move(fn);
} }
SDLContext::AudioInfo SDLContext::init_audio(const AudioConfig &config, SDL_AudioCallback cb, void *cb_ctx) {
char buff[256];
_audio.reset();
SDL_AudioSpec aspec = {};
aspec.callback = cb;
aspec.userdata = cb_ctx;
aspec.channels = 2;
aspec.format = AUDIO_F32;
aspec.freq = 44100;
aspec.samples = 1024;
SDL_AudioSpec obtaied = {};
auto id = SDL_OpenAudioDevice(config.audioDevice, 0, &aspec, &obtaied, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
if (id < 1) {
snprintf(buff, sizeof(buff), "SDL Error create audio: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_audio.reset(reinterpret_cast<void *>(id));
return {obtaied.freq};
}
void SDLContext::pause_audio(bool pause) {
SDL_AudioDeviceID id = reinterpret_cast<std::intptr_t>(_audio.get());
SDL_PauseAudioDevice(id, pause?1:0);
}
void SDLContext::close_audio() {
_audio.reset();
}

View file

@ -15,7 +15,7 @@ public:
SDLContext(); SDLContext();
struct Config { struct VideoConfig {
int window_width; int window_width;
int window_height; int window_height;
bool crt_filter; bool crt_filter;
@ -26,11 +26,22 @@ public:
int aspect_y; int aspect_y;
}; };
struct AudioConfig {
const char *audioDevice;
};
void init_video(const Config &config, const char *title); struct AudioInfo {
int freq;
};
void init_video(const VideoConfig &config, const char *title);
void close_video(); void close_video();
AudioInfo init_audio(const AudioConfig &config, SDL_AudioCallback cb, void *cb_ctx);
void pause_audio(bool pause);
void close_audio();
void present_rect(uint16_t *pixels, unsigned int pitch, unsigned int x, unsigned int y, unsigned int xs,unsigned ys); void present_rect(uint16_t *pixels, unsigned int pitch, unsigned int x, unsigned int y, unsigned int xs,unsigned ys);
void swap_render_buffers(); void swap_render_buffers();
@ -99,6 +110,12 @@ protected:
slide_transition slide_transition
}; };
struct SDL_Audio_Deleter {
void operator()(void *x);
};
MS_EVENT ms_event; MS_EVENT ms_event;
mutable std::mutex _mx; mutable std::mutex _mx;
@ -112,6 +129,7 @@ protected:
std::unique_ptr<SDL_Texture, SDL_Deleter> _texture; std::unique_ptr<SDL_Texture, SDL_Deleter> _texture;
std::unique_ptr<SDL_Texture, SDL_Deleter> _texture2; std::unique_ptr<SDL_Texture, SDL_Deleter> _texture2;
std::unique_ptr<SDL_Texture, SDL_Deleter> _crt_effect; std::unique_ptr<SDL_Texture, SDL_Deleter> _crt_effect;
std::unique_ptr<void, SDL_Audio_Deleter> _audio;
SDL_Texture *_visible_texture; SDL_Texture *_visible_texture;
SDL_Texture *_hidden_texture; SDL_Texture *_hidden_texture;
@ -125,6 +143,7 @@ protected:
std::atomic<bool> _quit_requested = false; std::atomic<bool> _quit_requested = false;
std::vector<char> _display_update_queue; std::vector<char> _display_update_queue;
using QueueIter = const char *; using QueueIter = const char *;
std::queue<uint16_t> _keyboard_queue; std::queue<uint16_t> _keyboard_queue;

View file

@ -1,36 +1,250 @@
#include "../platform.h" #include "../platform.h"
#include <libs/zvuk.h> #include "../sound.h"
#include <libs/logfile.h>
#include "global_context.h"
#include "sound_mixer.h"
#include <cstring>
#include <iostream>
#include <thread> #include <thread>
void set_mixing_device(int mix_dev,int mix_freq,...) {
#define countof(array) (sizeof(array)/sizeof(array[0]))
static void SDLCALL mixing_callback(void *userdata, Uint8 * stream, int len);
static SoundMixer<2> sound_mixer;
static float master_volume = 1.0;
static float sound_effect_volume = 1.0;
static float music_volume = 1.0;
static float base_freq;
bool swap_channels = false;
static void empty_deleter(const void *) {}
constexpr int BACK_BUFF_SIZE = 0x40000;
static uint8_t *music_buffer = {};
std::shared_ptr<WaveSource> music_source;
static std::unique_ptr<IMusicStream> current_music = NULL;
struct VolumePreset {
float left = 1.0;
float right = 1.0;
};
static std::unordered_map<int, VolumePreset > channel_volume_presset;
void game_sound_init_device(const INI_CONFIG_SECTION *audio_section) {
const char *dev = ini_get_string(audio_section, "device", "");
if (!dev[0]) dev = nullptr;
SDLContext::AudioConfig cfg;
cfg.audioDevice = dev;
auto r = get_sdl_global_context().init_audio(cfg, mixing_callback, nullptr);
base_freq = r.freq;
} }
static void SDLCALL mixing_callback(void *userdata, Uint8 * stream, int len) {
float *s = reinterpret_cast<float *>(stream);
int samples = len/8;
std::fill(s, s+2*samples, 0.0f);
sound_mixer.mix_to_buffer(s, samples);
}
char start_mixing() { char start_mixing() {
return 0; get_sdl_global_context().pause_audio(false);
return 0;
} }
void stop_mixing() { void stop_mixing() {
get_sdl_global_context().pause_audio(true);
} }
static std::array<float,2> make_channel_volume(float left, float right) {
if (swap_channels) std::swap(left, right);
return {
left * master_volume * sound_effect_volume,
right * master_volume * sound_effect_volume,
};
}
void play_sample(int channel,const void *sample,int32_t size,int32_t lstart,int32_t sfreq,int type) { void play_sample(int channel,const void *sample,int32_t size,int32_t lstart,int32_t sfreq,int type) {
float speed = sfreq/base_freq;
int step = type==1?1:2;
auto samples = size/step;
auto start = lstart/step;
WaveSource::Format fmt = step==1?WaveSource::Format::uint8:WaveSource::Format::int16;
auto src = std::make_shared<WaveSource>(WaveSource::WavePtr(sample,&empty_deleter),fmt,speed,samples,start);
VolumePreset &vp = channel_volume_presset[channel];
sound_mixer.set_track(channel, WaveMixer<2>(src, make_channel_volume(vp.left, vp.right), 1.0));
} }
void set_channel_volume(int channel,int left,int right) { void set_channel_volume(int channel,int left,int right) {
float l = left / 32768.0;
float r = right / 32768.0;
VolumePreset &vp = channel_volume_presset[channel];
vp.left = l;
vp.right = r;
sound_mixer.visit_track(channel, [&](WaveMixer<2> &mx){
mx.set_channel_volumes_fade(make_channel_volume(vp.left, vp.right), {2e-4,2e-4});
});
} }
void set_end_of_song_callback(const char * (*cb)(void *), void *ctx) {
static size_t music_buffer_write_pos = 0;
size_t copy_to_music_buffer(const void *data, size_t data_size) {
size_t rdpos = music_source->get_output_position_bytes();
if (static_cast<std::make_signed_t<std::size_t> >(rdpos) < 0) return 0;
size_t l = music_source->length_bytes();
size_t space = rdpos < music_buffer_write_pos?l - music_buffer_write_pos: rdpos - music_buffer_write_pos;
if (space == 0) return 0;
data_size = std::min(space,data_size);
std::memcpy(music_buffer+music_buffer_write_pos, data, data_size);
music_buffer_write_pos += data_size;
if (music_buffer_write_pos >= l) {
music_buffer_write_pos = 0;
}
return data_size;
}
static const char * (*end_of_song_callback)(void *ctx) = NULL;
static void *end_of_song_callback_ctx = NULL;
void set_end_of_song_callback(const char * (*cb)(void *), void *ctx) {
end_of_song_callback = cb;
end_of_song_callback_ctx = ctx;
} }
void fade_music() { void fade_music() {
std::lock_guard _(music_source->get_lock());
size_t rdpos = music_source->get_output_position_samples();
WaveSource::StereoInt16 *rdptr = reinterpret_cast<WaveSource::StereoInt16 *>(music_buffer);
size_t wrsamples = music_buffer_write_pos / sizeof(WaveSource::StereoInt16);
size_t l =music_source->length();
float_t size = (rdpos < wrsamples?0:l) + wrsamples - rdpos;
for (size_t i = 0; i < size; ++i) {
float fact = (size - i)/size;
rdptr[rdpos].left = rdptr[rdpos].left * fact;
rdptr[rdpos].right = rdptr[rdpos].right * fact;
++rdpos;
if (rdpos >=l) rdpos = 0;
}
}
constexpr int music_track_id_base = 0x1000;
static void music_buffer_deleter(const void *ptr) {
uint8_t *p = reinterpret_cast<uint8_t *>(const_cast<void *>(ptr));
delete [] p;
}
static void stop_music() {
if (music_source) {
std::lock_guard _(music_source->get_lock());
size_t l =music_source->length_bytes();
size_t rdpos = music_source->get_output_position_bytes();
size_t size = (rdpos < music_buffer_write_pos?0:l) + music_buffer_write_pos - rdpos;
music_source->stop_after_bytes(size);
}
music_source.reset();
}
void create_new_music_track(int freq) {
stop_music();
static int track_id = music_track_id_base;
double basef = base_freq;
double rel_speed = freq/basef;
music_buffer = new uint8_t[BACK_BUFF_SIZE];
std::memset(music_buffer, 0, BACK_BUFF_SIZE);
music_source = std::make_shared<WaveSource>(
WaveSource::WavePtr(music_buffer,music_buffer_deleter),
WaveSource::Format::stereo_int16, rel_speed,
BACK_BUFF_SIZE/sizeof(WaveSource::StereoInt16),0);
sound_mixer.set_track(track_id, WaveMixer<2>(music_source,{music_volume,music_volume},1.0));
track_id = track_id ^ 1;
music_buffer_write_pos = BACK_BUFF_SIZE/2;
}
static std::unique_ptr<IMusicStream> load_music_ex(const char *name) {
if (name) {
TMUSIC_STREAM *stream = music_open(name);
if (stream) {
TMUSIC_STREAM_INFO nfo = music_get_info(stream);
if (nfo.channels != 2) {
SEND_LOG("(MUSIC) Reject music - only stereo is supported");
} else if (nfo.format != 2){
SEND_LOG("(MUSIC) Reject music - only 16bit is supported");
} else {
return std::unique_ptr<IMusicStream>(static_cast<IMusicStream *>(stream));
}
}
}
return {};
} }
int mix_back_sound(int synchro) {
return 0; static void handle_end_of_song() {
current_music.reset();
if (end_of_song_callback != NULL) {
const char *new_music = end_of_song_callback(end_of_song_callback_ctx);
current_music = load_music_ex(new_music);
if (current_music) {
auto nfo = current_music->get_info();
create_new_music_track(nfo.freq);
mix_back_sound(0);
}
}
}
int mix_back_sound(int _) {
if (current_music==NULL) {
stop_music();
} else {
auto data = current_music->read();
while (!data.empty()) {
auto sz = copy_to_music_buffer(data.data(), data.size());
if (sz == 0) {
current_music->put_back(data);
break;
}
data = data.substr(sz);
if (data.empty()) data = current_music->read();
}
if (data.empty()) {
handle_end_of_song();
return mix_back_sound(_);
}
}
return 1;
} }
//int open_backsound(char *filename); //int open_backsound(char *filename);
void change_music(const char *filename) { void change_music(const char *filename) {
if (music_source) {
fade_music();
stop_music();
}
current_music = load_music_ex(filename);
if (current_music) {
auto nfo = current_music->get_info();
create_new_music_track(nfo.freq);
mix_back_sound(0);
}
} }
//char *device_name(int device); //char *device_name(int device);
@ -39,29 +253,67 @@ void change_music(const char *filename) {
//void set_backsnd_freq(int freq); //void set_backsnd_freq(int freq);
char get_channel_state(int channel) { char get_channel_state(int channel) {
return 0; char c = 0;
sound_mixer.visit_track(channel, [&](auto){c = 1;});
return c;
} }
void get_channel_volume(int channel,int *left,int *right) { void get_channel_volume(int channel,int *left,int *right) {
VolumePreset &vp = channel_volume_presset[channel];
*left = static_cast<int>(vp.left*32768.0);
*right = static_cast<int>(vp.right*32768.0);
} }
void mute_channel(int channel) { void mute_channel(int channel) {
sound_mixer.visit_track(channel, [&](WaveMixer<2> &mx){
mx.get_source()->stop();
});
} }
void chan_break_loop(int channel) { void chan_break_loop(int channel) {
sound_mixer.visit_track(channel, [&](WaveMixer<2> &mx){
mx.get_source()->break_loop();
});
} }
void chan_break_ext(int channel,const void *org_sample,int32_t size_sample) { void chan_break_ext(int channel,const void *org_sample,int32_t size_sample) {
sound_mixer.visit_track(channel, [&](WaveMixer<2> &mx){
auto src = mx.get_source();
src->break_loop(src->length_in_samples(src->get_format(), size_sample));
});
} }
char set_snd_effect(int funct,int data) {
static void update_music_volume(){
float v = music_volume * master_volume;
for (int i = 0; i <2; i++)
sound_mixer.visit_track(music_track_id_base+i,[&](WaveMixer<2> &m){
m.set_channel_volume(v, v);
});
}
char set_snd_effect(AUDIO_PROPERTY funct,int data) {
switch (funct) {
case SND_PING: break;
case SND_GFX: sound_effect_volume = data/256.0;break;
case SND_MUSIC: music_volume = data/128.0;update_music_volume();break;
case SND_GVOLUME: master_volume = data/256.0;update_music_volume();break;
case SND_SWAP: swap_channels = !!data;break;
default: return 0;
}
return 1;
}
char check_snd_effect(AUDIO_PROPERTY funct) {
if (funct == SND_PING|| funct == SND_GFX || funct == SND_MUSIC || funct == SND_GVOLUME) return 1;
return 0; return 0;
} }
char check_snd_effect(int funct) { int get_snd_effect(AUDIO_PROPERTY funct) {
return 0; switch (funct) {
} case SND_PING: return 1;
int get_snd_effect(int funct) { case SND_GFX: return static_cast<int>(sound_effect_volume * 256.0);break;
return 0; case SND_MUSIC: return static_cast<int>(music_volume * 128.0);break;
case SND_GVOLUME: return static_cast<int>(master_volume * 256.0);break;
case SND_SWAP:return swap_channels?1:0;
default: return 0;
}
} }
void *PrepareVideoSound(int mixfreq, int buffsize) { void *PrepareVideoSound(int mixfreq, int buffsize) {

View file

@ -1,15 +0,0 @@
/*
* sound.h
*
* Created on: 26. 1. 2025
* Author: ondra
*/
#ifndef PLATFORM_SDL_SOUND_H_
#define PLATFORM_SDL_SOUND_H_
#endif /* PLATFORM_SDL_SOUND_H_ */

104
platform/sdl/sound_mixer.h Normal file
View file

@ -0,0 +1,104 @@
#pragma once
#include "wave_mixer.h"
#include <algorithm>
#include <optional>
#include <vector>
template<int channels>
class SoundMixer {
public:
void mix_to_buffer(float *buffer, std::size_t samples_len) {
std::lock_guard _(_mx);
for (std::size_t i = 0; i < _tracks.size(); ++i) {
Track &t = _tracks[i];
bool playing = t.wave.output_mix(buffer, samples_len);
if (!playing) {
Track &b = _tracks.back();
if (&b != &t) std::swap(b,t);
_tracks.pop_back();
--i;
}
}
}
template<std::invocable<WaveMixer<channels> &> Fn>
bool visit_track(int trackID, Fn &&fn) {
std::lock_guard _(_mx);
Track *t = find_track_by_id(trackID);
if (t) {
fn(t->wave);
return true;
} else {
return false;
}
}
template<std::invocable<const WaveMixer<channels> &> Fn>
bool visit_track(int trackID, Fn &&fn) const {
std::lock_guard _(_mx);
const Track *t = find_track_by_id(trackID);
if (t) {
fn(t->wave);
return true;
} else {
return false;
}
}
void set_track(int trackID, WaveMixer<channels> wave) {
std::lock_guard _(_mx);
Track *t = find_track_by_id(trackID);
if (t) t->wave = std::move(wave);
else _tracks.push_back({trackID, std::move(wave)});
}
struct Track {
int trackID;
WaveMixer<channels> wave;
};
void set_tracks(Track *tracks, int count) {
std::lock_guard _(_mx);
for (int i = 0; i < count; ++i) {
Track *t = find_track_by_id(tracks[i].trackID);
if (t) t->wave = std::move(tracks[i].wave);
else _tracks.push_back(std::move(tracks[i]));
}
}
protected:
Track *find_track_by_id(int track) {
auto iter = std::find_if(_tracks.begin(), _tracks.end(), [&](const Track &t){
return t.trackID == track;
});
if (iter == _tracks.end()) {
return nullptr;
} else {
return &(*iter);
}
}
const Track *find_track_by_id(int track) const {
auto iter = std::find_if(_tracks.begin(), _tracks.end(), [&](const Track &t){
return t.trackID == track;
});
if (iter == _tracks.end()) {
return nullptr;
} else {
return &(*iter);
}
}
std::mutex _mx;
std::vector<Track> _tracks;
};

145
platform/sdl/wave_mixer.h Normal file
View file

@ -0,0 +1,145 @@
#pragma once
#include "wave_source.h"
#include <atomic>
template<typename X>
class NoCopyObject: public X {
public:
using X::X;
NoCopyObject(const NoCopyObject &other):X() {}
NoCopyObject &operator=(const NoCopyObject &other) {
return *this;
}
};
template<int channels>
class WaveMixer {
public:
enum State {
playing,
done
};
WaveMixer(std::shared_ptr<WaveSource> source)
:_src(std::move(source)) {}
WaveMixer(std::shared_ptr<WaveSource> source, const std::array<float,channels> &volumes, float speed = 1.0)
:_src(std::move(source))
,_volume{volumes}
,_speed(speed) {}
static_assert(channels > 0);
///output to buffer by adding values (mixing)
/**
* @param buffer sound buffer - add values here
* @param samples_count count of samples in buffer (so samples * channels for count of floats)
* @retval true still playing
* @retval false done playing
*/
bool output_mix(float *buffer, std::size_t samples_count) {
std::array<float, channels> vol;
float speed;
{
std::lock_guard _(_mx);
if (_state == done) {
return 0;
}
vol = _volume;
speed = _speed;
}
bool ch = false;
auto do_volume_fade = [this,&ch](int c){
float d = _target_volume[c] - _volume[c];
if (d > 0) {
d = std::min(d, _volume_change[c]);
ch = true;
}
else if (d < 0) {
d = std::max(d, -_volume_change[c]);
ch = true;
}
_volume[c] += d;
};
auto iter = buffer;
bool playing = _src->output([&](const auto &value){
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, float>) {
for (int c = 0; c < channels; ++c) {
if (_volume_fade) do_volume_fade(c);
float v = value * vol[c];
v = v + (*iter);
(*iter) = v;
++iter;
}
} else {
//TODO: handle 1 channel audio
static_assert(std::is_same_v<T, WaveSource::StereoFloat>);
if (_volume_fade) for (int c =0; c< channels; ++c) do_volume_fade(c);
if constexpr(channels == 1) {
float v = (value.left + value.right) * 0.5;
(*iter) += v * vol[0];
} else {
(*iter) += value.left * vol[0];
++iter;
(*iter) += value.right * vol[0];
++iter;
}
}
}, samples_count, speed);
_volume_fade = ch;
return playing;
}
void set_channel_volumes(const std::array<float,channels> &volumes) {
std::lock_guard _(_mx);
_volume = volumes;
}
void set_channel_volumes_fade(const std::array<float,channels> &volumes, const std::array<float,channels> &change_speed) {
std::lock_guard _(_mx);
_volume_fade = true;
_target_volume = volumes;
_volume_change = change_speed;
}
void set_channel_volume(int channel, float volume) {
std::lock_guard _(_mx);
if (channel >= 0 && channel < channels) {
_volume[channel] = volume;
}
}
void set_speed(float speed) {
std::lock_guard _(_mx);
_speed = speed;
}
std::shared_ptr<WaveSource> get_source() const {return _src;}
protected:
std::shared_ptr<WaveSource> _src;
mutable NoCopyObject<std::mutex> _mx;
std::array<float, channels> _volume = {};
std::array<float, channels> _target_volume = {};
std::array<float, channels> _volume_change = {};
bool _volume_fade = false;
float _speed = 1.0;
State _state = playing;
};

248
platform/sdl/wave_source.h Normal file
View file

@ -0,0 +1,248 @@
#pragma once
#include <atomic>
#include <cmath>
#include <cstdint>
#include <memory>
#include <mutex>
class WaveSource {
public:
using WaveDeleter = void (*)(const void *);
using WavePtr = std::unique_ptr<const void, WaveDeleter>;
struct StereoInt16 {
std::int16_t left;
std::int16_t right;
};
struct StereoFloat {
float left;
float right;
};
enum class Format {
uint8,
int8,
uint16,
int16,
stereo_int16
};
constexpr static std::size_t length_in_samples(Format f, std::size_t bytes) {
if (f == Format::stereo_int16) return bytes>>2;
if (f == Format::uint16 || f == Format::int16) return bytes>>1;
else return bytes;
}
constexpr static std::size_t length_in_bytes(Format f, std::size_t samples) {
if (f == Format::stereo_int16) return samples<<2;
if (f == Format::uint16 || f == Format::int16) return samples<<1;
else return samples;
}
Format get_format() const {return _format;}
///retrieve overall play time in samples of source wave
/**
* @return play time in samples - this value can be non-integer - when pointer
* can point between two samples
*
*/
double get_play_time() const {
return _position;
}
///retrieves output position in samples.
/** useful for cycle buffer returns sample index in wave buffer which has been
* recently sent to the wave device, so it is save to overwrite it. As the
* @return output position in samples
*/
std::size_t get_output_position_samples() const {
std::lock_guard _(_mx);
return static_cast<std::size_t>(std::round(wrap_pos(_position - 1.0)));
}
///retrieves output position in samples.
/** useful for cycle buffer returns byte offset in wave buffer which has been
* recently sent to the wave device, so it is save to overwrite it. As the
* @return output position in bytes
*/
std::size_t get_output_position_bytes() const {
return length_in_bytes(_format,get_output_position_samples());
}
///break loop - so wave finishes current cycle and ends
void break_loop() {
std::lock_guard _(_mx);
break_loop_finish_at(_length);
}
///break loop by extending play beyond the loop - must be already defined in wave buffer
void break_loop(std::size_t final_len) {
std::lock_guard _(_mx);
break_loop_finish_at(final_len);
}
///stop playing now
void stop() {
std::lock_guard _(_mx);
_wrap_offset = _position - _length;
_loop_pos = _length;
}
///stops playing after given samples played
void stop_after_samples(std::size_t pos) {
std::lock_guard _(_mx);
_stop_at = _position + pos;
}
///stops playing after given bytes played
void stop_after_bytes(std::size_t pos) {
stop_after_samples(length_in_samples(_format, pos));
}
std::size_t length() const {return _length;}
std::size_t length_bytes() const {return length_in_bytes(_format, _length);}
///create wave source
/**
* @param data pointer to data, can be allocated or static, depend on deleter
* @param format format - one of enum
* @param speed frequency in ratio to base frequency. For instance if base frequency is 44100
* and wave frequency is 11025, then speed is 0.25
* @param len_samples count of samples
* @param loop_pos
*/
WaveSource(WavePtr data, Format format, double speed, std::size_t len_samples, std::size_t loop_pos)
:_wave(std::move(data))
,_format(format)
,_speed(speed)
,_length(len_samples)
,_loop_pos(std::min(loop_pos, len_samples))
{
}
template<std::invocable<float> Fn>
bool output(Fn &&fn, std::size_t samples, double speed = 1.0) {
std::lock_guard _(_mx);
switch (_format) {
case Format::int8: return do_output(std::forward<Fn>(fn),
reinterpret_cast<const std::int8_t *>(_wave.get()),
samples, speed) > 0;
case Format::int16: return do_output(std::forward<Fn>(fn),
reinterpret_cast<const std::int16_t *>(_wave.get()),
samples, speed) > 0;
case Format::uint8: return do_output(std::forward<Fn>(fn),
reinterpret_cast<const std::uint8_t *>(_wave.get()),
samples, speed) > 0;
case Format::uint16: return do_output(std::forward<Fn>(fn),
reinterpret_cast<const std::uint16_t *>(_wave.get()),
samples, speed) > 0;
case Format::stereo_int16: return do_output(std::forward<Fn>(fn),
reinterpret_cast<const StereoInt16 *>(_wave.get()),
samples, speed) > 0;
}
return false;
}
///for locking buffer - but probably not needed if you use memory barriers
std::recursive_mutex &get_lock() {return _mx;}
protected:
mutable std::recursive_mutex _mx;
WavePtr _wave;
Format _format;
double _speed;
double _length;
double _loop_pos;
double _position = 0;
double _wrap_offset = 0;
double _interpolation_step = 1.0;
double _stop_at = std::numeric_limits<double>::max();
constexpr float interpolate(float s1, float s2, float frac) {
return s1 + (s2 - s1) * frac;
}
constexpr float interpolate(uint8_t s1, uint8_t s2, float frac) {
float sampl1 = static_cast<float>(s1)/static_cast<float>(128)- 1.0f;
float sampl2 = static_cast<float>(s2)/static_cast<float>(128)- 1.0f;
return interpolate(sampl1, sampl2, frac);
}
constexpr float interpolate(uint16_t s1, uint16_t s2, float frac) {
float sampl1 = static_cast<float>(s1)/static_cast<float>(32768) - 1.0f;
float sampl2 = static_cast<float>(s2)/static_cast<float>(32768) - 1.0f;
return interpolate(sampl1, sampl2, frac);
}
constexpr float interpolate(int16_t s1, int16_t s2, float frac) {
float sampl1 = static_cast<float>(s1)/static_cast<float>(32768);
float sampl2 = static_cast<float>(s2)/static_cast<float>(32768);
return interpolate(sampl1, sampl2, frac);
}
constexpr StereoFloat interpolate(const StereoInt16 &s1, const StereoInt16 &s2, float frac) {
return {
interpolate(s1.left, s1.left, frac),
interpolate(s2.right, s2.right, frac)
};
}
constexpr float interpolate(int8_t s1, int8_t s2, float frac) {
float sampl1 = static_cast<float>(s1)/static_cast<float>(128);
float sampl2 = static_cast<float>(s2)/static_cast<float>(128);
return interpolate(sampl1, sampl2, frac);
}
template<typename T, typename Fn >
std::size_t do_output(Fn &&fn, const T *wave, std::size_t samples, double speed) {
float spd = speed * _speed;
for (std::size_t i = 0; i < samples; ++i) {
if (_position>= _stop_at) {
return i;
}
double p = wrap_pos(_position - _wrap_offset);
double sp;
double fp = std::modf(p,&sp);
std::size_t s1 = static_cast<std::size_t>(wrap_pos(sp));
std::size_t s2 = static_cast<std::size_t>(wrap_pos(sp+1));
if (s1 == s2) return i;
fn(interpolate(wave[s1], wave[s2], fp));
_position = _position + spd;
}
return samples;
}
double wrap_pos(double position) const {
if (position >= _length) {
if (_loop_pos == _length) {
position = _length;
} else {
position = std::fmod((position - _length),(_length - _loop_pos)) + _loop_pos;
}
}
return position;
}
void break_loop_finish_at(double position) {
double adj = wrap_pos(_position);
_wrap_offset = _position - adj;
_loop_pos = _length = position;
}
};

51
platform/sound.h Normal file
View file

@ -0,0 +1,51 @@
#pragma once
#include "../libs/music.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
SND_PING,
SND_GVOLUME,
SND_BASS,
SND_TREBL,
SND_SWAP,
SND_LSWAP,
SND_SURROUND,
SND_OUTFILTER,
SND_GFX,
SND_MUSIC,
SND_XBASS,
SND_MAXFUNCT} AUDIO_PROPERTY;
void game_sound_init_device(const INI_CONFIG_SECTION *audio_section);
char start_mixing();
void stop_mixing();
void play_sample(int channel,const void *sample,int32_t size,int32_t lstart,int32_t sfreq,int type);
void set_channel_volume(int channel,int left,int right);
void set_end_of_song_callback(const char * (*cb)(void *), void *ctx);
void fade_music();
int mix_back_sound(int synchro);
void change_music(const char *filename);
char get_channel_state(int channel);
void get_channel_volume(int channel,int *left,int *right);
void mute_channel(int channel);
void chan_break_loop(int channel);
void chan_break_ext(int channel,const void *org_sample,int32_t size_sample);
char set_snd_effect(AUDIO_PROPERTY funct,int data);
char check_snd_effect(AUDIO_PROPERTY funct);
int get_snd_effect(AUDIO_PROPERTY funct);
void *PrepareVideoSound(int mixfreq, int buffsize) ;
char LoadNextVideoFrame(void *buffer, const char *data, int size, const short *xlat, short *accnums, int32_t *writepos);
void DoneVideoSound(void *buffer);
const char *device_name(int );
#ifdef __cplusplus
}
#endif