audio: add a few enhancements

main
Dominik Madarász 2023-12-12 11:56:44 +01:00
parent 427c957e49
commit 4c71f9899e
11 changed files with 184 additions and 21 deletions

View File

@ -33,6 +33,7 @@ if "%1"=="help" (
echo %0 [v4web] ; sync v4 website
echo %0 [swap] ; toggle #line directives on/off
echo %0 [split^|join] ; engine/v4k* ^>split^> engine/split/* or engine/split/* ^>join^> engine/v4k*
echo %0 [3rd] ; join 3rd parties together
echo %0 [lua] ; execute lua script with v4k
echo %0 [amalgamation] ; combine engine/v4k* into a single-header file
echo %0 [prep] ; combine split files into a single-header file, ready for use
@ -253,6 +254,10 @@ if "%1"=="join" (
call tools\join
exit /b
)
if "%1"=="3rd" (
call tools\join_3rd
exit /b
)
if "%1"=="swap" (
echo Swapping #line on v4k.h
call tools\linswap engine\v4k.h

View File

@ -2083,6 +2083,8 @@ typedef struct audio_handle* audio_t;
int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch );
int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan );
int audio_stop( audio_t a );
void audio_loop( audio_t a, bool loop );
bool audio_playing( audio_t a );
float audio_volume_clip(float gain);
float audio_volume_stream(float gain);
float audio_volume_master(float gain);
@ -3349,7 +3351,7 @@ unsigned play;
bool paused;
struct atlas_t *a;
} sprite_t;
enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on_L__LINE__ : !!(10 <= 255); } static_assert_on_Lconcat(_L,3973)___COUNTER__; typedef struct { unsigned static_assert_on_L__LINE__ : !!(sizeof(sprite_t)); } static_assert_on_Lconcat(_L,3973)___COUNTER__;;
enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on_L__LINE__ : !!(10 <= 255); } static_assert_on_Lconcat(_L,3975)___COUNTER__; typedef struct { unsigned static_assert_on_L__LINE__ : !!(sizeof(sprite_t)); } static_assert_on_Lconcat(_L,3975)___COUNTER__;;
void sprite_ctor(sprite_t *s);
void sprite_dtor(sprite_t *s);
void sprite_tick(sprite_t *s);

View File

@ -15636,6 +15636,8 @@ API int audio_play_gain( audio_t a, int flags, float gain/*0*/ );
API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch/*1*/ );
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API void audio_loop( audio_t a, bool loop );
API bool audio_playing( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case
@ -78392,6 +78394,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// See the example at the end of the file.
//
// VERSION HISTORY
// 0.03 (2022-12-12) add an ability to stop audio stream via callback, add a method to check if voice is already stopped
// 0.02 (2022-05-10) allow voice queueing in same channel. ie, chain another sample on same voice channel after current sample playback is done (@r-lyeh)
// 0.01 (2016-05-01) initial version
//
@ -78440,7 +78443,7 @@ typedef struct {
//
// The callback which will be called when the stream needs more data.
typedef void (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
typedef bool (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
typedef struct {
void* userdata; // a userdata pointer which will passed to the callback
@ -78508,6 +78511,12 @@ int sts_mixer_play_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream, float
// Stops voice with the given voice no. You can pass the returned number of sts_mixer_play_sample / sts_mixer_play_stream here.
void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice);
// Returns whether the given sample has already stopped playing.
bool sts_mixer_sample_stopped(sts_mixer_t* mixer, sts_mixer_sample_t* sample);
// Returns whether the given stream has already stopped playing.
bool sts_mixer_stream_stopped(sts_mixer_t* mixer, sts_mixer_stream_t* stream);
// Stops all voices playing the given sample. Useful when you want to delete the sample and make sure it is not used anymore.
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample);
@ -78650,6 +78659,19 @@ void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice) {
if (voice >= 0 && voice < STS_MIXER_VOICES) sts_mixer__reset_voice(mixer, voice);
}
bool sts_mixer_sample_stopped(sts_mixer_t* mixer, sts_mixer_sample_t* sample) {
for (int i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].sample == sample && mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) return false;
}
return true;
}
bool sts_mixer_stream_stopped(sts_mixer_t* mixer, sts_mixer_stream_t* stream) {
for (int i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].stream == stream && mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) return false;
}
return true;
}
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample) {
int i;
@ -78699,9 +78721,13 @@ void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples)
position = ((int)voice->position) * 2;
if (position >= voice->stream->sample.length) {
// buffer empty...refill
voice->stream->callback(&voice->stream->sample, voice->stream->userdata);
voice->position = 0.0f;
position = 0;
if (voice->stream->callback(&voice->stream->sample, voice->stream->userdata)) {
voice->position = 0.0f;
position = 0;
} else {
sts_mixer__reset_voice(mixer, i);
continue;
}
}
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
@ -353847,6 +353873,7 @@ typedef struct {
float dataf[4096*2];
};
bool rewind;
bool loop;
} mystream_t;
static void downsample_to_mono_flt( int channels, float *buffer, int samples ) {
@ -353871,7 +353898,7 @@ static void downsample_to_mono_s16( int channels, short *buffer, int samples ) {
}
// the callback to refill the (stereo) stream data
static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
static bool refill_stream(sts_mixer_sample_t* sample, void* userdata) {
mystream_t* stream = (mystream_t*)userdata;
switch( stream->type ) {
default:
@ -353880,6 +353907,7 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, ma_dr_wav_seek_to_pcm_frame(&stream->wav, 0);
if (ma_dr_wav_read_pcm_frames_s16(&stream->wav, sl, (short*)stream->data) < sl) {
ma_dr_wav_seek_to_pcm_frame(&stream->wav, 0);
if (!stream->loop) return false;
}
}
break; case MP3: {
@ -353887,6 +353915,7 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, ma_dr_mp3_seek_to_pcm_frame(&stream->mp3_, 0);
if (ma_dr_mp3_read_pcm_frames_f32(&stream->mp3_, sl, stream->dataf) < sl) {
ma_dr_mp3_seek_to_pcm_frame(&stream->mp3_, 0);
if (!stream->loop) return false;
}
}
break; case OGG: {
@ -353894,9 +353923,12 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, stb_vorbis_seek(stream->ogg, 0);
if( stb_vorbis_get_samples_short_interleaved(ogg, 2, (short*)stream->data, sample->length) == 0 ) {
stb_vorbis_seek(stream->ogg, 0);
if (!stream->loop) return false;
}
}
}
return true;
}
static void reset_stream(mystream_t* stream) {
if( stream ) memset( stream->data, 0, sizeof(stream->data) ), stream->rewind = 1;
@ -353910,6 +353942,7 @@ static bool load_stream(mystream_t* stream, const char *filename) {
int error;
int HZ = 44100;
stream->type = UNK;
stream->loop = true;
if( stream->type == UNK && (stream->ogg = stb_vorbis_open_memory((const unsigned char *)data, datalen, &error, NULL)) ) {
stb_vorbis_info info = stb_vorbis_get_info(stream->ogg);
if( info.channels != 2 ) { puts("cannot stream ogg file. stereo required."); goto end; } // @fixme: upsample
@ -354221,6 +354254,22 @@ int audio_stop( audio_t a ) {
return 1;
}
void audio_loop( audio_t a, bool loop ) {
if ( a->is_stream ) {
a->stream.loop = loop;
}
}
bool audio_playing( audio_t a ) {
if( a->is_clip ) {
return !sts_mixer_sample_stopped(&mixer, &a->clip);
}
if( a->is_stream ) {
return !sts_mixer_stream_stopped(&mixer, &a->stream.stream);
}
return false;
}
// -----------------------------------------------------------------------------
// audio queue
@ -354248,7 +354297,7 @@ static void audio_queue_init() {
do_once thread_queue_init(&queue_mutex, countof(audio_queues), audio_queues, 0);
}
static void audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
static bool audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
(void)userdata;
int sl = sample->length / 2; // 2 ch
@ -354272,6 +354321,8 @@ static void audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
aq = 0;
}
} while( bytes > 0 );
return 1;
}
static int audio_queue_voice = -1;

View File

@ -18,6 +18,7 @@
// See the example at the end of the file.
//
// VERSION HISTORY
// 0.03 (2022-12-12) add an ability to stop audio stream via callback, add a method to check if voice is already stopped
// 0.02 (2022-05-10) allow voice queueing in same channel. ie, chain another sample on same voice channel after current sample playback is done (@r-lyeh)
// 0.01 (2016-05-01) initial version
//
@ -66,7 +67,7 @@ typedef struct {
//
// The callback which will be called when the stream needs more data.
typedef void (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
typedef bool (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
typedef struct {
void* userdata; // a userdata pointer which will passed to the callback
@ -134,6 +135,12 @@ int sts_mixer_play_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream, float
// Stops voice with the given voice no. You can pass the returned number of sts_mixer_play_sample / sts_mixer_play_stream here.
void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice);
// Returns whether the given sample has already stopped playing.
bool sts_mixer_sample_stopped(sts_mixer_t* mixer, sts_mixer_sample_t* sample);
// Returns whether the given stream has already stopped playing.
bool sts_mixer_stream_stopped(sts_mixer_t* mixer, sts_mixer_stream_t* stream);
// Stops all voices playing the given sample. Useful when you want to delete the sample and make sure it is not used anymore.
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample);
@ -276,6 +283,19 @@ void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice) {
if (voice >= 0 && voice < STS_MIXER_VOICES) sts_mixer__reset_voice(mixer, voice);
}
bool sts_mixer_sample_stopped(sts_mixer_t* mixer, sts_mixer_sample_t* sample) {
for (int i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].sample == sample && mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) return false;
}
return true;
}
bool sts_mixer_stream_stopped(sts_mixer_t* mixer, sts_mixer_stream_t* stream) {
for (int i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].stream == stream && mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) return false;
}
return true;
}
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample) {
int i;
@ -325,9 +345,13 @@ void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples)
position = ((int)voice->position) * 2;
if (position >= voice->stream->sample.length) {
// buffer empty...refill
voice->stream->callback(&voice->stream->sample, voice->stream->userdata);
voice->position = 0.0f;
position = 0;
if (voice->stream->callback(&voice->stream->sample, voice->stream->userdata)) {
voice->position = 0.0f;
position = 0;
} else {
sts_mixer__reset_voice(mixer, i);
continue;
}
}
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);

View File

@ -70,6 +70,7 @@ typedef struct {
float dataf[4096*2];
};
bool rewind;
bool loop;
} mystream_t;
static void downsample_to_mono_flt( int channels, float *buffer, int samples ) {
@ -94,7 +95,7 @@ static void downsample_to_mono_s16( int channels, short *buffer, int samples ) {
}
// the callback to refill the (stereo) stream data
static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
static bool refill_stream(sts_mixer_sample_t* sample, void* userdata) {
mystream_t* stream = (mystream_t*)userdata;
switch( stream->type ) {
default:
@ -103,6 +104,7 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, ma_dr_wav_seek_to_pcm_frame(&stream->wav, 0);
if (ma_dr_wav_read_pcm_frames_s16(&stream->wav, sl, (short*)stream->data) < sl) {
ma_dr_wav_seek_to_pcm_frame(&stream->wav, 0);
if (!stream->loop) return false;
}
}
break; case MP3: {
@ -110,6 +112,7 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, ma_dr_mp3_seek_to_pcm_frame(&stream->mp3_, 0);
if (ma_dr_mp3_read_pcm_frames_f32(&stream->mp3_, sl, stream->dataf) < sl) {
ma_dr_mp3_seek_to_pcm_frame(&stream->mp3_, 0);
if (!stream->loop) return false;
}
}
break; case OGG: {
@ -117,9 +120,12 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, stb_vorbis_seek(stream->ogg, 0);
if( stb_vorbis_get_samples_short_interleaved(ogg, 2, (short*)stream->data, sample->length) == 0 ) {
stb_vorbis_seek(stream->ogg, 0);
if (!stream->loop) return false;
}
}
}
return true;
}
static void reset_stream(mystream_t* stream) {
if( stream ) memset( stream->data, 0, sizeof(stream->data) ), stream->rewind = 1;
@ -133,6 +139,7 @@ static bool load_stream(mystream_t* stream, const char *filename) {
int error;
int HZ = 44100;
stream->type = UNK;
stream->loop = true;
if( stream->type == UNK && (stream->ogg = stb_vorbis_open_memory((const unsigned char *)data, datalen, &error, NULL)) ) {
stb_vorbis_info info = stb_vorbis_get_info(stream->ogg);
if( info.channels != 2 ) { puts("cannot stream ogg file. stereo required."); goto end; } // @fixme: upsample
@ -444,6 +451,22 @@ int audio_stop( audio_t a ) {
return 1;
}
void audio_loop( audio_t a, bool loop ) {
if ( a->is_stream ) {
a->stream.loop = loop;
}
}
bool audio_playing( audio_t a ) {
if( a->is_clip ) {
return !sts_mixer_sample_stopped(&mixer, &a->clip);
}
if( a->is_stream ) {
return !sts_mixer_stream_stopped(&mixer, &a->stream.stream);
}
return false;
}
// -----------------------------------------------------------------------------
// audio queue
@ -471,7 +494,7 @@ static void audio_queue_init() {
do_once thread_queue_init(&queue_mutex, countof(audio_queues), audio_queues, 0);
}
static void audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
static bool audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
(void)userdata;
int sl = sample->length / 2; // 2 ch
@ -495,6 +518,8 @@ static void audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
aq = 0;
}
} while( bytes > 0 );
return 1;
}
static int audio_queue_voice = -1;

View File

@ -23,6 +23,8 @@ API int audio_play_gain( audio_t a, int flags, float gain/*0*/ );
API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch/*1*/ );
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API void audio_loop( audio_t a, bool loop );
API bool audio_playing( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case

View File

@ -59542,6 +59542,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// See the example at the end of the file.
//
// VERSION HISTORY
// 0.03 (2022-12-12) add an ability to stop audio stream via callback, add a method to check if voice is already stopped
// 0.02 (2022-05-10) allow voice queueing in same channel. ie, chain another sample on same voice channel after current sample playback is done (@r-lyeh)
// 0.01 (2016-05-01) initial version
//
@ -59590,7 +59591,7 @@ typedef struct {
//
// The callback which will be called when the stream needs more data.
typedef void (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
typedef bool (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
typedef struct {
void* userdata; // a userdata pointer which will passed to the callback
@ -59658,6 +59659,12 @@ int sts_mixer_play_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream, float
// Stops voice with the given voice no. You can pass the returned number of sts_mixer_play_sample / sts_mixer_play_stream here.
void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice);
// Returns whether the given sample has already stopped playing.
bool sts_mixer_sample_stopped(sts_mixer_t* mixer, sts_mixer_sample_t* sample);
// Returns whether the given stream has already stopped playing.
bool sts_mixer_stream_stopped(sts_mixer_t* mixer, sts_mixer_stream_t* stream);
// Stops all voices playing the given sample. Useful when you want to delete the sample and make sure it is not used anymore.
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample);
@ -59800,6 +59807,19 @@ void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice) {
if (voice >= 0 && voice < STS_MIXER_VOICES) sts_mixer__reset_voice(mixer, voice);
}
bool sts_mixer_sample_stopped(sts_mixer_t* mixer, sts_mixer_sample_t* sample) {
for (int i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].sample == sample && mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) return false;
}
return true;
}
bool sts_mixer_stream_stopped(sts_mixer_t* mixer, sts_mixer_stream_t* stream) {
for (int i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].stream == stream && mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) return false;
}
return true;
}
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample) {
int i;
@ -59849,9 +59869,13 @@ void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples)
position = ((int)voice->position) * 2;
if (position >= voice->stream->sample.length) {
// buffer empty...refill
voice->stream->callback(&voice->stream->sample, voice->stream->userdata);
voice->position = 0.0f;
position = 0;
if (voice->stream->callback(&voice->stream->sample, voice->stream->userdata)) {
voice->position = 0.0f;
position = 0;
} else {
sts_mixer__reset_voice(mixer, i);
continue;
}
}
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);

View File

@ -4165,6 +4165,7 @@ typedef struct {
float dataf[4096*2];
};
bool rewind;
bool loop;
} mystream_t;
static void downsample_to_mono_flt( int channels, float *buffer, int samples ) {
@ -4189,7 +4190,7 @@ static void downsample_to_mono_s16( int channels, short *buffer, int samples ) {
}
// the callback to refill the (stereo) stream data
static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
static bool refill_stream(sts_mixer_sample_t* sample, void* userdata) {
mystream_t* stream = (mystream_t*)userdata;
switch( stream->type ) {
default:
@ -4198,6 +4199,7 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, ma_dr_wav_seek_to_pcm_frame(&stream->wav, 0);
if (ma_dr_wav_read_pcm_frames_s16(&stream->wav, sl, (short*)stream->data) < sl) {
ma_dr_wav_seek_to_pcm_frame(&stream->wav, 0);
if (!stream->loop) return false;
}
}
break; case MP3: {
@ -4205,6 +4207,7 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, ma_dr_mp3_seek_to_pcm_frame(&stream->mp3_, 0);
if (ma_dr_mp3_read_pcm_frames_f32(&stream->mp3_, sl, stream->dataf) < sl) {
ma_dr_mp3_seek_to_pcm_frame(&stream->mp3_, 0);
if (!stream->loop) return false;
}
}
break; case OGG: {
@ -4212,9 +4215,12 @@ static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
if( stream->rewind ) stream->rewind = 0, stb_vorbis_seek(stream->ogg, 0);
if( stb_vorbis_get_samples_short_interleaved(ogg, 2, (short*)stream->data, sample->length) == 0 ) {
stb_vorbis_seek(stream->ogg, 0);
if (!stream->loop) return false;
}
}
}
return true;
}
static void reset_stream(mystream_t* stream) {
if( stream ) memset( stream->data, 0, sizeof(stream->data) ), stream->rewind = 1;
@ -4228,6 +4234,7 @@ static bool load_stream(mystream_t* stream, const char *filename) {
int error;
int HZ = 44100;
stream->type = UNK;
stream->loop = true;
if( stream->type == UNK && (stream->ogg = stb_vorbis_open_memory((const unsigned char *)data, datalen, &error, NULL)) ) {
stb_vorbis_info info = stb_vorbis_get_info(stream->ogg);
if( info.channels != 2 ) { puts("cannot stream ogg file. stereo required."); goto end; } // @fixme: upsample
@ -4539,6 +4546,22 @@ int audio_stop( audio_t a ) {
return 1;
}
void audio_loop( audio_t a, bool loop ) {
if ( a->is_stream ) {
a->stream.loop = loop;
}
}
bool audio_playing( audio_t a ) {
if( a->is_clip ) {
return !sts_mixer_sample_stopped(&mixer, &a->clip);
}
if( a->is_stream ) {
return !sts_mixer_stream_stopped(&mixer, &a->stream.stream);
}
return false;
}
// -----------------------------------------------------------------------------
// audio queue
@ -4566,7 +4589,7 @@ static void audio_queue_init() {
do_once thread_queue_init(&queue_mutex, countof(audio_queues), audio_queues, 0);
}
static void audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
static bool audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
(void)userdata;
int sl = sample->length / 2; // 2 ch
@ -4590,6 +4613,8 @@ static void audio_queue_callback(sts_mixer_sample_t* sample, void* userdata) {
aq = 0;
}
} while( bytes > 0 );
return 1;
}
static int audio_queue_voice = -1;

View File

@ -1703,6 +1703,8 @@ API int audio_play_gain( audio_t a, int flags, float gain/*0*/ );
API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch/*1*/ );
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API void audio_loop( audio_t a, bool loop );
API bool audio_playing( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case

View File

@ -11,8 +11,8 @@
// - linux/tcc : tcc hello.c -lm -ldl -lpthread -lX11 -D__STDC_NO_VLA__
// - osx : cc -ObjC hello.c -framework cocoa -framework iokit -framework audiotoolbox
#define V4K_IMPLEMENTATION // unrolls single-header implementation
#include "engine/joint/v4k.h" // single-header file
// #define V4K_IMPLEMENTATION // unrolls single-header implementation
#include "engine/v4k.c" // single-header file
int main() {
// options

View File

@ -0,0 +1,3 @@
#!/bin/bash 2>nul
python tools/join.py --template engine/split/v4k.x.inl --path ./engine/split/ --output ./engine/v4k