mirror of
https://github.com/ondra-novak/gates_of_skeldal.git
synced 2025-07-05 14:10:27 -04:00
sound, music, mixer
This commit is contained in:
parent
42087c926c
commit
f8a1501289
42 changed files with 1345 additions and 157 deletions
|
@ -1,5 +1,5 @@
|
|||
#include <skeldal_win.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
|
|
@ -42,4 +42,5 @@ target_link_libraries(skeldal
|
|||
skeldal_libs
|
||||
skeldal_platform
|
||||
skeldal_sdl
|
||||
skeldal_libs
|
||||
${SDL2_LIBRARIES} pthread)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include "engine1.h"
|
||||
#include <libs/pcx.h>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include "engine1.h"
|
||||
#include <libs/pcx.h>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/gui.h>
|
||||
#include <libs/basicobj.h>
|
||||
#include "engine1.h"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/gui.h>
|
||||
#include <libs/basicobj.h>
|
||||
#include "engine1.h"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include <libs/mgifmem.h>
|
||||
#include "engine1.h"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <libs/event.h>
|
||||
#include <libs/memman.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include "engine1.h"
|
||||
#include "globals.h"
|
||||
#include "specproc.h"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <libs/memman.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/event.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include "math.h"
|
||||
#include "globals.h"
|
||||
#include "engine1.h"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <libs/bmouse.h>
|
||||
#include <libs/memman.h>
|
||||
#include <fcntl.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <stdarg.h>
|
||||
#include "globals.h"
|
||||
#include "temp_storage.h"
|
||||
|
|
|
@ -1428,12 +1428,12 @@ extern char **sound_table;
|
|||
|
||||
void init_tracks(void);
|
||||
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);
|
||||
const char *get_next_music_from_playlist(void);
|
||||
const char * end_of_song_callback(void *ctx);
|
||||
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 stop_track(int track);
|
||||
char test_playing(int track);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include "engine1.h"
|
||||
#include <libs/pcx.h>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/memman.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include <ctype.h>
|
||||
#include <libs/gui.h>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/gui.h>
|
||||
#include <libs/basicobj.h>
|
||||
#include "engine1.h"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include "engine1.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 (~(p->bit16) & up) {
|
||||
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 {
|
||||
play_effekt(0,0,0,0,-1,-1,p);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include <stdarg.h>
|
||||
#include "engine1.h"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include "engine1.h"
|
||||
#include <libs/pcx.h>
|
||||
|
@ -185,7 +185,6 @@ int load_map(char *filename)
|
|||
mob_template=NULL;
|
||||
mob_size=0;
|
||||
if (f==NULL) return -1;
|
||||
if (snd_devnum==DEV_DAC) stop_mixing();
|
||||
do
|
||||
{
|
||||
r=load_section(f,&temp,§,&size);
|
||||
|
@ -329,7 +328,6 @@ int load_map(char *filename)
|
|||
exit(0);
|
||||
}
|
||||
doNotLoadMapState=0;
|
||||
if (snd_devnum==DEV_DAC) start_mixing();
|
||||
return suc;
|
||||
}
|
||||
|
||||
|
@ -1605,45 +1603,49 @@ void step_zoom(char smer)
|
|||
if (cur_mode==MD_GAME) recalc_volumes(viewsector,viewdir);
|
||||
}
|
||||
|
||||
void turn_zoom(int smer)
|
||||
{
|
||||
if (running_anm) return;
|
||||
if (pass_zavora) return;else pass_zavora=1;
|
||||
if (!GlobEvent(MAGLOB_ONTURN,viewsector,viewdir)) return;
|
||||
if (set_halucination) do_halucinace();
|
||||
norefresh=1;
|
||||
hold_timer(TM_BACK_MUSIC,1);
|
||||
viewdir=(viewdir+smer)&3;
|
||||
render_scene(viewsector,viewdir);
|
||||
hide_ms_at(387);
|
||||
OutBuffer2nd();
|
||||
other_draw();
|
||||
bott_draw(0);
|
||||
if (smer==1)
|
||||
{
|
||||
anim_sipky(H_SIPKY_SV,1);
|
||||
anim_sipky(0,255);
|
||||
turn_left();
|
||||
}
|
||||
else
|
||||
{
|
||||
anim_sipky(H_SIPKY_SZ,1);
|
||||
anim_sipky(0,255);
|
||||
turn_right();
|
||||
}
|
||||
chod_s_postavama(0);
|
||||
if (battle || (game_extras & EX_ALWAYS_MINIMAP)) draw_medium_map();
|
||||
update_mysky();
|
||||
ukaz_mysku();
|
||||
showview(0,0,0,0);
|
||||
recalc_volumes(viewsector,viewdir);
|
||||
if (!battle) calc_game();
|
||||
norefresh=0;
|
||||
cancel_render=1;
|
||||
hold_timer(TM_BACK_MUSIC,0);
|
||||
mix_back_sound(0);
|
||||
pass_zavora=0;
|
||||
}
|
||||
void turn_zoom(int smer) {
|
||||
if (running_anm)
|
||||
return;
|
||||
if (pass_zavora)
|
||||
return;
|
||||
else
|
||||
pass_zavora = 1;
|
||||
if (!GlobEvent(MAGLOB_ONTURN, viewsector, viewdir))
|
||||
return;
|
||||
if (set_halucination)
|
||||
do_halucinace();
|
||||
norefresh = 1;
|
||||
hold_timer(TM_BACK_MUSIC, 1);
|
||||
viewdir = (viewdir + smer) & 3;
|
||||
recalc_volumes(viewsector, viewdir);
|
||||
render_scene(viewsector, viewdir);
|
||||
hide_ms_at(387);
|
||||
OutBuffer2nd();
|
||||
other_draw();
|
||||
bott_draw(0);
|
||||
if (smer == 1) {
|
||||
anim_sipky(H_SIPKY_SV, 1);
|
||||
anim_sipky(0, 255);
|
||||
turn_left();
|
||||
} else {
|
||||
anim_sipky(H_SIPKY_SZ, 1);
|
||||
anim_sipky(0, 255);
|
||||
turn_right();
|
||||
}
|
||||
chod_s_postavama(0);
|
||||
if (battle || (game_extras & EX_ALWAYS_MINIMAP))
|
||||
draw_medium_map();
|
||||
update_mysky();
|
||||
ukaz_mysku();
|
||||
showview(0, 0, 0, 0);
|
||||
if (!battle)
|
||||
calc_game();
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include "engine1.h"
|
||||
#include <libs/pcx.h>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <libs/event.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/memman.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include <libs/gui.h>
|
||||
#include <libs/basicobj.h>
|
||||
|
@ -87,8 +87,6 @@ THUMAN postavy[POCET_POSTAV],postavy_save[POCET_POSTAV];
|
|||
void (*unwire_proc)(void);
|
||||
void (*wire_proc)(void);
|
||||
char cur_mode,battle_mode;
|
||||
static int init_music_vol=127;
|
||||
static int init_gfx_vol=255;
|
||||
static char titles_on=0;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -984,7 +963,8 @@ void init_skeldal(const INI_CONFIG *cfg)
|
|||
|
||||
add_game_window();
|
||||
|
||||
music_init();
|
||||
game_sound_init_device(ini_section_open(cfg, "audio"));
|
||||
start_mixing();
|
||||
|
||||
if ((verr=init_mysky())!=0)
|
||||
{
|
||||
|
@ -1267,7 +1247,7 @@ void play_anim(int anim_num)
|
|||
TSTR_LIST titl=NULL;
|
||||
const char *s = build_pathname(2,gpathtable[SR_VIDEO], texty[anim_num]);
|
||||
s = local_strdup(s);
|
||||
if (snd_devnum==DEV_NOSOUND || titles_on)
|
||||
if (titles_on)
|
||||
{
|
||||
concat(t,s," ");
|
||||
z=strrchr(t,'.');
|
||||
|
@ -1344,8 +1324,8 @@ static void game_big_circle(char enforced)
|
|||
{viewdir=i;break;}
|
||||
}
|
||||
}
|
||||
for(r=0;r<mapsize*4;r++) call_macro(r,MC_STARTLEV);
|
||||
recalc_volumes(viewsector,viewdir);
|
||||
for(r=0;r<mapsize*4;r++) call_macro(r,MC_STARTLEV);
|
||||
loadlevel.name[0]=0;
|
||||
reroll_all_shops();
|
||||
|
||||
|
|
209
game/sndandmus.c
209
game/sndandmus.c
|
@ -3,7 +3,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <libs/memman.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/wav_mem.h>
|
||||
#include <libs/event.h>
|
||||
#include "globals.h"
|
||||
|
@ -29,13 +29,27 @@ typedef struct snd_info
|
|||
{
|
||||
const TMA_SOUND *data; //4
|
||||
short xpos,ypos,side; //10
|
||||
word volume,block; //14
|
||||
word volume,sample_block; //14
|
||||
word sector;
|
||||
}SND_INFO;
|
||||
|
||||
static short chan_state[CHANNELS];
|
||||
static short track_state[TRACKS];
|
||||
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 int wav_last_size;
|
||||
static int mute_task=-1;
|
||||
|
@ -140,7 +154,7 @@ void release_channel(int channel)
|
|||
if (i==-1) return;
|
||||
mute_channel(channel);
|
||||
{
|
||||
aunlock(playings[channel].block);
|
||||
aunlock(playings[channel].sample_block);
|
||||
chan_state[channel]=-1;
|
||||
track_state[i]=-1;
|
||||
}
|
||||
|
@ -158,7 +172,62 @@ int calc_volume(int *x,int *y,int side)
|
|||
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 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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*/
|
||||
const void *wav_load(const void *p, int32_t *s)
|
||||
{
|
||||
const char *sr;
|
||||
|
@ -220,7 +289,7 @@ const void *wav_load(const void *p, int32_t *s)
|
|||
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 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;
|
||||
|
||||
if (!sound_enabled) return;
|
||||
side;
|
||||
chan=find_free_channel(p->soundid);
|
||||
release_channel(chan);
|
||||
track=&tracks[p->soundid];
|
||||
track->data=p;
|
||||
track->xpos=xd;
|
||||
track->ypos=yd;
|
||||
track->side=sided;
|
||||
track->side=side;
|
||||
track->sector = sector;
|
||||
track_state[p->soundid]=-1;
|
||||
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 {
|
||||
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);
|
||||
|
@ -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].xpos=xd;
|
||||
playings[chan].ypos=yd;
|
||||
playings[chan].side=sided;
|
||||
playings[chan].sector = sector;
|
||||
playings[chan].side=side;
|
||||
playings[chan].volume=p->volume;
|
||||
playings[chan].block=blockid;
|
||||
playings[chan].sample_block=blockid;
|
||||
chan_state[chan]=p->soundid;
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
|
@ -275,16 +408,29 @@ void recalc_volumes(int sector,int side)
|
|||
|
||||
side;
|
||||
SEND_LOG("(SOUND) %s","Recalculating volumes");
|
||||
build_dungeon_sound_map(sector, side);
|
||||
newx=map_coord[sector].x;
|
||||
newy=map_coord[sector].y;
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
@ -360,7 +507,7 @@ const char *get_next_music_from_playlist()
|
|||
if (!remain_play)
|
||||
for(i=0;cur_playlist[i]!=NULL;remain_play++,i++) cur_playlist[i][0]=32;
|
||||
if (play_list_mode==PL_RANDOM)
|
||||
step=rand()*(playlist_size-1)/32768+1;
|
||||
step=rnd(playlist_size)+1;
|
||||
else
|
||||
step=1;
|
||||
i=playing_track;
|
||||
|
@ -387,24 +534,22 @@ void purge_playlist()
|
|||
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;
|
||||
struct t_wave *p;
|
||||
int siz;
|
||||
int oldtrack;
|
||||
|
||||
if (!sound_enabled) return;
|
||||
if (map_coord[sector1].layer!=map_coord[sector2].layer) return;
|
||||
x=map_coord[sector1].x;
|
||||
y=map_coord[sector1].y;
|
||||
xd=map_coord[sector2].x;
|
||||
yd=map_coord[sector2].y;
|
||||
if (map_coord[listener].layer!=map_coord[source].layer) return;
|
||||
xd=map_coord[source].x;
|
||||
yd=map_coord[source].y;
|
||||
chan=find_free_channel(track);
|
||||
oldtrack=track_state[track];
|
||||
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)
|
||||
{
|
||||
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].side=viewdir;
|
||||
playings[chan].volume=100;
|
||||
playings[chan].block=sample;
|
||||
playings[chan].sample_block=sample;
|
||||
playings[chan].sector=source;
|
||||
chan_state[chan]=track;
|
||||
track_state[track]=chan;
|
||||
}
|
||||
|
@ -519,7 +665,8 @@ char test_playing(int track)
|
|||
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)
|
||||
{
|
||||
|
@ -534,8 +681,9 @@ void start_play_flute(char note)
|
|||
q=ablock(H_FLETNA);
|
||||
w=q;w+=sizeof(struct t_wave)+4;
|
||||
vol*=SND_EFF_MAXVOL/100;
|
||||
set_channel_volume(flute_canal,vol,vol);
|
||||
play_sample(flute_canal,w,0x1665,0xADE,(int)(realfrq+0.5),1);
|
||||
int ch = flute_channel+flute_channel_offset;
|
||||
set_channel_volume(ch,vol,vol);
|
||||
play_sample(ch,w,0x1665,0xADE,(int)(realfrq+0.5),1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -552,8 +700,9 @@ void stop_play_flute()
|
|||
{
|
||||
q=ablock(H_FLETNA);
|
||||
w=q;w+=sizeof(struct t_wave);
|
||||
chan_break_ext(flute_canal,w+4,*(int *)w);
|
||||
flute_canal^=1;
|
||||
int ch = flute_channel+flute_channel_offset;
|
||||
chan_break_ext(ch,w+4,*(int *)w);
|
||||
flute_channel_offset = (flute_channel_offset+1) & 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libs/event.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/bgraph.h>
|
||||
#include <libs/bmouse.h>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <libs/devices.h>
|
||||
#include <libs/bmouse.h>
|
||||
#include <libs/memman.h>
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
#include <libs/strlite.h>
|
||||
#include <libs/gui.h>
|
||||
#include <libs/basicobj.h>
|
||||
|
|
|
@ -13,7 +13,8 @@ SET(files basicobj.c
|
|||
pcx.c
|
||||
strlite.c
|
||||
wav_mem.c
|
||||
strlists.c
|
||||
strlists.c
|
||||
music.cpp
|
||||
swaper.c )
|
||||
|
||||
add_library(skeldal_libs ${files})
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
#include <libs/bgraph.h>
|
||||
#include <libs/memman.h>
|
||||
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
|
||||
//#include <vesa.h>
|
||||
//#include <i86.h>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "memman.h"
|
||||
#include "mem.h"
|
||||
#include "mgifmem.h"
|
||||
#include <libs/zvuk.h>
|
||||
#include <platform/sound.h>
|
||||
|
||||
static MGIF_HEADER_T *mgif_header;
|
||||
|
||||
|
|
161
libs/music.cpp
Normal file
161
libs/music.cpp
Normal 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
51
libs/music.h
Normal 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
|
|
@ -75,6 +75,7 @@ int stricmp(const char *a, const char *b);
|
|||
void strupr(char *c);
|
||||
const char * int2ascii(int i, char *c, int radix);
|
||||
|
||||
int get_timer_value(void);
|
||||
uint32_t get_game_tick_count(void);
|
||||
void sleep_ms(uint32_t);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ static uint16_t screen_pitch = 640;
|
|||
|
||||
char game_display_init(const INI_CONFIG_SECTION *display_section, const char *title) {
|
||||
|
||||
SDLContext::Config cfg = {};
|
||||
SDLContext::VideoConfig cfg = {};
|
||||
const char *aspect_str;
|
||||
|
||||
aspect_str = ini_get_string(display_section, "aspect_ratio", "4:3");
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
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)
|
||||
|
|
|
@ -24,6 +24,11 @@ void SDLContext::SDL_Deleter::operator ()(SDL_Texture* 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 {
|
||||
|
||||
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];
|
||||
static Uint32 update_request_event = SDL_RegisterEvents(1);
|
||||
_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) {
|
||||
_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();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public:
|
|||
|
||||
SDLContext();
|
||||
|
||||
struct Config {
|
||||
struct VideoConfig {
|
||||
int window_width;
|
||||
int window_height;
|
||||
bool crt_filter;
|
||||
|
@ -26,11 +26,22 @@ public:
|
|||
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();
|
||||
|
||||
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 swap_render_buffers();
|
||||
|
@ -99,6 +110,12 @@ protected:
|
|||
slide_transition
|
||||
};
|
||||
|
||||
struct SDL_Audio_Deleter {
|
||||
void operator()(void *x);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
MS_EVENT ms_event;
|
||||
mutable std::mutex _mx;
|
||||
|
@ -112,6 +129,7 @@ protected:
|
|||
std::unique_ptr<SDL_Texture, SDL_Deleter> _texture;
|
||||
std::unique_ptr<SDL_Texture, SDL_Deleter> _texture2;
|
||||
std::unique_ptr<SDL_Texture, SDL_Deleter> _crt_effect;
|
||||
std::unique_ptr<void, SDL_Audio_Deleter> _audio;
|
||||
SDL_Texture *_visible_texture;
|
||||
SDL_Texture *_hidden_texture;
|
||||
|
||||
|
@ -125,6 +143,7 @@ protected:
|
|||
std::atomic<bool> _quit_requested = false;
|
||||
|
||||
|
||||
|
||||
std::vector<char> _display_update_queue;
|
||||
using QueueIter = const char *;
|
||||
std::queue<uint16_t> _keyboard_queue;
|
||||
|
|
|
@ -1,36 +1,250 @@
|
|||
#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>
|
||||
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() {
|
||||
return 0;
|
||||
get_sdl_global_context().pause_audio(false);
|
||||
return 0;
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
|
||||
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() {
|
||||
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);
|
||||
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);
|
||||
|
@ -39,29 +253,67 @@ void change_music(const char *filename) {
|
|||
//void set_backsnd_freq(int freq);
|
||||
|
||||
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) {
|
||||
|
||||
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) {
|
||||
|
||||
sound_mixer.visit_track(channel, [&](WaveMixer<2> &mx){
|
||||
mx.get_source()->stop();
|
||||
});
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
char check_snd_effect(int funct) {
|
||||
return 0;
|
||||
}
|
||||
int get_snd_effect(int funct) {
|
||||
return 0;
|
||||
int get_snd_effect(AUDIO_PROPERTY funct) {
|
||||
switch (funct) {
|
||||
case SND_PING: return 1;
|
||||
case SND_GFX: return static_cast<int>(sound_effect_volume * 256.0);break;
|
||||
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) {
|
||||
|
|
|
@ -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
104
platform/sdl/sound_mixer.h
Normal 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
145
platform/sdl/wave_mixer.h
Normal 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
248
platform/sdl/wave_source.h
Normal 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
51
platform/sound.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue