mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 20:58:07 +03:00
s/audio: move to libtrx
This commit is contained in:
parent
aa00b9e86d
commit
b0390747e1
13 changed files with 48 additions and 1534 deletions
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
1
tools/ffmpeg_flags.txt
Symbolic link
|
@ -0,0 +1 @@
|
|||
../subprojects/libtrx/tools/ffmpeg_flags.txt
|
|
@ -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=[],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue