s/audio: move to libtrx

This commit is contained in:
Marcin Kurczewski 2024-04-30 21:32:03 +02:00
parent aa00b9e86d
commit b0390747e1
13 changed files with 48 additions and 1534 deletions

View file

@ -265,9 +265,6 @@ sources = [
'src/math/math.c',
'src/math/math_misc.c',
'src/math/matrix.c',
'src/specific/s_audio.c',
'src/specific/s_audio_sample.c',
'src/specific/s_audio_stream.c',
'src/specific/s_clock.c',
'src/specific/s_fmv.c',
'src/specific/s_input.c',

View file

@ -4,8 +4,8 @@
#include "game/gameflow.h"
#include "game/sound.h"
#include "global/vars.h"
#include "specific/s_audio.h"
#include <libtrx/engine/audio.h>
#include <libtrx/filesystem.h>
#include <libtrx/log.h>
#include <libtrx/memory.h>
@ -40,8 +40,8 @@ static void Music_StopActiveStream(void)
// finished by itself. In cases where we end the streams early by hand,
// we clear the finish callback in order to avoid resuming the BGM playback
// just after we stop it.
S_Audio_StreamSoundSetFinishCallback(m_AudioStreamID, NULL, NULL);
S_Audio_StreamSoundClose(m_AudioStreamID);
Audio_Stream_SetFinishCallback(m_AudioStreamID, NULL, NULL);
Audio_Stream_Close(m_AudioStreamID);
}
static char *Music_GetTrackFileName(MUSIC_TRACK_ID track)
@ -66,12 +66,12 @@ static void Music_StreamFinished(int stream_id, void *user_data)
bool Music_Init(void)
{
return S_Audio_Init();
return Audio_Init();
}
void Music_Shutdown(void)
{
S_Audio_Shutdown();
Audio_Shutdown();
}
bool Music_Play(MUSIC_TRACK_ID track)
@ -94,7 +94,7 @@ bool Music_Play(MUSIC_TRACK_ID track)
Music_StopActiveStream();
char *file_path = Music_GetTrackFileName(track);
m_AudioStreamID = S_Audio_StreamSoundCreateFromFile(file_path);
m_AudioStreamID = Audio_Stream_CreateFromFile(file_path);
Memory_FreePointer(&file_path);
if (m_AudioStreamID < 0) {
@ -107,9 +107,8 @@ bool Music_Play(MUSIC_TRACK_ID track)
m_TrackLastPlayed = track;
}
S_Audio_StreamSoundSetVolume(m_AudioStreamID, m_MusicVolume);
S_Audio_StreamSoundSetFinishCallback(
m_AudioStreamID, Music_StreamFinished, NULL);
Audio_Stream_SetVolume(m_AudioStreamID, m_MusicVolume);
Audio_Stream_SetFinishCallback(m_AudioStreamID, Music_StreamFinished, NULL);
return true;
}
@ -129,7 +128,7 @@ bool Music_PlayLooped(MUSIC_TRACK_ID track)
Music_StopActiveStream();
char *file_path = Music_GetTrackFileName(track);
m_AudioStreamID = S_Audio_StreamSoundCreateFromFile(file_path);
m_AudioStreamID = Audio_Stream_CreateFromFile(file_path);
Memory_FreePointer(&file_path);
if (m_AudioStreamID < 0) {
@ -137,10 +136,9 @@ bool Music_PlayLooped(MUSIC_TRACK_ID track)
return false;
}
S_Audio_StreamSoundSetVolume(m_AudioStreamID, m_MusicVolume);
S_Audio_StreamSoundSetFinishCallback(
m_AudioStreamID, Music_StreamFinished, NULL);
S_Audio_StreamSoundSetIsLooped(m_AudioStreamID, true);
Audio_Stream_SetVolume(m_AudioStreamID, m_MusicVolume);
Audio_Stream_SetFinishCallback(m_AudioStreamID, Music_StreamFinished, NULL);
Audio_Stream_SetIsLooped(m_AudioStreamID, true);
m_TrackLooped = track;
@ -178,7 +176,7 @@ void Music_SetVolume(int16_t volume)
{
m_MusicVolume = volume ? (25 * volume + 5) / 255.0f : 0.0f;
if (m_AudioStreamID >= 0) {
S_Audio_StreamSoundSetVolume(m_AudioStreamID, m_MusicVolume);
Audio_Stream_SetVolume(m_AudioStreamID, m_MusicVolume);
}
}
@ -197,7 +195,7 @@ void Music_Pause(void)
if (m_AudioStreamID < 0) {
return;
}
S_Audio_StreamSoundPause(m_AudioStreamID);
Audio_Stream_Pause(m_AudioStreamID);
}
void Music_Unpause(void)
@ -205,7 +203,7 @@ void Music_Unpause(void)
if (m_AudioStreamID < 0) {
return;
}
S_Audio_StreamSoundUnpause(m_AudioStreamID);
Audio_Stream_Unpause(m_AudioStreamID);
}
MUSIC_TRACK_ID Music_GetCurrentTrack(void)
@ -233,7 +231,7 @@ double Music_GetDuration(void)
if (m_AudioStreamID < 0) {
return -1.0;
}
return S_Audio_StreamGetDuration(m_AudioStreamID);
return Audio_Stream_GetDuration(m_AudioStreamID);
}
double Music_GetTimestamp(void)
@ -241,7 +239,7 @@ double Music_GetTimestamp(void)
if (m_AudioStreamID < 0) {
return -1.0;
}
return S_Audio_StreamGetTimestamp(m_AudioStreamID);
return Audio_Stream_GetTimestamp(m_AudioStreamID);
}
bool Music_SeekTimestamp(double timestamp)
@ -249,5 +247,5 @@ bool Music_SeekTimestamp(double timestamp)
if (m_AudioStreamID < 0) {
return false;
}
return S_Audio_StreamSeekTimestamp(m_AudioStreamID, timestamp);
return Audio_Stream_SeekTimestamp(m_AudioStreamID, timestamp);
}

View file

@ -7,8 +7,8 @@
#include "global/const.h"
#include "global/vars.h"
#include "math/math.h"
#include "specific/s_audio.h"
#include <libtrx/engine/audio.h>
#include <libtrx/utils.h>
#include <math.h>
@ -204,13 +204,13 @@ bool Sound_Init(void)
m_MasterVolume = 32;
m_MasterVolumeDefault = 32;
m_SoundIsActive = S_Audio_Init();
m_SoundIsActive = Audio_Init();
return m_SoundIsActive;
}
void Sound_Shutdown(void)
{
S_Audio_Shutdown();
Audio_Shutdown();
}
void Sound_UpdateEffects(void)
@ -228,31 +228,31 @@ void Sound_UpdateEffects(void)
if (slot->flags & SOUND_FLAG_AMBIENT) {
if (slot->loudness != (uint32_t)SOUND_NOT_AUDIBLE
&& slot->sound_id != AUDIO_NO_SOUND) {
S_Audio_SampleSoundSetPan(
Audio_Sample_SetPan(
slot->sound_id, Sound_ConvertPanToDecibel(slot->pan));
S_Audio_SampleSoundSetVolume(
Audio_Sample_SetVolume(
slot->sound_id,
Sound_ConvertVolumeToDecibel(
(m_MasterVolume * slot->volume) >> 6));
} else {
if (slot->sound_id != AUDIO_NO_SOUND) {
S_Audio_SampleSoundClose(slot->sound_id);
Audio_Sample_Close(slot->sound_id);
}
Sound_ClearSlot(slot);
}
} else if (S_Audio_SampleSoundIsPlaying(slot->sound_id)) {
} else if (Audio_Sample_IsPlaying(slot->sound_id)) {
if (slot->pos != NULL) {
Sound_UpdateSlotParams(slot);
if (slot->volume > 0 && slot->sound_id != AUDIO_NO_SOUND) {
S_Audio_SampleSoundSetPan(
Audio_Sample_SetPan(
slot->sound_id, Sound_ConvertPanToDecibel(slot->pan));
S_Audio_SampleSoundSetVolume(
Audio_Sample_SetVolume(
slot->sound_id,
Sound_ConvertVolumeToDecibel(
(m_MasterVolume * slot->volume) >> 6));
} else {
if (slot->sound_id != AUDIO_NO_SOUND) {
S_Audio_SampleSoundClose(slot->sound_id);
Audio_Sample_Close(slot->sound_id);
}
Sound_ClearSlot(slot);
}
@ -352,7 +352,7 @@ bool Sound_Effect(int32_t sfx_num, const XYZ_32 *pos, uint32_t flags)
fxslot->flags &= ~SOUND_FLAG_RESTARTED;
return true;
}
fxslot->sound_id = S_Audio_SampleSoundPlay(
fxslot->sound_id = Audio_Sample_Play(
sfx_id, Sound_ConvertVolumeToDecibel(volume),
Sound_CalcPitch(pitch), Sound_ConvertPanToDecibel(pan), false);
if (fxslot->sound_id == AUDIO_NO_SOUND) {
@ -371,15 +371,15 @@ bool Sound_Effect(int32_t sfx_num, const XYZ_32 *pos, uint32_t flags)
return false;
}
if (fxslot->flags & SOUND_FLAG_RESTARTED) {
S_Audio_SampleSoundClose(fxslot->sound_id);
fxslot->sound_id = S_Audio_SampleSoundPlay(
Audio_Sample_Close(fxslot->sound_id);
fxslot->sound_id = Audio_Sample_Play(
sfx_id, Sound_ConvertVolumeToDecibel(volume),
Sound_CalcPitch(pitch), Sound_ConvertPanToDecibel(pan), false);
Sound_ClearSlotHandles(fxslot);
return true;
}
fxslot->sound_id = S_Audio_SampleSoundPlay(
fxslot->sound_id = Audio_Sample_Play(
sfx_id, Sound_ConvertVolumeToDecibel(volume),
Sound_CalcPitch(pitch), Sound_ConvertPanToDecibel(pan), false);
if (fxslot->sound_id == AUDIO_NO_SOUND) {
@ -412,7 +412,7 @@ bool Sound_Effect(int32_t sfx_num, const XYZ_32 *pos, uint32_t flags)
}
if (volume > 0) {
fxslot->sound_id = S_Audio_SampleSoundPlay(
fxslot->sound_id = Audio_Sample_Play(
sfx_id, Sound_ConvertVolumeToDecibel(volume),
Sound_CalcPitch(pitch), Sound_ConvertPanToDecibel(pan), true);
if (fxslot->sound_id == AUDIO_NO_SOUND) {
@ -446,11 +446,11 @@ bool Sound_StopEffect(int32_t sfx_num, const XYZ_32 *pos)
for (int i = 0; i < MAX_PLAYING_FX; i++) {
SOUND_SLOT *slot = &m_SFXPlaying[i];
if ((slot->flags & SOUND_FLAG_USED)
&& S_Audio_SampleSoundIsPlaying(slot->sound_id)) {
&& Audio_Sample_IsPlaying(slot->sound_id)) {
if ((!pos && slot->fxnum == sfx_num)
|| (pos && sfx_num >= 0 && slot->fxnum == sfx_num)
|| (pos && sfx_num < 0)) {
S_Audio_SampleSoundClose(slot->sound_id);
Audio_Sample_Close(slot->sound_id);
Sound_ClearSlot(slot);
return true;
}
@ -506,8 +506,8 @@ void Sound_StopAmbientSounds(void)
for (int i = 0; i < m_AmbientLookupIdx; i++) {
SOUND_SLOT *slot = &m_SFXPlaying[i];
if (S_Audio_SampleSoundIsPlaying(slot->sound_id)) {
S_Audio_SampleSoundClose(slot->sound_id);
if (Audio_Sample_IsPlaying(slot->sound_id)) {
Audio_Sample_Close(slot->sound_id);
Sound_ClearSlot(slot);
}
}
@ -516,22 +516,22 @@ void Sound_StopAmbientSounds(void)
void Sound_LoadSamples(
size_t num_samples, const char **sample_pointers, size_t *sizes)
{
S_Audio_SamplesLoad(num_samples, sample_pointers, sizes);
Audio_Sample_Load(num_samples, sample_pointers, sizes);
}
void Sound_PauseAll(void)
{
S_Audio_SampleSoundPauseAll();
Audio_Sample_PauseAll();
}
void Sound_UnpauseAll(void)
{
S_Audio_SampleSoundUnpauseAll();
Audio_Sample_UnpauseAll();
}
void Sound_StopAllSamples(void)
{
S_Audio_SampleSoundCloseAll();
Audio_Sample_CloseAll();
}
void Sound_SetMasterVolume(int8_t volume)

View file

@ -1,118 +0,0 @@
#define S_AUDIO_IMPL
#include "specific/s_audio.h"
#include <libtrx/log.h>
#include <libtrx/memory.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_error.h>
#include <SDL2/SDL_stdinc.h>
#include <stdint.h>
#include <string.h>
SDL_AudioDeviceID g_AudioDeviceID = 0;
static int32_t m_RefCount = 0;
static size_t m_WorkingBufferSize = 0;
static float *m_WorkingBuffer = NULL;
static Uint8 m_WorkingSilence = 0;
static void S_Audio_MixerCallback(void *userdata, Uint8 *stream_data, int len);
static void S_Audio_MixerCallback(void *userdata, Uint8 *stream_data, int len)
{
memset(m_WorkingBuffer, m_WorkingSilence, len);
S_Audio_StreamSoundMix(m_WorkingBuffer, len);
S_Audio_SampleSoundMix(m_WorkingBuffer, len);
memcpy(stream_data, m_WorkingBuffer, len);
}
bool S_Audio_Init(void)
{
m_RefCount++;
if (g_AudioDeviceID) {
// already initialized
return true;
}
int32_t result = SDL_Init(SDL_INIT_AUDIO);
if (result < 0) {
LOG_ERROR("Error while calling SDL_Init: 0x%lx", result);
return false;
}
S_Audio_SampleSoundInit();
S_Audio_StreamSoundInit();
SDL_AudioSpec desired;
SDL_memset(&desired, 0, sizeof(desired));
desired.freq = AUDIO_WORKING_RATE;
desired.format = AUDIO_WORKING_FORMAT;
desired.channels = AUDIO_WORKING_CHANNELS;
desired.samples = AUDIO_SAMPLES;
desired.callback = S_Audio_MixerCallback;
desired.userdata = NULL;
SDL_AudioSpec delivered;
g_AudioDeviceID = SDL_OpenAudioDevice(NULL, 0, &desired, &delivered, 0);
if (!g_AudioDeviceID) {
LOG_ERROR("Failed to open audio device: %s", SDL_GetError());
return false;
}
m_WorkingSilence = desired.silence;
m_WorkingBufferSize = desired.samples * desired.channels
* SDL_AUDIO_BITSIZE(desired.format) / 8;
m_WorkingBuffer = Memory_Alloc(m_WorkingBufferSize);
SDL_PauseAudioDevice(g_AudioDeviceID, 0);
return true;
}
bool S_Audio_Shutdown(void)
{
m_RefCount--;
if (m_RefCount > 0) {
return false;
}
S_Audio_SampleSoundShutdown();
S_Audio_StreamSoundShutdown();
if (g_AudioDeviceID) {
SDL_PauseAudioDevice(g_AudioDeviceID, 1);
SDL_CloseAudioDevice(g_AudioDeviceID);
g_AudioDeviceID = 0;
}
Memory_FreePointer(&m_WorkingBuffer);
return true;
}
int S_Audio_GetAVAudioFormat(const int sample_fmt)
{
// clang-format off
switch (sample_fmt) {
case AUDIO_U8: return AV_SAMPLE_FMT_U8;
case AUDIO_S16: return AV_SAMPLE_FMT_S16;
case AUDIO_S32: return AV_SAMPLE_FMT_S32;
case AUDIO_F32: return AV_SAMPLE_FMT_FLT;
default: return -1;
}
// clang-format on
}
int S_Audio_GetSDLAudioFormat(const enum AVSampleFormat sample_fmt)
{
// clang-format off
switch (sample_fmt) {
case AV_SAMPLE_FMT_U8: return AUDIO_U8;
case AV_SAMPLE_FMT_S16: return AUDIO_S16;
case AV_SAMPLE_FMT_S32: return AUDIO_S32;
case AV_SAMPLE_FMT_FLT: return AUDIO_F32;
default: return -1;
}
// clang-format on
}

View file

@ -1,68 +0,0 @@
#pragma once
#include <SDL2/SDL_audio.h>
#include <libavutil/samplefmt.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define AUDIO_MAX_SAMPLES 1000
#define AUDIO_MAX_ACTIVE_SAMPLES 50
#define AUDIO_MAX_ACTIVE_STREAMS 10
#define AUDIO_NO_SOUND (-1)
bool S_Audio_Init(void);
bool S_Audio_Shutdown(void);
bool S_Audio_StreamSoundPause(int sound_id);
bool S_Audio_StreamSoundUnpause(int sound_id);
int S_Audio_StreamSoundCreateFromFile(const char *path);
bool S_Audio_StreamSoundClose(int sound_id);
bool S_Audio_StreamSoundIsLooped(int sound_id);
bool S_Audio_StreamSoundSetVolume(int sound_id, float volume);
bool S_Audio_StreamSoundSetIsLooped(int sound_id, bool is_looped);
bool S_Audio_StreamSoundSetFinishCallback(
int sound_id, void (*callback)(int sound_id, void *user_data),
void *user_data);
bool S_Audio_SamplesClear(void);
bool S_Audio_SamplesLoad(size_t count, const char **contents, size_t *sizes);
int S_Audio_SampleSoundPlay(
int sample_id, int volume, float pitch, int pan, bool is_looped);
bool S_Audio_SampleSoundIsPlaying(int sound_id);
bool S_Audio_SampleSoundPause(int sound_id);
bool S_Audio_SampleSoundPauseAll(void);
bool S_Audio_SampleSoundUnpause(int sound_id);
bool S_Audio_SampleSoundUnpauseAll(void);
bool S_Audio_SampleSoundClose(int sound_id);
bool S_Audio_SampleSoundCloseAll(void);
bool S_Audio_SampleSoundSetPan(int sound_id, int pan);
bool S_Audio_SampleSoundSetVolume(int sound_id, int volume);
double S_Audio_StreamGetTimestamp(int sound_id);
double S_Audio_StreamGetDuration(int sound_id);
bool S_Audio_StreamSeekTimestamp(int sound_id, double timestamp);
#ifdef S_AUDIO_IMPL
#include <libavformat/avformat.h>
#include <SDL2/SDL.h>
#define AUDIO_WORKING_RATE 44100
#define AUDIO_WORKING_FORMAT AUDIO_F32
#define AUDIO_SAMPLES 500
#define AUDIO_WORKING_CHANNELS 2
extern SDL_AudioDeviceID g_AudioDeviceID;
int S_Audio_GetAVAudioFormat(const int sample_fmt);
int S_Audio_GetSDLAudioFormat(const enum AVSampleFormat sample_fmt);
void S_Audio_SampleSoundInit(void);
void S_Audio_SampleSoundShutdown(void);
void S_Audio_SampleSoundMix(float *dst_buffer, size_t len);
void S_Audio_StreamSoundInit(void);
void S_Audio_StreamSoundShutdown(void);
void S_Audio_StreamSoundMix(float *dst_buffer, size_t len);
#endif

View file

@ -1,674 +0,0 @@
#define S_AUDIO_IMPL
#include "specific/s_audio.h"
#include "game/shell.h"
#include <libtrx/log.h>
#include <libtrx/memory.h>
#include <SDL2/SDL_audio.h>
#include <errno.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/codec.h>
#include <libavcodec/packet.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
#include <libavutil/error.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
typedef struct AUDIO_SAMPLE {
float *sample_data;
int channels;
int num_samples;
} AUDIO_SAMPLE;
typedef struct AUDIO_SAMPLE_SOUND {
bool is_used;
bool is_looped;
bool is_playing;
float volume_l; // sample gain multiplier
float volume_r; // sample gain multiplier
float pitch;
int volume; // volume specified in hundredths of decibel
int pan; // pan specified in hundredths of decibel
// pitch shift means the same samples can be reused twice, hence float
float current_sample;
AUDIO_SAMPLE *sample;
} AUDIO_SAMPLE_SOUND;
typedef struct AUDIO_AV_BUFFER {
const char *data;
const char *ptr;
int size;
int remaining;
} AUDIO_AV_BUFFER;
static int m_LoadedSamplesCount = 0;
static AUDIO_SAMPLE m_LoadedSamples[AUDIO_MAX_SAMPLES] = { 0 };
static AUDIO_SAMPLE_SOUND m_SampleSounds[AUDIO_MAX_ACTIVE_SAMPLES] = { 0 };
static double S_Audio_DecibelToMultiplier(double db_gain)
{
return pow(2.0, db_gain / 600.0);
}
static bool S_Audio_SampleRecalculateChannelVolumes(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_SAMPLES) {
return false;
}
AUDIO_SAMPLE_SOUND *sound = &m_SampleSounds[sound_id];
sound->volume_l = S_Audio_DecibelToMultiplier(
sound->volume - (sound->pan > 0 ? sound->pan : 0));
sound->volume_r = S_Audio_DecibelToMultiplier(
sound->volume + (sound->pan < 0 ? sound->pan : 0));
return true;
}
static int S_Audio_ReadAVBuffer(void *opaque, uint8_t *dst, int dst_size)
{
AUDIO_AV_BUFFER *src = opaque;
int read = dst_size >= src->remaining ? src->remaining : dst_size;
if (!read) {
return AVERROR_EOF;
}
memcpy(dst, src->ptr, read);
src->ptr += read;
src->remaining -= read;
return read;
}
static int64_t S_Audio_SeekAVBuffer(void *opaque, int64_t offset, int whence)
{
AUDIO_AV_BUFFER *src = opaque;
if (whence & AVSEEK_SIZE) {
return src->size;
}
switch (whence) {
case SEEK_SET:
if (src->size - offset < 0) {
return AVERROR_EOF;
}
src->ptr = src->data + offset;
src->remaining = src->size - offset;
break;
case SEEK_CUR:
if (src->remaining - offset < 0) {
return AVERROR_EOF;
}
src->ptr += offset;
src->remaining -= offset;
break;
case SEEK_END:
if (src->size + offset < 0) {
return AVERROR_EOF;
}
src->ptr = src->data - offset;
src->remaining = src->size + offset;
break;
}
return src->ptr - src->data;
}
static bool S_Audio_SampleLoad(int sample_id, const char *content, size_t size)
{
if (!g_AudioDeviceID || sample_id < 0 || sample_id >= AUDIO_MAX_SAMPLES) {
return false;
}
bool ret = false;
AUDIO_SAMPLE *sample = &m_LoadedSamples[sample_id];
size_t working_buffer_size = 0;
float *working_buffer = NULL;
struct {
size_t read_buffer_size;
AVIOContext *avio_context;
AVStream *stream;
AVFormatContext *format_ctx;
const AVCodec *codec;
AVCodecContext *codec_ctx;
AVPacket *packet;
AVFrame *frame;
} av = {
.read_buffer_size = 8192,
.avio_context = NULL,
.stream = NULL,
.format_ctx = NULL,
.codec = NULL,
.codec_ctx = NULL,
.packet = NULL,
.frame = NULL,
};
struct {
int src_format;
int src_channels;
int src_sample_rate;
int dst_format;
int dst_channels;
int dst_sample_rate;
SwrContext *ctx;
} swr = { 0 };
int error_code;
unsigned char *read_buffer = av_malloc(av.read_buffer_size);
if (!read_buffer) {
error_code = AVERROR(ENOMEM);
goto cleanup;
}
AUDIO_AV_BUFFER av_buf = {
.data = content,
.ptr = content,
.size = size,
.remaining = size,
};
av.avio_context = avio_alloc_context(
read_buffer, av.read_buffer_size, 0, &av_buf, S_Audio_ReadAVBuffer,
NULL, S_Audio_SeekAVBuffer);
av.format_ctx = avformat_alloc_context();
av.format_ctx->pb = av.avio_context;
error_code =
avformat_open_input(&av.format_ctx, "dummy_filename", NULL, NULL);
if (error_code != 0) {
goto cleanup;
}
error_code = avformat_find_stream_info(av.format_ctx, NULL);
if (error_code < 0) {
goto cleanup;
}
av.stream = NULL;
for (unsigned int i = 0; i < av.format_ctx->nb_streams; i++) {
AVStream *current_stream = av.format_ctx->streams[i];
if (current_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
av.stream = current_stream;
break;
}
}
if (!av.stream) {
error_code = AVERROR_STREAM_NOT_FOUND;
goto cleanup;
}
av.codec = avcodec_find_decoder(av.stream->codecpar->codec_id);
if (!av.codec) {
error_code = AVERROR_DEMUXER_NOT_FOUND;
goto cleanup;
}
av.codec_ctx = avcodec_alloc_context3(av.codec);
if (!av.codec_ctx) {
error_code = AVERROR(ENOMEM);
goto cleanup;
}
error_code =
avcodec_parameters_to_context(av.codec_ctx, av.stream->codecpar);
if (error_code) {
goto cleanup;
}
error_code = avcodec_open2(av.codec_ctx, av.codec, NULL);
if (error_code < 0) {
goto cleanup;
}
av.packet = av_packet_alloc();
if (!av.packet) {
error_code = AVERROR(ENOMEM);
goto cleanup;
}
av.frame = av_frame_alloc();
if (!av.frame) {
error_code = AVERROR(ENOMEM);
goto cleanup;
}
while (1) {
error_code = av_read_frame(av.format_ctx, av.packet);
if (error_code == AVERROR_EOF) {
av_packet_unref(av.packet);
error_code = 0;
break;
}
if (error_code < 0) {
av_packet_unref(av.packet);
goto cleanup;
}
error_code = avcodec_send_packet(av.codec_ctx, av.packet);
if (error_code < 0) {
av_packet_unref(av.packet);
goto cleanup;
}
if (!swr.ctx) {
swr.src_sample_rate = av.codec_ctx->sample_rate;
swr.src_channels = av.codec_ctx->channels;
swr.src_format = av.codec_ctx->sample_fmt;
swr.dst_sample_rate = AUDIO_WORKING_RATE;
swr.dst_channels = 1;
swr.dst_format = S_Audio_GetAVAudioFormat(AUDIO_WORKING_FORMAT);
swr.ctx = swr_alloc_set_opts(
swr.ctx, swr.dst_channels, swr.dst_format, swr.dst_sample_rate,
swr.src_channels, swr.src_format, swr.src_sample_rate, 0, 0);
if (!swr.ctx) {
av_packet_unref(av.packet);
error_code = AVERROR(ENOMEM);
goto cleanup;
}
error_code = swr_init(swr.ctx);
if (error_code != 0) {
av_packet_unref(av.packet);
goto cleanup;
}
}
while (1) {
error_code = avcodec_receive_frame(av.codec_ctx, av.frame);
if (error_code == AVERROR(EAGAIN)) {
av_frame_unref(av.frame);
break;
}
if (error_code < 0) {
av_packet_unref(av.packet);
av_frame_unref(av.frame);
goto cleanup;
}
uint8_t *out_buffer = NULL;
const int out_samples =
swr_get_out_samples(swr.ctx, av.frame->nb_samples);
av_samples_alloc(
&out_buffer, NULL, swr.dst_channels, out_samples,
swr.dst_format, 1);
int resampled_size = swr_convert(
swr.ctx, &out_buffer, out_samples,
(const uint8_t **)av.frame->data, av.frame->nb_samples);
while (resampled_size > 0) {
int out_buffer_size = av_samples_get_buffer_size(
NULL, swr.dst_channels, resampled_size, swr.dst_format, 1);
if (out_buffer_size > 0) {
working_buffer = Memory_Realloc(
working_buffer, working_buffer_size + out_buffer_size);
if (out_buffer) {
memcpy(
(uint8_t *)working_buffer + working_buffer_size,
out_buffer, out_buffer_size);
}
working_buffer_size += out_buffer_size;
}
resampled_size =
swr_convert(swr.ctx, &out_buffer, out_samples, NULL, 0);
}
av_freep(&out_buffer);
av_frame_unref(av.frame);
}
av_packet_unref(av.packet);
}
int sample_format_bytes = av_get_bytes_per_sample(swr.dst_format);
sample->num_samples =
working_buffer_size / sample_format_bytes / swr.dst_channels;
sample->channels = swr.src_channels;
sample->sample_data = working_buffer;
ret = true;
cleanup:
if (error_code > 0) {
LOG_ERROR(
"Error while opening sample ID %d: %s", sample_id,
av_err2str(error_code));
}
if (swr.ctx) {
swr_free(&swr.ctx);
}
if (av.frame) {
av_frame_free(&av.frame);
}
if (av.packet) {
av_packet_free(&av.packet);
}
av.codec = NULL;
if (!ret) {
sample->sample_data = NULL;
sample->num_samples = 0;
sample->channels = 0;
Memory_FreePointer(&working_buffer);
}
if (av.codec_ctx) {
avcodec_close(av.codec_ctx);
av_freep(&av.codec_ctx);
}
if (av.format_ctx) {
avformat_close_input(&av.format_ctx);
}
if (av.avio_context) {
av_freep(&av.avio_context->buffer);
avio_context_free(&av.avio_context);
}
return ret;
}
void S_Audio_SampleSoundInit(void)
{
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_SAMPLES; sound_id++) {
AUDIO_SAMPLE_SOUND *sound = &m_SampleSounds[sound_id];
sound->is_used = false;
sound->is_playing = false;
sound->volume = 0.0f;
sound->pitch = 1.0f;
sound->pan = 0.0f;
sound->current_sample = 0.0f;
sound->sample = NULL;
}
}
void S_Audio_SampleSoundShutdown(void)
{
if (!g_AudioDeviceID) {
return;
}
S_Audio_SamplesClear();
}
bool S_Audio_SamplesClear(void)
{
if (!g_AudioDeviceID) {
return false;
}
S_Audio_SampleSoundCloseAll();
for (int i = 0; i < AUDIO_MAX_SAMPLES; i++) {
Memory_FreePointer(&m_LoadedSamples[i].sample_data);
}
return true;
}
bool S_Audio_SamplesLoad(size_t count, const char **contents, size_t *sizes)
{
if (!g_AudioDeviceID) {
return false;
}
if (count > AUDIO_MAX_SAMPLES) {
Shell_ExitSystemFmt(
"Trying to load too many samples (maximum supported count: %d)",
AUDIO_MAX_SAMPLES);
return false;
}
S_Audio_SamplesClear();
bool result = true;
for (int sample_id = 0; sample_id < (int)count; sample_id++) {
result &= S_Audio_SampleLoad(
sample_id, contents[sample_id], sizes[sample_id]);
}
if (result) {
m_LoadedSamplesCount = count;
} else {
S_Audio_SamplesClear();
}
return result;
}
int S_Audio_SampleSoundPlay(
int sample_id, int volume, float pitch, int pan, bool is_looped)
{
if (!g_AudioDeviceID || sample_id < 0
|| sample_id >= m_LoadedSamplesCount) {
return AUDIO_NO_SOUND;
}
int result = AUDIO_NO_SOUND;
SDL_LockAudioDevice(g_AudioDeviceID);
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_SAMPLES; sound_id++) {
AUDIO_SAMPLE_SOUND *sound = &m_SampleSounds[sound_id];
if (sound->is_used) {
continue;
}
sound->is_used = true;
sound->is_playing = true;
sound->volume = volume;
sound->pitch = pitch;
sound->pan = pan;
sound->is_looped = is_looped;
sound->current_sample = 0.0f;
sound->sample = &m_LoadedSamples[sample_id];
S_Audio_SampleRecalculateChannelVolumes(sound_id);
result = sound_id;
break;
}
SDL_UnlockAudioDevice(g_AudioDeviceID);
if (result == AUDIO_NO_SOUND) {
LOG_ERROR("All sample buffers are used!");
}
return result;
}
bool S_Audio_SampleSoundIsPlaying(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_SAMPLES) {
return false;
}
return m_SampleSounds[sound_id].is_playing;
}
bool S_Audio_SampleSoundPause(int sound_id)
{
if (!g_AudioDeviceID) {
return false;
}
if (m_SampleSounds[sound_id].is_playing) {
SDL_LockAudioDevice(g_AudioDeviceID);
m_SampleSounds[sound_id].is_playing = false;
SDL_UnlockAudioDevice(g_AudioDeviceID);
}
return true;
}
bool S_Audio_SampleSoundPauseAll(void)
{
if (!g_AudioDeviceID) {
return false;
}
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_SAMPLES; sound_id++) {
if (m_SampleSounds[sound_id].is_used) {
S_Audio_SampleSoundPause(sound_id);
}
}
return true;
}
bool S_Audio_SampleSoundUnpause(int sound_id)
{
if (!g_AudioDeviceID) {
return false;
}
if (!m_SampleSounds[sound_id].is_playing) {
SDL_LockAudioDevice(g_AudioDeviceID);
m_SampleSounds[sound_id].is_playing = true;
SDL_UnlockAudioDevice(g_AudioDeviceID);
}
return true;
}
bool S_Audio_SampleSoundUnpauseAll(void)
{
if (!g_AudioDeviceID) {
return false;
}
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_SAMPLES; sound_id++) {
if (m_SampleSounds[sound_id].is_used) {
S_Audio_SampleSoundUnpause(sound_id);
}
}
return true;
}
bool S_Audio_SampleSoundClose(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_SAMPLES) {
return false;
}
SDL_LockAudioDevice(g_AudioDeviceID);
m_SampleSounds[sound_id].is_used = false;
m_SampleSounds[sound_id].is_playing = false;
SDL_UnlockAudioDevice(g_AudioDeviceID);
return true;
}
bool S_Audio_SampleSoundCloseAll(void)
{
if (!g_AudioDeviceID) {
return false;
}
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_SAMPLES; sound_id++) {
if (m_SampleSounds[sound_id].is_used) {
S_Audio_SampleSoundClose(sound_id);
}
}
return true;
}
bool S_Audio_SampleSoundSetPan(int sound_id, int pan)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_SAMPLES) {
return false;
}
SDL_LockAudioDevice(g_AudioDeviceID);
m_SampleSounds[sound_id].pan = pan;
S_Audio_SampleRecalculateChannelVolumes(sound_id);
SDL_UnlockAudioDevice(g_AudioDeviceID);
return true;
}
bool S_Audio_SampleSoundSetVolume(int sound_id, int volume)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_SAMPLES) {
return false;
}
SDL_LockAudioDevice(g_AudioDeviceID);
m_SampleSounds[sound_id].volume = volume;
S_Audio_SampleRecalculateChannelVolumes(sound_id);
SDL_UnlockAudioDevice(g_AudioDeviceID);
return true;
}
void S_Audio_SampleSoundMix(float *dst_buffer, size_t len)
{
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_SAMPLES; sound_id++) {
AUDIO_SAMPLE_SOUND *sound = &m_SampleSounds[sound_id];
if (!sound->is_playing) {
continue;
}
int samples_requested =
len / sizeof(AUDIO_WORKING_FORMAT) / AUDIO_WORKING_CHANNELS;
float src_sample_idx = sound->current_sample;
const float *src_buffer = sound->sample->sample_data;
float *dst_ptr = dst_buffer;
while ((dst_ptr - dst_buffer) / AUDIO_WORKING_CHANNELS
< samples_requested) {
// because we handle 3d sound ourselves, downmix to mono
float src_sample = 0.0f;
for (int i = 0; i < sound->sample->channels; i++) {
src_sample += src_buffer
[(int)src_sample_idx * sound->sample->channels + i];
}
src_sample /= (float)sound->sample->channels;
*dst_ptr++ += src_sample * sound->volume_l;
*dst_ptr++ += src_sample * sound->volume_r;
src_sample_idx += sound->pitch;
if ((int)src_sample_idx >= sound->sample->num_samples) {
if (sound->is_looped) {
src_sample_idx = 0.0f;
} else {
break;
}
}
}
sound->current_sample = src_sample_idx;
if (sound->current_sample >= sound->sample->num_samples
&& !sound->is_looped) {
S_Audio_SampleSoundClose(sound_id);
}
}
}

View file

@ -1,592 +0,0 @@
#define S_AUDIO_IMPL
#include "specific/s_audio.h"
#include <libtrx/filesystem.h>
#include <libtrx/log.h>
#include <libtrx/memory.h>
#include <SDL2/SDL_audio.h>
#include <SDL2/SDL_error.h>
#include <assert.h>
#include <errno.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/codec.h>
#include <libavcodec/packet.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
#include <libavutil/error.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavutil/rational.h>
#include <libavutil/samplefmt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define READ_BUFFER_SIZE \
(AUDIO_SAMPLES * AUDIO_WORKING_CHANNELS * sizeof(AUDIO_WORKING_FORMAT))
typedef struct AUDIO_STREAM_SOUND {
bool is_used;
bool is_playing;
bool is_read_done;
bool is_looped;
float volume;
double duration;
double timestamp;
void (*finish_callback)(int sound_id, void *user_data);
void *finish_callback_user_data;
struct {
AVStream *stream;
AVFormatContext *format_ctx;
const AVCodec *codec;
AVCodecContext *codec_ctx;
AVPacket *packet;
AVFrame *frame;
} av;
struct {
SDL_AudioStream *stream;
} sdl;
} AUDIO_STREAM_SOUND;
extern SDL_AudioDeviceID g_AudioDeviceID;
static AUDIO_STREAM_SOUND m_StreamSounds[AUDIO_MAX_ACTIVE_STREAMS] = { 0 };
static float m_DecodeBuffer[AUDIO_SAMPLES * AUDIO_WORKING_CHANNELS] = { 0 };
static bool S_Audio_StreamSoundDecodeFrame(AUDIO_STREAM_SOUND *stream);
static bool S_Audio_StreamSoundEnqueueFrame(AUDIO_STREAM_SOUND *stream);
static bool S_Audio_StreamSoundInitialiseFromPath(
int sound_id, const char *file_path);
static void S_Audio_StreamSoundClear(AUDIO_STREAM_SOUND *stream);
static bool S_Audio_StreamSoundDecodeFrame(AUDIO_STREAM_SOUND *stream)
{
assert(stream);
int error_code = av_read_frame(stream->av.format_ctx, stream->av.packet);
if (error_code == AVERROR_EOF && stream->is_looped) {
avio_seek(stream->av.format_ctx->pb, 0, SEEK_SET);
avformat_seek_file(
stream->av.format_ctx, -1, 0, 0, 0, AVSEEK_FLAG_FRAME);
av_frame_unref(stream->av.frame);
return S_Audio_StreamSoundDecodeFrame(stream);
}
if (error_code < 0) {
return false;
}
if (stream->av.packet->stream_index != stream->av.stream->index) {
return true;
}
error_code = avcodec_send_packet(stream->av.codec_ctx, stream->av.packet);
if (error_code < 0) {
av_packet_unref(stream->av.packet);
LOG_ERROR(
"Got an error when decoding frame: %s", av_err2str(error_code));
return false;
}
return true;
}
static bool S_Audio_StreamSoundEnqueueFrame(AUDIO_STREAM_SOUND *stream)
{
assert(stream);
while (1) {
int error_code =
avcodec_receive_frame(stream->av.codec_ctx, stream->av.frame);
if (error_code == AVERROR(EAGAIN)) {
av_frame_unref(stream->av.frame);
break;
}
if (error_code < 0) {
av_packet_unref(stream->av.packet);
av_frame_unref(stream->av.frame);
LOG_ERROR(
"Got an error when decoding frame: %d, %s", error_code,
av_err2str(error_code));
break;
}
error_code = av_samples_get_buffer_size(
NULL, stream->av.codec_ctx->channels, stream->av.frame->nb_samples,
stream->av.codec_ctx->sample_fmt, 1);
if (error_code == AVERROR(EAGAIN)) {
av_frame_unref(stream->av.frame);
break;
}
if (error_code < 0) {
LOG_ERROR(
"Got an error when decoding frame: %d, %s", error_code,
av_err2str(error_code));
av_frame_unref(stream->av.frame);
break;
}
int data_size = error_code;
if (SDL_AudioStreamPut(
stream->sdl.stream, stream->av.frame->data[0], data_size)) {
LOG_ERROR("Got an error when decoding frame: %s", SDL_GetError());
av_frame_unref(stream->av.frame);
break;
}
double time_base_sec = av_q2d(stream->av.stream->time_base);
stream->timestamp =
stream->av.frame->best_effort_timestamp * time_base_sec;
av_frame_unref(stream->av.frame);
}
av_packet_unref(stream->av.packet);
return true;
}
static bool S_Audio_StreamSoundInitialiseFromPath(
int sound_id, const char *file_path)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
bool ret = false;
SDL_LockAudioDevice(g_AudioDeviceID);
int error_code;
char *full_path = File_GetFullPath(file_path);
AUDIO_STREAM_SOUND *stream = &m_StreamSounds[sound_id];
error_code =
avformat_open_input(&stream->av.format_ctx, full_path, NULL, NULL);
if (error_code != 0) {
goto cleanup;
}
error_code = avformat_find_stream_info(stream->av.format_ctx, NULL);
if (error_code < 0) {
goto cleanup;
}
stream->av.stream = NULL;
for (unsigned int i = 0; i < stream->av.format_ctx->nb_streams; i++) {
AVStream *current_stream = stream->av.format_ctx->streams[i];
if (current_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
stream->av.stream = current_stream;
break;
}
}
if (!stream->av.stream) {
error_code = AVERROR_STREAM_NOT_FOUND;
goto cleanup;
}
stream->av.codec =
avcodec_find_decoder(stream->av.stream->codecpar->codec_id);
if (!stream->av.codec) {
error_code = AVERROR_DEMUXER_NOT_FOUND;
goto cleanup;
}
stream->av.codec_ctx = avcodec_alloc_context3(stream->av.codec);
if (!stream->av.codec_ctx) {
error_code = AVERROR(ENOMEM);
goto cleanup;
}
error_code = avcodec_parameters_to_context(
stream->av.codec_ctx, stream->av.stream->codecpar);
if (error_code) {
goto cleanup;
}
error_code = avcodec_open2(stream->av.codec_ctx, stream->av.codec, NULL);
if (error_code < 0) {
goto cleanup;
}
stream->av.packet = av_packet_alloc();
if (!stream->av.packet) {
error_code = AVERROR(ENOMEM);
goto cleanup;
}
stream->av.frame = av_frame_alloc();
if (!stream->av.frame) {
error_code = AVERROR(ENOMEM);
goto cleanup;
}
S_Audio_StreamSoundDecodeFrame(stream);
int32_t sdl_format =
S_Audio_GetSDLAudioFormat(stream->av.codec_ctx->sample_fmt);
if (sdl_format < 0) {
LOG_ERROR(
"Unknown sample format: %d", stream->av.codec_ctx->sample_fmt);
goto cleanup;
}
int32_t sdl_sample_rate = stream->av.codec_ctx->sample_rate;
int32_t sdl_channels = stream->av.codec_ctx->channels;
stream->is_read_done = false;
stream->is_used = true;
stream->is_playing = true;
stream->is_looped = false;
stream->volume = 1.0f;
stream->timestamp = 0.0;
stream->finish_callback = NULL;
stream->finish_callback_user_data = NULL;
stream->duration =
(double)stream->av.format_ctx->duration / (double)AV_TIME_BASE;
stream->sdl.stream = SDL_NewAudioStream(
sdl_format, sdl_channels, sdl_sample_rate, AUDIO_WORKING_FORMAT,
sdl_channels, AUDIO_WORKING_RATE);
if (!stream->sdl.stream) {
LOG_ERROR("Failed to create SDL stream: %s", SDL_GetError());
goto cleanup;
}
ret = true;
S_Audio_StreamSoundEnqueueFrame(stream);
cleanup:
if (error_code) {
LOG_ERROR(
"Error while opening audio %s: %s", file_path,
av_err2str(error_code));
}
if (!ret) {
S_Audio_StreamSoundClose(sound_id);
}
SDL_UnlockAudioDevice(g_AudioDeviceID);
Memory_FreePointer(&full_path);
return ret;
}
static void S_Audio_StreamSoundClear(AUDIO_STREAM_SOUND *stream)
{
stream->is_used = false;
stream->is_playing = false;
stream->is_read_done = true;
stream->is_looped = false;
stream->volume = 0.0f;
stream->duration = 0.0;
stream->timestamp = 0.0;
stream->sdl.stream = NULL;
stream->finish_callback = NULL;
stream->finish_callback_user_data = NULL;
}
void S_Audio_StreamSoundInit(void)
{
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_STREAMS; sound_id++) {
S_Audio_StreamSoundClear(&m_StreamSounds[sound_id]);
}
}
void S_Audio_StreamSoundShutdown(void)
{
if (!g_AudioDeviceID) {
return;
}
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_STREAMS; sound_id++) {
if (m_StreamSounds[sound_id].is_used) {
S_Audio_StreamSoundClose(sound_id);
}
}
}
bool S_Audio_StreamSoundPause(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
if (m_StreamSounds[sound_id].is_playing) {
SDL_LockAudioDevice(g_AudioDeviceID);
m_StreamSounds[sound_id].is_playing = false;
SDL_UnlockAudioDevice(g_AudioDeviceID);
}
return true;
}
bool S_Audio_StreamSoundUnpause(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
if (!m_StreamSounds[sound_id].is_playing) {
SDL_LockAudioDevice(g_AudioDeviceID);
m_StreamSounds[sound_id].is_playing = true;
SDL_UnlockAudioDevice(g_AudioDeviceID);
}
return true;
}
int S_Audio_StreamSoundCreateFromFile(const char *file_path)
{
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_STREAMS; sound_id++) {
AUDIO_STREAM_SOUND *stream = &m_StreamSounds[sound_id];
if (stream->is_used) {
continue;
}
if (!S_Audio_StreamSoundInitialiseFromPath(sound_id, file_path)) {
return AUDIO_NO_SOUND;
}
return sound_id;
}
return AUDIO_NO_SOUND;
}
bool S_Audio_StreamSoundClose(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
SDL_LockAudioDevice(g_AudioDeviceID);
AUDIO_STREAM_SOUND *stream = &m_StreamSounds[sound_id];
if (stream->av.codec_ctx) {
avcodec_close(stream->av.codec_ctx);
av_free(stream->av.codec_ctx);
stream->av.codec_ctx = NULL;
}
if (stream->av.format_ctx) {
avformat_close_input(&stream->av.format_ctx);
stream->av.format_ctx = NULL;
}
if (stream->av.packet) {
av_packet_free(&stream->av.packet);
stream->av.packet = NULL;
}
if (stream->av.frame) {
av_frame_free(&stream->av.frame);
stream->av.frame = NULL;
}
stream->av.stream = NULL;
stream->av.codec = NULL;
if (stream->sdl.stream) {
SDL_FreeAudioStream(stream->sdl.stream);
}
void (*finish_callback)(int, void *) = stream->finish_callback;
void *finish_callback_user_data = stream->finish_callback_user_data;
S_Audio_StreamSoundClear(stream);
SDL_UnlockAudioDevice(g_AudioDeviceID);
if (finish_callback) {
finish_callback(sound_id, finish_callback_user_data);
}
return true;
}
bool S_Audio_StreamSoundSetVolume(int sound_id, float volume)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
m_StreamSounds[sound_id].volume = volume;
return true;
}
bool S_Audio_StreamSoundIsLooped(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
return m_StreamSounds[sound_id].is_looped;
}
bool S_Audio_StreamSoundSetIsLooped(int sound_id, bool is_looped)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
m_StreamSounds[sound_id].is_looped = is_looped;
return true;
}
bool S_Audio_StreamSoundSetFinishCallback(
int sound_id, void (*callback)(int sound_id, void *user_data),
void *user_data)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
m_StreamSounds[sound_id].finish_callback = callback;
m_StreamSounds[sound_id].finish_callback_user_data = user_data;
return true;
}
void S_Audio_StreamSoundMix(float *dst_buffer, size_t len)
{
for (int sound_id = 0; sound_id < AUDIO_MAX_ACTIVE_STREAMS; sound_id++) {
AUDIO_STREAM_SOUND *stream = &m_StreamSounds[sound_id];
if (!stream->is_playing) {
continue;
}
while ((SDL_AudioStreamAvailable(stream->sdl.stream) < (int)len)
&& !stream->is_read_done) {
if (S_Audio_StreamSoundDecodeFrame(stream)) {
S_Audio_StreamSoundEnqueueFrame(stream);
} else {
stream->is_read_done = true;
}
}
memset(m_DecodeBuffer, 0, READ_BUFFER_SIZE);
int bytes_gotten = SDL_AudioStreamGet(
stream->sdl.stream, m_DecodeBuffer, READ_BUFFER_SIZE);
if (bytes_gotten < 0) {
LOG_ERROR("Error reading from sdl.stream: %s", SDL_GetError());
stream->is_playing = false;
stream->is_used = false;
stream->is_read_done = true;
} else if (bytes_gotten == 0) {
// legit end of stream. looping is handled in
// S_Audio_StreamSoundDecodeFrame
stream->is_playing = false;
stream->is_used = false;
stream->is_read_done = true;
} else {
int samples_gotten = bytes_gotten
/ (stream->av.codec_ctx->channels
* sizeof(AUDIO_WORKING_FORMAT));
const float *src_ptr = &m_DecodeBuffer[0];
float *dst_ptr = dst_buffer;
if (stream->av.codec_ctx->channels == 2) {
for (int s = 0; s < samples_gotten; s++) {
*dst_ptr++ += *src_ptr++ * stream->volume;
*dst_ptr++ += *src_ptr++ * stream->volume;
}
} else if (stream->av.codec_ctx->channels == 1) {
for (int s = 0; s < samples_gotten; s++) {
*dst_ptr++ += *src_ptr * stream->volume;
*dst_ptr++ += *src_ptr++ * stream->volume;
}
} else {
for (int s = 0; s < samples_gotten; s++) {
// downmix to mono
float src_sample = 0.0f;
for (int i = 0; i < stream->av.codec_ctx->channels; i++) {
src_sample += *src_ptr++;
}
src_sample /= (float)stream->av.codec_ctx->channels;
*dst_ptr++ += src_sample * stream->volume;
*dst_ptr++ += src_sample * stream->volume;
}
}
}
if (!stream->is_used) {
S_Audio_StreamSoundClose(sound_id);
}
}
}
double S_Audio_StreamGetTimestamp(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return -1.0;
}
double timestamp = -1.0;
AUDIO_STREAM_SOUND *stream = &m_StreamSounds[sound_id];
if (stream->duration > 0.0) {
SDL_LockAudioDevice(g_AudioDeviceID);
timestamp = stream->timestamp;
SDL_UnlockAudioDevice(g_AudioDeviceID);
}
return timestamp;
}
double S_Audio_StreamGetDuration(int sound_id)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return -1.0;
}
SDL_LockAudioDevice(g_AudioDeviceID);
AUDIO_STREAM_SOUND *stream = &m_StreamSounds[sound_id];
double duration = stream->duration;
SDL_UnlockAudioDevice(g_AudioDeviceID);
return duration;
}
bool S_Audio_StreamSeekTimestamp(int sound_id, double timestamp)
{
if (!g_AudioDeviceID || sound_id < 0
|| sound_id >= AUDIO_MAX_ACTIVE_STREAMS) {
return false;
}
if (m_StreamSounds[sound_id].is_playing) {
SDL_LockAudioDevice(g_AudioDeviceID);
AUDIO_STREAM_SOUND *stream = &m_StreamSounds[sound_id];
const double time_base_sec = av_q2d(stream->av.stream->time_base);
av_seek_frame(
stream->av.format_ctx, sound_id, timestamp / time_base_sec,
AVSEEK_FLAG_ANY);
SDL_UnlockAudioDevice(g_AudioDeviceID);
return true;
}
return false;
}

View file

@ -31,10 +31,10 @@
#include "gfx/2d/2d_surface.h"
#include "gfx/context.h"
#include "global/types.h"
#include "specific/s_audio.h"
#include "specific/s_output.h"
#include "specific/s_shell.h"
#include <libtrx/engine/audio.h>
#include <libtrx/filesystem.h>
#include <libtrx/log.h>
#include <libtrx/memory.h>
@ -2294,7 +2294,7 @@ bool S_FMV_Play(const char *file_path)
return false;
}
S_Audio_Shutdown();
Audio_Shutdown();
int flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
if (SDL_Init(flags)) {
@ -2324,7 +2324,7 @@ cleanup:
LOG_DEBUG("Finished playing FMV: %s", file_path);
S_Audio_Init();
Audio_Init();
S_Output_ApplyRenderSettings();

@ -1 +1 @@
Subproject commit 7a11c77fbdf10c2f959258d57cc8d11a6cd915dc
Subproject commit 285fe579e710f763470d352ee551c2615183597d

View file

@ -126,7 +126,7 @@ RUN apt-get install -y \
pkg-config \
git \
python3-pip \
&& python3 -m pip install \
&& python3 -m pip install --break-system-packages \
pyjson5 \
meson \
ninja

View file

@ -134,7 +134,7 @@ RUN apt-get install -y \
pkg-config \
upx \
python3-pip \
&& python3 -m pip install \
&& python3 -m pip install --break-system-packages \
pyjson5 \
meson \
ninja

View file

@ -1,27 +0,0 @@
--enable-gpl
--enable-decoder=pcx
--enable-decoder=png
--enable-decoder=gif
--enable-decoder=mjpeg
--enable-decoder=mpeg4
--enable-decoder=mdec
--enable-decoder=h264
--enable-decoder=h264_qsv
--enable-decoder=libopenh264
--enable-demuxer=mov
--enable-demuxer=avi
--enable-demuxer=h264
--enable-demuxer=str
--enable-demuxer=image2
--enable-zlib
--enable-small
--disable-debug
--disable-ffplay
--disable-ffprobe
--disable-doc
--disable-network
--disable-htmlpages
--disable-manpages
--disable-podpages
--disable-txtpages
--disable-asm

1
tools/ffmpeg_flags.txt Symbolic link
View file

@ -0,0 +1 @@
../subprojects/libtrx/tools/ffmpeg_flags.txt

View file

@ -23,9 +23,6 @@ run_script(
"game/lara/lara.c": "game/lara.h",
"game/option/option.c": "game/option.h",
"game/savegame/savegame.c": "game/savegame.h",
"specific/s_audio_sample.c": "specific/s_audio.h",
"specific/s_audio_stream.c": "specific/s_audio.h",
"specific/s_shell.c": "specific/s_shell.h",
},
fix_map={"libavcodec/version_major.h": "libavcodec/version.h"},
forced_order=[],