Update to miniaudio 0.10 (#1092)
* Update to miniaudio 0.10 This replaces the old ma_pcm_converter with ma_data_converter. At this time of this commit, miniaudio 0.10 is still in the testing phase. To make it easier to update miniaudio.h during this period, I've temporarily moved the @raysan5 Win32 customizations to raudio.c because there may be quite a few updates to miniaudio.h during this time. * Use miniaudio's built-in volume control.
This commit is contained in:
parent
d2bb6185f1
commit
7024628c65
2 changed files with 8374 additions and 7167 deletions
15184
src/external/miniaudio.h
vendored
15184
src/external/miniaudio.h
vendored
File diff suppressed because it is too large
Load diff
357
src/raudio.c
357
src/raudio.c
|
@ -78,6 +78,85 @@
|
||||||
#include "utils.h" // Required for: fopen() Android mapping
|
#include "utils.h" // Required for: fopen() Android mapping
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
// @raysan5: To avoid conflicting windows.h symbols with raylib, so flags are defined
|
||||||
|
// WARNING: Those flags avoid inclusion of some Win32 headers that could be required
|
||||||
|
// by user at some point and won't be included...
|
||||||
|
//-------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// If defined, the following flags inhibit definition of the indicated items.
|
||||||
|
#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
|
||||||
|
#define NOVIRTUALKEYCODES // VK_*
|
||||||
|
#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_*
|
||||||
|
#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
|
||||||
|
#define NOSYSMETRICS // SM_*
|
||||||
|
#define NOMENUS // MF_*
|
||||||
|
#define NOICONS // IDI_*
|
||||||
|
#define NOKEYSTATES // MK_*
|
||||||
|
#define NOSYSCOMMANDS // SC_*
|
||||||
|
#define NORASTEROPS // Binary and Tertiary raster ops
|
||||||
|
#define NOSHOWWINDOW // SW_*
|
||||||
|
#define OEMRESOURCE // OEM Resource values
|
||||||
|
#define NOATOM // Atom Manager routines
|
||||||
|
#define NOCLIPBOARD // Clipboard routines
|
||||||
|
#define NOCOLOR // Screen colors
|
||||||
|
#define NOCTLMGR // Control and Dialog routines
|
||||||
|
#define NODRAWTEXT // DrawText() and DT_*
|
||||||
|
#define NOGDI // All GDI defines and routines
|
||||||
|
#define NOKERNEL // All KERNEL defines and routines
|
||||||
|
#define NOUSER // All USER defines and routines
|
||||||
|
//#define NONLS // All NLS defines and routines
|
||||||
|
#define NOMB // MB_* and MessageBox()
|
||||||
|
#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
|
||||||
|
#define NOMETAFILE // typedef METAFILEPICT
|
||||||
|
#define NOMINMAX // Macros min(a,b) and max(a,b)
|
||||||
|
#define NOMSG // typedef MSG and associated routines
|
||||||
|
#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
|
||||||
|
#define NOSCROLL // SB_* and scrolling routines
|
||||||
|
#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
|
||||||
|
#define NOSOUND // Sound driver routines
|
||||||
|
#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines
|
||||||
|
#define NOWH // SetWindowsHook and WH_*
|
||||||
|
#define NOWINOFFSETS // GWL_*, GCL_*, associated routines
|
||||||
|
#define NOCOMM // COMM driver routines
|
||||||
|
#define NOKANJI // Kanji support stuff.
|
||||||
|
#define NOHELP // Help engine interface.
|
||||||
|
#define NOPROFILER // Profiler interface.
|
||||||
|
#define NODEFERWINDOWPOS // DeferWindowPos routines
|
||||||
|
#define NOMCX // Modem Configuration Extensions
|
||||||
|
|
||||||
|
// Type required before windows.h inclusion
|
||||||
|
typedef struct tagMSG *LPMSG;
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// Type required by some unused function...
|
||||||
|
typedef struct tagBITMAPINFOHEADER {
|
||||||
|
DWORD biSize;
|
||||||
|
LONG biWidth;
|
||||||
|
LONG biHeight;
|
||||||
|
WORD biPlanes;
|
||||||
|
WORD biBitCount;
|
||||||
|
DWORD biCompression;
|
||||||
|
DWORD biSizeImage;
|
||||||
|
LONG biXPelsPerMeter;
|
||||||
|
LONG biYPelsPerMeter;
|
||||||
|
DWORD biClrUsed;
|
||||||
|
DWORD biClrImportant;
|
||||||
|
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
|
||||||
|
|
||||||
|
#include <objbase.h>
|
||||||
|
#include <mmreg.h>
|
||||||
|
#include <mmsystem.h>
|
||||||
|
|
||||||
|
// @raysan5: Some required types defined for MSVC/TinyC compiler
|
||||||
|
#if defined(_MSC_VER) || defined(__TINYC__)
|
||||||
|
#include "propidl.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MA_NO_JACK
|
#define MA_NO_JACK
|
||||||
#define MINIAUDIO_IMPLEMENTATION
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
#include "external/miniaudio.h" // miniaudio library
|
#include "external/miniaudio.h" // miniaudio library
|
||||||
|
@ -172,7 +251,7 @@ typedef enum {
|
||||||
|
|
||||||
// Audio buffer structure
|
// Audio buffer structure
|
||||||
struct rAudioBuffer {
|
struct rAudioBuffer {
|
||||||
ma_pcm_converter dsp; // PCM data converter
|
ma_data_converter converter; // Audio data converter
|
||||||
|
|
||||||
float volume; // Audio buffer volume
|
float volume; // Audio buffer volume
|
||||||
float pitch; // Audio buffer pitch
|
float pitch; // Audio buffer pitch
|
||||||
|
@ -185,7 +264,7 @@ struct rAudioBuffer {
|
||||||
bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer)
|
bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer)
|
||||||
unsigned int sizeInFrames; // Total buffer size in frames
|
unsigned int sizeInFrames; // Total buffer size in frames
|
||||||
unsigned int frameCursorPos; // Frame cursor position
|
unsigned int frameCursorPos; // Frame cursor position
|
||||||
unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timming)
|
unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timing)
|
||||||
|
|
||||||
unsigned char *data; // Data buffer, on music stream keeps filling
|
unsigned char *data; // Data buffer, on music stream keeps filling
|
||||||
|
|
||||||
|
@ -202,7 +281,6 @@ typedef struct AudioData {
|
||||||
ma_device device; // miniaudio device
|
ma_device device; // miniaudio device
|
||||||
ma_mutex lock; // miniaudio mutex lock
|
ma_mutex lock; // miniaudio mutex lock
|
||||||
bool isReady; // Check if audio device is ready
|
bool isReady; // Check if audio device is ready
|
||||||
float masterVolume; // Master volume (multiplied on output mixing)
|
|
||||||
} System;
|
} System;
|
||||||
struct {
|
struct {
|
||||||
AudioBuffer *first; // Pointer to first AudioBuffer in the list
|
AudioBuffer *first; // Pointer to first AudioBuffer in the list
|
||||||
|
@ -225,7 +303,6 @@ static AudioData AUDIO = { 0 }; // Global CORE context
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message);
|
static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message);
|
||||||
static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount);
|
static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount);
|
||||||
static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint32 frameCount, void *pUserData);
|
|
||||||
static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume);
|
static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume);
|
||||||
|
|
||||||
static void InitAudioBufferPool(void); // Initialise the multichannel buffer pool
|
static void InitAudioBufferPool(void); // Initialise the multichannel buffer pool
|
||||||
|
@ -273,7 +350,6 @@ void UntrackAudioBuffer(AudioBuffer *buffer);
|
||||||
void InitAudioDevice(void)
|
void InitAudioDevice(void)
|
||||||
{
|
{
|
||||||
// TODO: Load AUDIO context memory dynamically?
|
// TODO: Load AUDIO context memory dynamically?
|
||||||
AUDIO.System.masterVolume = 1.0f;
|
|
||||||
|
|
||||||
// Init audio context
|
// Init audio context
|
||||||
ma_context_config ctxConfig = ma_context_config_init();
|
ma_context_config ctxConfig = ma_context_config_init();
|
||||||
|
@ -366,10 +442,7 @@ bool IsAudioDeviceReady(void)
|
||||||
// Set master volume (listener)
|
// Set master volume (listener)
|
||||||
void SetMasterVolume(float volume)
|
void SetMasterVolume(float volume)
|
||||||
{
|
{
|
||||||
if (volume < 0.0f) volume = 0.0f;
|
ma_device_set_master_volume(&AUDIO.System.device, volume);
|
||||||
else if (volume > 1.0f) volume = 1.0f;
|
|
||||||
|
|
||||||
AUDIO.System.masterVolume = volume;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
@ -390,19 +463,10 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
|
||||||
audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
|
audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
|
||||||
|
|
||||||
// Audio data runs through a format converter
|
// Audio data runs through a format converter
|
||||||
ma_pcm_converter_config dspConfig;
|
ma_data_converter_config converterConfig = ma_data_converter_config_init(format, DEVICE_FORMAT, channels, DEVICE_CHANNELS, sampleRate, DEVICE_SAMPLE_RATE);
|
||||||
memset(&dspConfig, 0, sizeof(dspConfig));
|
converterConfig.resampling.allowDynamicSampleRate = true; // Required for pitch shifting
|
||||||
dspConfig.formatIn = format;
|
|
||||||
dspConfig.formatOut = DEVICE_FORMAT;
|
|
||||||
dspConfig.channelsIn = channels;
|
|
||||||
dspConfig.channelsOut = DEVICE_CHANNELS;
|
|
||||||
dspConfig.sampleRateIn = sampleRate;
|
|
||||||
dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE;
|
|
||||||
dspConfig.onRead = OnAudioBufferDSPRead; // Callback on data reading
|
|
||||||
dspConfig.pUserData = audioBuffer; // Audio data pointer
|
|
||||||
dspConfig.allowDynamicSampleRate = true; // Required for pitch shifting
|
|
||||||
|
|
||||||
ma_result result = ma_pcm_converter_init(&dspConfig, &audioBuffer->dsp);
|
ma_result result = ma_data_converter_init(&converterConfig, &audioBuffer->converter);
|
||||||
|
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
|
@ -437,6 +501,7 @@ void CloseAudioBuffer(AudioBuffer *buffer)
|
||||||
{
|
{
|
||||||
if (buffer != NULL)
|
if (buffer != NULL)
|
||||||
{
|
{
|
||||||
|
ma_data_converter_uninit(&buffer->converter);
|
||||||
UntrackAudioBuffer(buffer);
|
UntrackAudioBuffer(buffer);
|
||||||
RL_FREE(buffer->data);
|
RL_FREE(buffer->data);
|
||||||
RL_FREE(buffer);
|
RL_FREE(buffer);
|
||||||
|
@ -519,10 +584,10 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
|
||||||
// Note that this changes the duration of the sound:
|
// Note that this changes the duration of the sound:
|
||||||
// - higher pitches will make the sound faster
|
// - higher pitches will make the sound faster
|
||||||
// - lower pitches make it slower
|
// - lower pitches make it slower
|
||||||
ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->dsp.src.config.sampleRateOut/pitchMul);
|
ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->converter.config.sampleRateOut/pitchMul);
|
||||||
buffer->pitch *= (float)buffer->dsp.src.config.sampleRateOut/newOutputSampleRate;
|
buffer->pitch *= (float)buffer->converter.config.sampleRateOut/newOutputSampleRate;
|
||||||
|
|
||||||
ma_pcm_converter_set_output_sample_rate(&buffer->dsp, newOutputSampleRate);
|
ma_data_converter_set_rate(&buffer->converter, buffer->converter.config.sampleRateIn, newOutputSampleRate);
|
||||||
}
|
}
|
||||||
else TRACELOG(LOG_WARNING, "SetAudioBufferPitch() : No audio buffer");
|
else TRACELOG(LOG_WARNING, "SetAudioBufferPitch() : No audio buffer");
|
||||||
}
|
}
|
||||||
|
@ -621,13 +686,13 @@ Sound LoadSoundFromWave(Wave wave)
|
||||||
ma_format formatIn = ((wave.sampleSize == 8)? ma_format_u8 : ((wave.sampleSize == 16)? ma_format_s16 : ma_format_f32));
|
ma_format formatIn = ((wave.sampleSize == 8)? ma_format_u8 : ((wave.sampleSize == 16)? ma_format_s16 : ma_format_f32));
|
||||||
ma_uint32 frameCountIn = wave.sampleCount/wave.channels;
|
ma_uint32 frameCountIn = wave.sampleCount/wave.channels;
|
||||||
|
|
||||||
ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn);
|
ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, frameCountIn, formatIn, wave.channels, wave.sampleRate);
|
||||||
if (frameCount == 0) TRACELOG(LOG_WARNING, "LoadSoundFromWave() : Failed to get frame count for format conversion");
|
if (frameCount == 0) TRACELOG(LOG_WARNING, "LoadSoundFromWave() : Failed to get frame count for format conversion");
|
||||||
|
|
||||||
AudioBuffer *audioBuffer = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC);
|
AudioBuffer *audioBuffer = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC);
|
||||||
if (audioBuffer == NULL) TRACELOG(LOG_WARNING, "LoadSoundFromWave() : Failed to create audio buffer");
|
if (audioBuffer == NULL) TRACELOG(LOG_WARNING, "LoadSoundFromWave() : Failed to create audio buffer");
|
||||||
|
|
||||||
frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, audioBuffer->dsp.formatConverterIn.config.formatIn, audioBuffer->dsp.formatConverterIn.config.channels, audioBuffer->dsp.src.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn);
|
frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate);
|
||||||
if (frameCount == 0) TRACELOG(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed");
|
if (frameCount == 0) TRACELOG(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed");
|
||||||
|
|
||||||
sound.sampleCount = frameCount*DEVICE_CHANNELS;
|
sound.sampleCount = frameCount*DEVICE_CHANNELS;
|
||||||
|
@ -666,7 +731,7 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
|
||||||
StopAudioBuffer(audioBuffer);
|
StopAudioBuffer(audioBuffer);
|
||||||
|
|
||||||
// TODO: May want to lock/unlock this since this data buffer is read at mixing time
|
// TODO: May want to lock/unlock this since this data buffer is read at mixing time
|
||||||
memcpy(audioBuffer->data, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn));
|
memcpy(audioBuffer->data, data, samplesCount*ma_get_bytes_per_frame(audioBuffer->converter.config.formatIn, audioBuffer->converter.config.channelsIn));
|
||||||
}
|
}
|
||||||
else TRACELOG(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer");
|
else TRACELOG(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer");
|
||||||
}
|
}
|
||||||
|
@ -869,7 +934,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
||||||
|
|
||||||
ma_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
|
ma_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
|
||||||
|
|
||||||
ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn);
|
ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, formatOut, channels, sampleRate, NULL, frameCountIn, formatIn, wave->channels, wave->sampleRate);
|
||||||
if (frameCount == 0)
|
if (frameCount == 0)
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion.");
|
TRACELOG(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion.");
|
||||||
|
@ -878,7 +943,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
||||||
|
|
||||||
void *data = RL_MALLOC(frameCount*channels*(sampleSize/8));
|
void *data = RL_MALLOC(frameCount*channels*(sampleSize/8));
|
||||||
|
|
||||||
frameCount = (ma_uint32)ma_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn);
|
frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate);
|
||||||
if (frameCount == 0)
|
if (frameCount == 0)
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_ERROR, "WaveFormat() : Format conversion failed.");
|
TRACELOG(LOG_ERROR, "WaveFormat() : Format conversion failed.");
|
||||||
|
@ -1012,7 +1077,7 @@ Music LoadMusicStream(const char *fileName)
|
||||||
music.ctxType = MUSIC_AUDIO_MP3;
|
music.ctxType = MUSIC_AUDIO_MP3;
|
||||||
|
|
||||||
music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels);
|
music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels);
|
||||||
music.sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels;
|
music.sampleCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels;
|
||||||
music.loopCount = 0; // Infinite loop by default
|
music.loopCount = 0; // Infinite loop by default
|
||||||
musicLoaded = true;
|
musicLoaded = true;
|
||||||
}
|
}
|
||||||
|
@ -1487,6 +1552,142 @@ static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel,
|
||||||
TRACELOG(LOG_ERROR, message); // All log messages from miniaudio are errors
|
TRACELOG(LOG_ERROR, message); // All log messages from miniaudio are errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reads audio data from an AudioBuffer object in internal format.
|
||||||
|
static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount)
|
||||||
|
{
|
||||||
|
ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames;
|
||||||
|
ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
|
||||||
|
|
||||||
|
if (currentSubBufferIndex > 1)
|
||||||
|
{
|
||||||
|
TRACELOGD("Frame cursor position moved too far forward in audio stream");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another thread can update the processed state of buffers so
|
||||||
|
// we just take a copy here to try and avoid potential synchronization problems
|
||||||
|
bool isSubBufferProcessed[2];
|
||||||
|
isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
|
||||||
|
isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1];
|
||||||
|
|
||||||
|
ma_uint32 frameSizeInBytes = ma_get_bytes_per_frame(audioBuffer->converter.config.formatIn, audioBuffer->converter.config.channelsIn);
|
||||||
|
|
||||||
|
// Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0
|
||||||
|
ma_uint32 framesRead = 0;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// We break from this loop differently depending on the buffer's usage
|
||||||
|
// - For static buffers, we simply fill as much data as we can
|
||||||
|
// - For streaming buffers we only fill the halves of the buffer that are processed
|
||||||
|
// Unprocessed halves must keep their audio data in-tact
|
||||||
|
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
||||||
|
{
|
||||||
|
if (framesRead >= frameCount) break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isSubBufferProcessed[currentSubBufferIndex]) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_uint32 totalFramesRemaining = (frameCount - framesRead);
|
||||||
|
if (totalFramesRemaining == 0) break;
|
||||||
|
|
||||||
|
ma_uint32 framesRemainingInOutputBuffer;
|
||||||
|
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
||||||
|
{
|
||||||
|
framesRemainingInOutputBuffer = audioBuffer->sizeInFrames - audioBuffer->frameCursorPos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex;
|
||||||
|
framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_uint32 framesToRead = totalFramesRemaining;
|
||||||
|
if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer;
|
||||||
|
|
||||||
|
memcpy((unsigned char *)framesOut + (framesRead*frameSizeInBytes), audioBuffer->data + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes);
|
||||||
|
audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->sizeInFrames;
|
||||||
|
framesRead += framesToRead;
|
||||||
|
|
||||||
|
// If we've read to the end of the buffer, mark it as processed
|
||||||
|
if (framesToRead == framesRemainingInOutputBuffer)
|
||||||
|
{
|
||||||
|
audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true;
|
||||||
|
isSubBufferProcessed[currentSubBufferIndex] = true;
|
||||||
|
|
||||||
|
currentSubBufferIndex = (currentSubBufferIndex + 1)%2;
|
||||||
|
|
||||||
|
// We need to break from this loop if we're not looping
|
||||||
|
if (!audioBuffer->looping)
|
||||||
|
{
|
||||||
|
StopAudioBuffer(audioBuffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero-fill excess
|
||||||
|
ma_uint32 totalFramesRemaining = (frameCount - framesRead);
|
||||||
|
if (totalFramesRemaining > 0)
|
||||||
|
{
|
||||||
|
memset((unsigned char *)framesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
|
||||||
|
|
||||||
|
// For static buffers we can fill the remaining frames with silence for safety, but we don't want
|
||||||
|
// to report those frames as "read". The reason for this is that the caller uses the return value
|
||||||
|
// to know whether or not a non-looping sound has finished playback.
|
||||||
|
if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
return framesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads audio data from an AudioBuffer object in device format. Returned data will be in a format appropriate for mixing.
|
||||||
|
static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount)
|
||||||
|
{
|
||||||
|
// What's going on here is that we're continuously converting data from the AudioBuffer's internal format to the mixing format, which
|
||||||
|
// should be defined by the output format of the data converter. We do this until frameCount frames have been output. The important
|
||||||
|
// detail to remember here is that we never, ever attempt to read more input data than is required for the specified number of output
|
||||||
|
// frames. This can be achieved with ma_data_converter_get_required_input_frame_count().
|
||||||
|
ma_uint8 inputBuffer[4096];
|
||||||
|
ma_uint32 inputBufferFrameCap = sizeof(inputBuffer) / ma_get_bytes_per_frame(audioBuffer->converter.config.formatIn, audioBuffer->converter.config.channelsIn);
|
||||||
|
|
||||||
|
ma_uint32 totalOutputFramesProcessed = 0;
|
||||||
|
while (totalOutputFramesProcessed < frameCount)
|
||||||
|
{
|
||||||
|
ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed;
|
||||||
|
|
||||||
|
ma_uint64 inputFramesToProcessThisIteration = ma_data_converter_get_required_input_frame_count(&audioBuffer->converter, outputFramesToProcessThisIteration);
|
||||||
|
if (inputFramesToProcessThisIteration > inputBufferFrameCap)
|
||||||
|
{
|
||||||
|
inputFramesToProcessThisIteration = inputBufferFrameCap;
|
||||||
|
}
|
||||||
|
|
||||||
|
float *runningFramesOut = framesOut + (totalOutputFramesProcessed * audioBuffer->converter.config.channelsOut);
|
||||||
|
|
||||||
|
/* At this point we can convert the data to our mixing format. */
|
||||||
|
ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); /* Safe cast. */
|
||||||
|
ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration;
|
||||||
|
ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration);
|
||||||
|
|
||||||
|
totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; /* Safe cast. */
|
||||||
|
|
||||||
|
if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration)
|
||||||
|
{
|
||||||
|
break; /* Ran out of input data. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This should never be hit, but will add it here for safety. Ensures we get out of the loop when no input nor output frames are processed. */
|
||||||
|
if (inputFramesProcessedThisIteration == 0 && outputFramesProcessedThisIteration == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalOutputFramesProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sending audio data to device callback function
|
// Sending audio data to device callback function
|
||||||
// NOTE: All the mixing takes place here
|
// NOTE: All the mixing takes place here
|
||||||
static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount)
|
static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount)
|
||||||
|
@ -1530,7 +1731,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
|
||||||
framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS;
|
framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_uint32 framesJustRead = (ma_uint32)ma_pcm_converter_read(&audioBuffer->dsp, tempBuffer, framesToReadRightNow);
|
ma_uint32 framesJustRead = ReadAudioBufferFramesInMixingFormat(audioBuffer, tempBuffer, framesToReadRightNow);
|
||||||
if (framesJustRead > 0)
|
if (framesJustRead > 0)
|
||||||
{
|
{
|
||||||
float *framesOut = (float *)pFramesOut + (framesRead*AUDIO.System.device.playback.channels);
|
float *framesOut = (float *)pFramesOut + (framesRead*AUDIO.System.device.playback.channels);
|
||||||
|
@ -1576,98 +1777,6 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
|
||||||
ma_mutex_unlock(&AUDIO.System.lock);
|
ma_mutex_unlock(&AUDIO.System.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DSP read from audio buffer callback function
|
|
||||||
static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint32 frameCount, void *pUserData)
|
|
||||||
{
|
|
||||||
AudioBuffer *audioBuffer = (AudioBuffer *)pUserData;
|
|
||||||
|
|
||||||
ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames;
|
|
||||||
ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
|
|
||||||
|
|
||||||
if (currentSubBufferIndex > 1)
|
|
||||||
{
|
|
||||||
TRACELOGD("Frame cursor position moved too far forward in audio stream");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Another thread can update the processed state of buffers so
|
|
||||||
// we just take a copy here to try and avoid potential synchronization problems
|
|
||||||
bool isSubBufferProcessed[2];
|
|
||||||
isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
|
|
||||||
isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1];
|
|
||||||
|
|
||||||
ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)*audioBuffer->dsp.formatConverterIn.config.channels;
|
|
||||||
|
|
||||||
// Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0
|
|
||||||
ma_uint32 framesRead = 0;
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
// We break from this loop differently depending on the buffer's usage
|
|
||||||
// - For static buffers, we simply fill as much data as we can
|
|
||||||
// - For streaming buffers we only fill the halves of the buffer that are processed
|
|
||||||
// Unprocessed halves must keep their audio data in-tact
|
|
||||||
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
|
||||||
{
|
|
||||||
if (framesRead >= frameCount) break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isSubBufferProcessed[currentSubBufferIndex]) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ma_uint32 totalFramesRemaining = (frameCount - framesRead);
|
|
||||||
if (totalFramesRemaining == 0) break;
|
|
||||||
|
|
||||||
ma_uint32 framesRemainingInOutputBuffer;
|
|
||||||
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
|
||||||
{
|
|
||||||
framesRemainingInOutputBuffer = audioBuffer->sizeInFrames - audioBuffer->frameCursorPos;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex;
|
|
||||||
framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
ma_uint32 framesToRead = totalFramesRemaining;
|
|
||||||
if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer;
|
|
||||||
|
|
||||||
memcpy((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->data + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes);
|
|
||||||
audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->sizeInFrames;
|
|
||||||
framesRead += framesToRead;
|
|
||||||
|
|
||||||
// If we've read to the end of the buffer, mark it as processed
|
|
||||||
if (framesToRead == framesRemainingInOutputBuffer)
|
|
||||||
{
|
|
||||||
audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true;
|
|
||||||
isSubBufferProcessed[currentSubBufferIndex] = true;
|
|
||||||
|
|
||||||
currentSubBufferIndex = (currentSubBufferIndex + 1)%2;
|
|
||||||
|
|
||||||
// We need to break from this loop if we're not looping
|
|
||||||
if (!audioBuffer->looping)
|
|
||||||
{
|
|
||||||
StopAudioBuffer(audioBuffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero-fill excess
|
|
||||||
ma_uint32 totalFramesRemaining = (frameCount - framesRead);
|
|
||||||
if (totalFramesRemaining > 0)
|
|
||||||
{
|
|
||||||
memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
|
|
||||||
|
|
||||||
// For static buffers we can fill the remaining frames with silence for safety, but we don't want
|
|
||||||
// to report those frames as "read". The reason for this is that the caller uses the return value
|
|
||||||
// to know whether or not a non-looping sound has finished playback.
|
|
||||||
if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
return framesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation.
|
// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation.
|
||||||
// NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function.
|
// NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function.
|
||||||
static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume)
|
static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume)
|
||||||
|
@ -1679,7 +1788,7 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr
|
||||||
float *frameOut = framesOut + (iFrame*AUDIO.System.device.playback.channels);
|
float *frameOut = framesOut + (iFrame*AUDIO.System.device.playback.channels);
|
||||||
const float *frameIn = framesIn + (iFrame*AUDIO.System.device.playback.channels);
|
const float *frameIn = framesIn + (iFrame*AUDIO.System.device.playback.channels);
|
||||||
|
|
||||||
frameOut[iChannel] += (frameIn[iChannel]*AUDIO.System.masterVolume*localVolume);
|
frameOut[iChannel] += (frameIn[iChannel]*localVolume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue