ADDED: Audio recording
This commit is contained in:
parent
67359073ca
commit
283843f0e7
4 changed files with 364 additions and 1 deletions
199
src/raudio.c
199
src/raudio.c
|
|
@ -390,6 +390,13 @@ typedef struct AudioData {
|
|||
size_t pcmBufferSize; // Pre-allocated buffer size
|
||||
void *pcmBuffer; // Pre-allocated buffer to read audio data from file/memory
|
||||
} System;
|
||||
struct {
|
||||
ma_context context; // miniaudio context data
|
||||
ma_device device; // miniaudio device
|
||||
ma_mutex lock; // miniaudio mutex lock
|
||||
bool isReady; // Check if audio device is ready
|
||||
AudioRecordingCallback callback; // Recording callback function
|
||||
} RecordingSystem;
|
||||
struct {
|
||||
AudioBuffer *first; // Pointer to first AudioBuffer in the list
|
||||
AudioBuffer *last; // Pointer to last AudioBuffer in the list
|
||||
|
|
@ -421,6 +428,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
|
|||
static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount);
|
||||
|
||||
static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount);
|
||||
static void OnReceiveAudioDataFromDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount);
|
||||
static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer);
|
||||
|
||||
static bool IsAudioBufferPlayingInLockedState(AudioBuffer *buffer);
|
||||
|
|
@ -2369,6 +2377,181 @@ void DetachAudioMixedProcessor(AudioCallback process)
|
|||
ma_mutex_unlock(&AUDIO.System.lock);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Definition - Audio Recording Device initialization and closing
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Initialize audio recording device with custom sampleRate, sampleSize and channels
|
||||
void InitAudioRecordingDeviceEx(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels)
|
||||
{
|
||||
// Init audio context
|
||||
ma_context_config ctxConfig = ma_context_config_init();
|
||||
ma_log_callback_init(OnLog, NULL);
|
||||
|
||||
ma_result result = ma_context_init(NULL, 0, &ctxConfig, &AUDIO.RecordingSystem.context);
|
||||
if (result != MA_SUCCESS)
|
||||
{
|
||||
TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize recording context");
|
||||
return;
|
||||
}
|
||||
|
||||
ma_format format = ((sampleSize == 8)? ma_format_u8 : ((sampleSize == 16)? ma_format_s16 : ma_format_f32));
|
||||
|
||||
// Init audio device
|
||||
// NOTE: Using the default device
|
||||
ma_device_config config = ma_device_config_init(ma_device_type_capture);
|
||||
config.capture.pDeviceID = NULL; // NULL for the default capture AUDIO.RecordingSystem.device
|
||||
config.capture.format = format;
|
||||
config.capture.channels = channels;
|
||||
config.sampleRate = sampleRate;
|
||||
config.periodSizeInFrames = AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES;
|
||||
config.dataCallback = OnReceiveAudioDataFromDevice;
|
||||
config.pUserData = NULL;
|
||||
config.noFixedSizedCallback = true; // raylib does not require fixed sized callback guarantees. This bypasses an internal intermediary buffer
|
||||
|
||||
result = ma_device_init(&AUDIO.RecordingSystem.context, &config, &AUDIO.RecordingSystem.device);
|
||||
if (result != MA_SUCCESS)
|
||||
{
|
||||
TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize recording device");
|
||||
ma_context_uninit(&AUDIO.RecordingSystem.context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Recording happens on a separate thread which means synchronization is needed
|
||||
// A mutex is used here to make things simple, but may want to look at something
|
||||
// a bit smarter later on to keep everything real-time, if that's necessary
|
||||
if (ma_mutex_init(&AUDIO.RecordingSystem.lock) != MA_SUCCESS)
|
||||
{
|
||||
TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for recording");
|
||||
ma_device_uninit(&AUDIO.RecordingSystem.device);
|
||||
ma_context_uninit(&AUDIO.RecordingSystem.context);
|
||||
return;
|
||||
}
|
||||
|
||||
TRACELOG(LOG_INFO, "AUDIO: Recording device initialized successfully");
|
||||
TRACELOG(LOG_INFO, " > Backend: miniaudio | %s", ma_get_backend_name(AUDIO.RecordingSystem.context.backend));
|
||||
TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.RecordingSystem.device.capture.format), ma_get_format_name(AUDIO.RecordingSystem.device.capture.internalFormat));
|
||||
TRACELOG(LOG_INFO, " > Channels: %d -> %d", AUDIO.RecordingSystem.device.capture.channels, AUDIO.RecordingSystem.device.capture.internalChannels);
|
||||
TRACELOG(LOG_INFO, " > Sample rate: %d -> %d", AUDIO.RecordingSystem.device.sampleRate, AUDIO.RecordingSystem.device.capture.internalSampleRate);
|
||||
TRACELOG(LOG_INFO, " > Periods size: %d", AUDIO.RecordingSystem.device.capture.internalPeriodSizeInFrames*AUDIO.RecordingSystem.device.capture.internalPeriods);
|
||||
|
||||
AUDIO.RecordingSystem.isReady = true;
|
||||
}
|
||||
|
||||
// Initialize audio recording device
|
||||
void InitAudioRecordingDevice(void)
|
||||
{
|
||||
unsigned int sampleSize = ((AUDIO_DEVICE_FORMAT == ma_format_u8)? 8 : ((AUDIO_DEVICE_FORMAT == ma_format_s16)? 16 : 32));
|
||||
InitAudioRecordingDeviceEx(AUDIO_DEVICE_SAMPLE_RATE, sampleSize, AUDIO_DEVICE_CHANNELS);
|
||||
}
|
||||
|
||||
// Close the audio recording device for all contexts
|
||||
void CloseAudioRecordingDevice(void)
|
||||
{
|
||||
if (AUDIO.RecordingSystem.isReady)
|
||||
{
|
||||
ma_mutex_uninit(&AUDIO.RecordingSystem.lock);
|
||||
ma_device_uninit(&AUDIO.RecordingSystem.device);
|
||||
ma_context_uninit(&AUDIO.RecordingSystem.context);
|
||||
|
||||
AUDIO.RecordingSystem.isReady = false;
|
||||
|
||||
TRACELOG(LOG_INFO, "AUDIO: Recording device closed successfully");
|
||||
}
|
||||
else TRACELOG(LOG_WARNING, "AUDIO: Recording device could not be closed, not currently initialized");
|
||||
}
|
||||
|
||||
// Check if recording device has been initialized successfully
|
||||
bool IsAudioRecordingDeviceReady(void)
|
||||
{
|
||||
return AUDIO.RecordingSystem.isReady;
|
||||
}
|
||||
|
||||
// Get recording sample rate
|
||||
unsigned int GetAudioRecordingSampleRate(void)
|
||||
{
|
||||
return AUDIO.RecordingSystem.device.sampleRate;
|
||||
}
|
||||
|
||||
// Get recording sample size in bits
|
||||
unsigned int GetAudioRecordingSampleSize(void)
|
||||
{
|
||||
return 8 * ma_get_bytes_per_sample(AUDIO.RecordingSystem.device.capture.format);
|
||||
}
|
||||
|
||||
// Get recording channels
|
||||
unsigned int GetAudioRecordingChannels(void)
|
||||
{
|
||||
return AUDIO.RecordingSystem.device.capture.channels;
|
||||
}
|
||||
|
||||
// Set master volume (recording)
|
||||
void SetMasterRecordingVolume(float volume)
|
||||
{
|
||||
ma_device_set_master_volume(&AUDIO.RecordingSystem.device, volume);
|
||||
}
|
||||
|
||||
// Get master volume (recording)
|
||||
float GetMasterRecordingVolume(void)
|
||||
{
|
||||
float volume = 0.0f;
|
||||
ma_device_get_master_volume(&AUDIO.RecordingSystem.device, &volume);
|
||||
return volume;
|
||||
}
|
||||
|
||||
// Audio recording callback to receive new data
|
||||
void SetAudioRecordingCallback(AudioRecordingCallback callback)
|
||||
{
|
||||
ma_mutex_lock(&AUDIO.RecordingSystem.lock);
|
||||
AUDIO.RecordingSystem.callback = callback;
|
||||
ma_mutex_unlock(&AUDIO.RecordingSystem.lock);
|
||||
}
|
||||
|
||||
// Start audio recording
|
||||
void StartAudioRecording(void)
|
||||
{
|
||||
if (!AUDIO.RecordingSystem.isReady)
|
||||
{
|
||||
TRACELOG(LOG_WARNING, "AUDIO: Recording device could not be started, not currently initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
ma_result result = ma_device_start(&AUDIO.RecordingSystem.device);
|
||||
if (result != MA_SUCCESS)
|
||||
{
|
||||
TRACELOG(LOG_WARNING, "AUDIO: Failed to start recording device");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop audio recording
|
||||
void StopAudioRecording(void)
|
||||
{
|
||||
if (!AUDIO.RecordingSystem.isReady)
|
||||
{
|
||||
TRACELOG(LOG_WARNING, "AUDIO: Recording device could not be stopped, not currently initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
ma_result result = ma_device_stop(&AUDIO.RecordingSystem.device);
|
||||
if (result != MA_SUCCESS)
|
||||
{
|
||||
TRACELOG(LOG_WARNING, "AUDIO: Failed to stop recording device");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if device is recording
|
||||
bool IsAudioRecording(void)
|
||||
{
|
||||
if (!AUDIO.RecordingSystem.isReady)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ma_device_is_started(&AUDIO.RecordingSystem.device);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Internal Functions Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
|
|
@ -2653,6 +2836,22 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
|
|||
ma_mutex_unlock(&AUDIO.System.lock);
|
||||
}
|
||||
|
||||
// Sending audio data to device callback function
|
||||
// This function will be called when miniaudio sends more data
|
||||
static void OnReceiveAudioDataFromDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount)
|
||||
{
|
||||
AudioRecordingCallback callback = NULL;
|
||||
|
||||
ma_mutex_lock(&AUDIO.RecordingSystem.lock);
|
||||
callback = AUDIO.RecordingSystem.callback;
|
||||
ma_mutex_unlock(&AUDIO.RecordingSystem.lock);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
callback(pFramesInput, frameCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Main mixing function, pretty simple in this project, only an accumulation
|
||||
// NOTE: framesOut is both an input and an output, it is initially filled with zeros outside of this function
|
||||
static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer)
|
||||
|
|
|
|||
18
src/raylib.h
18
src/raylib.h
|
|
@ -1663,9 +1663,10 @@ RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vect
|
|||
RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Audio Loading and Playing Functions (Module: audio)
|
||||
// Audio Loading, Playing and Recording Functions (Module: audio)
|
||||
//------------------------------------------------------------------------------------
|
||||
typedef void (*AudioCallback)(void *bufferData, unsigned int frames);
|
||||
typedef void (*AudioRecordingCallback)(const void *bufferData, unsigned int frames);
|
||||
|
||||
// Audio device management functions
|
||||
RLAPI void InitAudioDevice(void); // Initialize audio device and context
|
||||
|
|
@ -1674,6 +1675,21 @@ RLAPI bool IsAudioDeviceReady(void); // Check i
|
|||
RLAPI void SetMasterVolume(float volume); // Set master volume (listener)
|
||||
RLAPI float GetMasterVolume(void); // Get master volume (listener)
|
||||
|
||||
// Audio recording device management functions
|
||||
RLAPI void InitAudioRecordingDevice(void); // Initialize audio recording device
|
||||
RLAPI void InitAudioRecordingDeviceEx(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Initialize audio recording device with custom sampleRate, sampleSize and channels
|
||||
RLAPI void CloseAudioRecordingDevice(void); // Close the audio recording device for all contexts
|
||||
RLAPI bool IsAudioRecordingDeviceReady(void); // Check if recording device has been initialized successfully
|
||||
RLAPI unsigned int GetAudioRecordingSampleRate(void); // Get recording sample rate
|
||||
RLAPI unsigned int GetAudioRecordingSampleSize(void); // Get recording sample size in bits
|
||||
RLAPI unsigned int GetAudioRecordingChannels(void); // Get recording channels
|
||||
RLAPI void SetMasterRecordingVolume(float volume); // Set master volume (recording)
|
||||
RLAPI float GetMasterRecordingVolume(void); // Get master volume (recording)
|
||||
RLAPI void SetAudioRecordingCallback(AudioRecordingCallback callback); // Audio recording callback to receive new data
|
||||
RLAPI void StartAudioRecording(void); // Start audio recording
|
||||
RLAPI void StopAudioRecording(void); // Stop audio recording
|
||||
RLAPI bool IsAudioRecording(void); // Check if device is recording
|
||||
|
||||
// Wave/Sound loading/unloading functions
|
||||
RLAPI Wave LoadWave(const char *fileName); // Load wave data from file
|
||||
RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav'
|
||||
|
|
|
|||
Loading…
Reference in a new issue