view release on metacpan or search on metacpan
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
ma_format format;
ma_uint32 channels;
ma_channel channelMap[MA_MAX_CHANNELS];
ma_format internalFormat;
ma_uint32 internalChannels;
ma_uint32 internalSampleRate;
ma_channel internalChannelMap[MA_MAX_CHANNELS];
ma_uint32 internalPeriodSizeInFrames;
ma_uint32 internalPeriods;
ma_data_converter converter;
} capture;
union
{
#ifdef MA_SUPPORT_WASAPI
struct
{
/*IAudioClient**/ ma_ptr pAudioClientPlayback;
/*IAudioClient**/ ma_ptr pAudioClientCapture;
/*IAudioRenderClient**/ ma_ptr pRenderClient;
/*IAudioCaptureClient**/ ma_ptr pCaptureClient;
/*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
ma_IMMNotificationClient notificationClient;
/*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
/*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
ma_uint32 actualPeriodSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */
ma_uint32 actualPeriodSizeInFramesCapture;
ma_uint32 originalPeriodSizeInFrames;
ma_uint32 originalPeriodSizeInMilliseconds;
ma_uint32 originalPeriods;
ma_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
ma_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
ma_uint32 periodSizeInFramesPlayback;
ma_uint32 periodSizeInFramesCapture;
ma_bool32 isStartedCapture; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
ma_bool32 isStartedPlayback; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
ma_bool32 noAutoConvertSRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
ma_bool32 noDefaultQualitySRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
ma_bool32 noHardwareOffloading : 1;
ma_bool32 allowCaptureAutoStreamRouting : 1;
ma_bool32 allowPlaybackAutoStreamRouting : 1;
} wasapi;
#endif
#ifdef MA_SUPPORT_DSOUND
struct
{
/*LPDIRECTSOUND*/ ma_ptr pPlayback;
/*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
/*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
/*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
/*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
} dsound;
#endif
#ifdef MA_SUPPORT_WINMM
struct
{
/*HWAVEOUT*/ ma_handle hDevicePlayback;
/*HWAVEIN*/ ma_handle hDeviceCapture;
/*HANDLE*/ ma_handle hEventPlayback;
/*HANDLE*/ ma_handle hEventCapture;
ma_uint32 fragmentSizeInFrames;
ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
ma_uint32 headerFramesConsumedCapture; /* ^^^ */
/*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
/*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
ma_uint8* pIntermediaryBufferPlayback;
ma_uint8* pIntermediaryBufferCapture;
ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
} winmm;
#endif
#ifdef MA_SUPPORT_ALSA
struct
{
/*snd_pcm_t**/ ma_ptr pPCMPlayback;
/*snd_pcm_t**/ ma_ptr pPCMCapture;
ma_bool32 isUsingMMapPlayback : 1;
ma_bool32 isUsingMMapCapture : 1;
} alsa;
#endif
#ifdef MA_SUPPORT_PULSEAUDIO
struct
{
/*pa_mainloop**/ ma_ptr pMainLoop;
/*pa_mainloop_api**/ ma_ptr pAPI;
/*pa_context**/ ma_ptr pPulseContext;
/*pa_stream**/ ma_ptr pStreamPlayback;
/*pa_stream**/ ma_ptr pStreamCapture;
/*pa_context_state*/ ma_uint32 pulseContextState;
void* pMappedBufferPlayback;
const void* pMappedBufferCapture;
ma_uint32 mappedBufferFramesRemainingPlayback;
ma_uint32 mappedBufferFramesRemainingCapture;
ma_uint32 mappedBufferFramesCapacityPlayback;
ma_uint32 mappedBufferFramesCapacityCapture;
ma_bool32 breakFromMainLoop : 1;
} pulse;
#endif
#ifdef MA_SUPPORT_JACK
struct
{
/*jack_client_t**/ ma_ptr pClient;
/*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS];
/*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS];
float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
float* pIntermediaryBufferCapture;
ma_pcm_rb duplexRB;
} jack;
#endif
#ifdef MA_SUPPORT_COREAUDIO
struct
{
ma_uint32 deviceObjectIDPlayback;
ma_uint32 deviceObjectIDCapture;
/*AudioUnit*/ ma_ptr audioUnitPlayback;
/*AudioUnit*/ ma_ptr audioUnitCapture;
/*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
ma_event stopEvent;
ma_uint32 originalPeriodSizeInFrames;
ma_uint32 originalPeriodSizeInMilliseconds;
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Safe. It's safe to call this from any thread with the exception of the callback thread.
Callback Safety
---------------
Unsafe. It is not safe to call this inside any callback.
Remarks
-------
For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid
audio data in the buffer, which needs to be done before the device begins playback.
This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
Do not call this in any callback.
See Also
--------
ma_device_stop()
*/
MA_API ma_result ma_device_start(ma_device* pDevice);
/*
Stops the device. For playback devices this stops playback. For capture devices it stops recording.
Use `ma_device_start()` to start the device again.
Parameters
----------
pDevice (in)
A pointer to the device to stop.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Safe. It's safe to call this from any thread with the exception of the callback thread.
Callback Safety
---------------
Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
Remarks
-------
This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some
backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size
that was specified at initialization time).
Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and
the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the
speakers or received from the microphone which can in turn result in de-syncs.
Do not call this in any callback.
This will be called implicitly by `ma_device_uninit()`.
See Also
--------
ma_device_start()
*/
MA_API ma_result ma_device_stop(ma_device* pDevice);
/*
Determines whether or not the device is started.
Parameters
----------
pDevice (in)
A pointer to the device whose start state is being retrieved.
Return Value
------------
True if the device is started, false otherwise.
Thread Safety
-------------
Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return
value will be out of sync.
Callback Safety
---------------
Safe. This is implemented as a simple accessor.
See Also
--------
ma_device_start()
ma_device_stop()
*/
MA_API ma_bool32 ma_device_is_started(ma_device* pDevice);
/*
Sets the master volume factor for the device.
The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_gain_db()` to use decibel notation, where 0 is full volume and
values less than 0 decreases the volume.
Parameters
----------
pDevice (in)
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
}
ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
}
static ma_result ma_device_init__winmm(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
{
const char* errorMsg = "";
ma_result errorCode = MA_ERROR;
ma_result result = MA_SUCCESS;
ma_uint32 heapSize;
UINT winMMDeviceIDPlayback = 0;
UINT winMMDeviceIDCapture = 0;
ma_uint32 periodSizeInMilliseconds;
MA_ASSERT(pDevice != NULL);
MA_ZERO_OBJECT(&pDevice->winmm);
if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
/* No exlusive mode with WinMM. */
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
return MA_SHARE_MODE_NOT_SUPPORTED;
}
periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
if (periodSizeInMilliseconds == 0) {
periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate);
}
/* WinMM has horrible latency. */
if (pDevice->usingDefaultBufferSize) {
if (pConfig->performanceProfile == ma_performance_profile_low_latency) {
periodSizeInMilliseconds = 40;
} else {
periodSizeInMilliseconds = 400;
}
}
if (pConfig->playback.pDeviceID != NULL) {
winMMDeviceIDPlayback = (UINT)pConfig->playback.pDeviceID->winmm;
}
if (pConfig->capture.pDeviceID != NULL) {
winMMDeviceIDCapture = (UINT)pConfig->capture.pDeviceID->winmm;
}
/* The capture device needs to be initialized first. */
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
WAVEINCAPSA caps;
WAVEFORMATEX wf;
MMRESULT resultMM;
/* We use an event to know when a new fragment needs to be enqueued. */
pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL);
if (pDevice->winmm.hEventCapture == NULL) {
errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
goto on_error;
}
/* The format should be based on the device's actual format. */
if (((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
goto on_error;
}
result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
if (result != MA_SUCCESS) {
errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
goto on_error;
}
resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
if (resultMM != MMSYSERR_NOERROR) {
errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
goto on_error;
}
pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
pDevice->capture.internalChannels = wf.nChannels;
pDevice->capture.internalSampleRate = wf.nSamplesPerSec;
ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
pDevice->capture.internalPeriods = pConfig->periods;
pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->capture.internalSampleRate);
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
WAVEOUTCAPSA caps;
WAVEFORMATEX wf;
MMRESULT resultMM;
/* We use an event to know when a new fragment needs to be enqueued. */
pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
if (pDevice->winmm.hEventPlayback == NULL) {
errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
goto on_error;
}
/* The format should be based on the device's actual format. */
if (((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
goto on_error;
}
result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
if (result != MA_SUCCESS) {
errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
goto on_error;
}
resultMM = ((MA_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
if (resultMM != MMSYSERR_NOERROR) {
errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
goto on_error;
}
pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
pDevice->playback.internalChannels = wf.nChannels;
pDevice->playback.internalSampleRate = wf.nSamplesPerSec;
ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
pDevice->playback.internalPeriods = pConfig->periods;
pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
}
/*
The heap allocated data is allocated like so:
[Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
*/
heapSize = 0;
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
heapSize += sizeof(WAVEHDR)*pDevice->capture.internalPeriods + (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
heapSize += sizeof(WAVEHDR)*pDevice->playback.internalPeriods + (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
}
pDevice->winmm._pHeapData = (ma_uint8*)ma__calloc_from_callbacks(heapSize, &pContext->allocationCallbacks);
if (pDevice->winmm._pHeapData == NULL) {
errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
goto on_error;
}
MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
ma_uint32 iPeriod;
if (pConfig->deviceType == ma_device_type_capture) {
pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
} else {
pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods));
}
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
#define MA_PA_CHANNEL_POSITION_AUX21 33
#define MA_PA_CHANNEL_POSITION_AUX22 34
#define MA_PA_CHANNEL_POSITION_AUX23 35
#define MA_PA_CHANNEL_POSITION_AUX24 36
#define MA_PA_CHANNEL_POSITION_AUX25 37
#define MA_PA_CHANNEL_POSITION_AUX26 38
#define MA_PA_CHANNEL_POSITION_AUX27 39
#define MA_PA_CHANNEL_POSITION_AUX28 40
#define MA_PA_CHANNEL_POSITION_AUX29 41
#define MA_PA_CHANNEL_POSITION_AUX30 42
#define MA_PA_CHANNEL_POSITION_AUX31 43
#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
typedef int ma_pa_channel_map_def_t;
#define MA_PA_CHANNEL_MAP_AIFF 0
#define MA_PA_CHANNEL_MAP_ALSA 1
#define MA_PA_CHANNEL_MAP_AUX 2
#define MA_PA_CHANNEL_MAP_WAVEEX 3
#define MA_PA_CHANNEL_MAP_OSS 4
#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
typedef int ma_pa_sample_format_t;
#define MA_PA_SAMPLE_INVALID -1
#define MA_PA_SAMPLE_U8 0
#define MA_PA_SAMPLE_ALAW 1
#define MA_PA_SAMPLE_ULAW 2
#define MA_PA_SAMPLE_S16LE 3
#define MA_PA_SAMPLE_S16BE 4
#define MA_PA_SAMPLE_FLOAT32LE 5
#define MA_PA_SAMPLE_FLOAT32BE 6
#define MA_PA_SAMPLE_S32LE 7
#define MA_PA_SAMPLE_S32BE 8
#define MA_PA_SAMPLE_S24LE 9
#define MA_PA_SAMPLE_S24BE 10
#define MA_PA_SAMPLE_S24_32LE 11
#define MA_PA_SAMPLE_S24_32BE 12
typedef struct ma_pa_mainloop ma_pa_mainloop;
typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
typedef struct ma_pa_context ma_pa_context;
typedef struct ma_pa_operation ma_pa_operation;
typedef struct ma_pa_stream ma_pa_stream;
typedef struct ma_pa_spawn_api ma_pa_spawn_api;
typedef struct
{
ma_uint32 maxlength;
ma_uint32 tlength;
ma_uint32 prebuf;
ma_uint32 minreq;
ma_uint32 fragsize;
} ma_pa_buffer_attr;
typedef struct
{
ma_uint8 channels;
ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
} ma_pa_channel_map;
typedef struct
{
ma_uint8 channels;
ma_uint32 values[MA_PA_CHANNELS_MAX];
} ma_pa_cvolume;
typedef struct
{
ma_pa_sample_format_t format;
ma_uint32 rate;
ma_uint8 channels;
} ma_pa_sample_spec;
typedef struct
{
const char* name;
ma_uint32 index;
const char* description;
ma_pa_sample_spec sample_spec;
ma_pa_channel_map channel_map;
ma_uint32 owner_module;
ma_pa_cvolume volume;
int mute;
ma_uint32 monitor_source;
const char* monitor_source_name;
ma_uint64 latency;
const char* driver;
ma_pa_sink_flags_t flags;
void* proplist;
ma_uint64 configured_latency;
ma_uint32 base_volume;
ma_pa_sink_state_t state;
ma_uint32 n_volume_steps;
ma_uint32 card;
ma_uint32 n_ports;
void** ports;
void* active_port;
ma_uint8 n_formats;
void** formats;
} ma_pa_sink_info;
typedef struct
{
const char *name;
ma_uint32 index;
const char *description;
ma_pa_sample_spec sample_spec;
ma_pa_channel_map channel_map;
ma_uint32 owner_module;
ma_pa_cvolume volume;
int mute;
ma_uint32 monitor_of_sink;
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
ma_device* pDevice;
if (endOfList > 0) {
return;
}
pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
(void)pPulseContext; /* Unused. */
}
static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
{
ma_device* pDevice;
if (endOfList > 0) {
return;
}
pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
(void)pPulseContext; /* Unused. */
}
static void ma_device_uninit__pulse(ma_device* pDevice)
{
ma_context* pContext;
MA_ASSERT(pDevice != NULL);
pContext = pDevice->pContext;
MA_ASSERT(pContext != NULL);
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
}
((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
}
static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
{
ma_pa_buffer_attr attr;
attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
attr.tlength = attr.maxlength / periods;
attr.prebuf = (ma_uint32)-1;
attr.minreq = (ma_uint32)-1;
attr.fragsize = attr.maxlength / periods;
return attr;
}
static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap)
{
static int g_StreamCounter = 0;
char actualStreamName[256];
if (pStreamName != NULL) {
ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
} else {
ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
}
g_StreamCounter += 1;
return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
}
static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
{
ma_result result = MA_SUCCESS;
int error = 0;
const char* devPlayback = NULL;
const char* devCapture = NULL;
ma_uint32 periodSizeInMilliseconds;
ma_pa_sink_info sinkInfo;
ma_pa_source_info sourceInfo;
ma_pa_operation* pOP = NULL;
ma_pa_sample_spec ss;
ma_pa_channel_map cmap;
ma_pa_buffer_attr attr;
const ma_pa_sample_spec* pActualSS = NULL;
const ma_pa_channel_map* pActualCMap = NULL;
const ma_pa_buffer_attr* pActualAttr = NULL;
ma_uint32 iChannel;
ma_pa_stream_flags_t streamFlags;
MA_ASSERT(pDevice != NULL);
MA_ZERO_OBJECT(&pDevice->pulse);
if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
/* No exclusive mode with the PulseAudio backend. */
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
return MA_SHARE_MODE_NOT_SUPPORTED;
}
if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL) {
devPlayback = pConfig->playback.pDeviceID->pulse;
}
if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL) {
devCapture = pConfig->capture.pDeviceID->pulse;
}
periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
pDevice->pulse.pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
if (pDevice->pulse.pAPI == NULL) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve PulseAudio main loop.", MA_FAILED_TO_INIT_BACKEND);
goto on_error1;
}
pDevice->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)((ma_pa_mainloop_api*)pDevice->pulse.pAPI, pContext->pulse.pApplicationName);
if (pDevice->pulse.pPulseContext == NULL) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context for device.", MA_FAILED_TO_INIT_BACKEND);
goto on_error1;
}
error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pDevice->pulse.pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
if (error != MA_PA_OK) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", ma_result_from_pulse(error));
goto on_error2;
}
pDevice->pulse.pulseContextState = MA_PA_CONTEXT_UNCONNECTED;
((ma_pa_context_set_state_callback_proc)pContext->pulse.pa_context_set_state_callback)((ma_pa_context*)pDevice->pulse.pPulseContext, ma_pulse_device_state_callback, pDevice);
/* Wait for PulseAudio to get itself ready before returning. */
for (;;) {
if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_READY) {
break;
}
/* An error may have occurred. */
if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_FAILED || pDevice->pulse.pulseContextState == MA_PA_CONTEXT_TERMINATED) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
goto on_error3;
}
error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
if (error < 0) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio context.", ma_result_from_pulse(error));
goto on_error3;
}
}
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_info_callback, &sourceInfo);
if (pOP != NULL) {
ma_device__wait_for_operation__pulse(pDevice, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
} else {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", ma_result_from_pulse(error));
goto on_error3;
}
ss = sourceInfo.sample_spec;
cmap = sourceInfo.channel_map;
pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
pDevice->capture.internalPeriods = pConfig->periods;
attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, &ss);
#ifdef MA_DEBUG_OUTPUT
printf("[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalPeriodSizeInFram...
#endif
pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
if (pDevice->pulse.pStreamCapture == NULL) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
goto on_error3;
}
streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
if (devCapture != NULL) {
streamFlags |= MA_PA_STREAM_DONT_MOVE;
}
error = ((ma_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
if (error != MA_PA_OK) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
goto on_error4;
}
while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamCapture) != MA_PA_STREAM_READY) {
error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
if (error < 0) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio capture stream.", ma_result_from_pulse(error));
goto on_error5;
}
}
/* Internal format. */
pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
if (pActualSS != NULL) {
/* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, pActualSS);
pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &attr, NULL, NULL);
if (pOP != NULL) {
ma_device__wait_for_operation__pulse(pDevice, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
}
ss = *pActualSS;
}
pDevice->capture.internalFormat = ma_format_from_pulse(ss.format);
pDevice->capture.internalChannels = ss.channels;
pDevice->capture.internalSampleRate = ss.rate;
/* Internal channel map. */
pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
if (pActualCMap != NULL) {
cmap = *pActualCMap;
}
for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
}
/* Buffer. */
pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
if (pActualAttr != NULL) {
attr = *pActualAttr;
}
pDevice->capture.internalPeriods = attr.maxlength / attr.fragsize;
pDevice->capture.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pDevice->capture.internalPeriods;
#ifdef MA_DEBUG_OUTPUT
printf("[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalPeriodSiz...
#endif
/* Name. */
devCapture = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
if (devCapture != NULL) {
ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice);
if (pOP != NULL) {
ma_device__wait_for_operation__pulse(pDevice, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
}
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_info_callback, &sinkInfo);
if (pOP != NULL) {
ma_device__wait_for_operation__pulse(pDevice, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
} else {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", ma_result_from_pulse(error));
goto on_error3;
}
ss = sinkInfo.sample_spec;
cmap = sinkInfo.channel_map;
pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
pDevice->playback.internalPeriods = pConfig->periods;
attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, &ss);
#ifdef MA_DEBUG_OUTPUT
printf("[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalPeriodSizeInFr...
#endif
pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
if (pDevice->pulse.pStreamPlayback == NULL) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
goto on_error3;
}
streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
if (devPlayback != NULL) {
streamFlags |= MA_PA_STREAM_DONT_MOVE;
}
error = ((ma_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);
if (error != MA_PA_OK) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
goto on_error6;
}
while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamPlayback) != MA_PA_STREAM_READY) {
error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
if (error < 0) {
result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio playback stream.", ma_result_from_pulse(error));
goto on_error7;
}
}
/* Internal format. */
pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
if (pActualSS != NULL) {
/* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, pActualSS);
pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &attr, NULL, NULL);
if (pOP != NULL) {
ma_device__wait_for_operation__pulse(pDevice, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
}
ss = *pActualSS;
}
pDevice->playback.internalFormat = ma_format_from_pulse(ss.format);
pDevice->playback.internalChannels = ss.channels;
pDevice->playback.internalSampleRate = ss.rate;
/* Internal channel map. */
pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
if (pActualCMap != NULL) {
cmap = *pActualCMap;
}
for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
}
/* Buffer. */
pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
if (pActualAttr != NULL) {
attr = *pActualAttr;
}
pDevice->playback.internalPeriods = attr.maxlength / attr.tlength;
pDevice->playback.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) / pDevice->playback.internalPeriods;
#ifdef MA_DEBUG_OUTPUT
printf("[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalPeriodS...
#endif
/* Name. */
devPlayback = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
if (devPlayback != NULL) {
ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice);
if (pOP != NULL) {
ma_device__wait_for_operation__pulse(pDevice, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
}
}
return MA_SUCCESS;
on_error7:
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
}
on_error6:
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
}
on_error5:
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
}
on_error4:
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
}
on_error3: ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
on_error2: ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
on_error1: ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
on_error0:
return result;
}
static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
{
ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
MA_ASSERT(pIsSuccessful != NULL);
*pIsSuccessful = (ma_bool32)success;
(void)pStream; /* Unused. */
}
static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
{
ma_context* pContext = pDevice->pContext;
ma_bool32 wasSuccessful;
ma_pa_stream* pStream;
ma_pa_operation* pOP;
ma_result result;
/* This should not be called with a duplex device type. */
if (deviceType == ma_device_type_duplex) {
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to map write buffer.", ma_result_from_pulse(error));
}
pDevice->pulse.mappedBufferFramesCapacityPlayback = bytesToMap / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
pDevice->pulse.mappedBufferFramesRemainingPlayback = pDevice->pulse.mappedBufferFramesCapacityPlayback;
break;
} else {
/* No data available. Need to wait for more. */
int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
if (error < 0) {
return ma_result_from_pulse(error);
}
continue;
}
} else {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's writable size.", MA_ERROR);
}
}
}
if (pFramesWritten != NULL) {
*pFramesWritten = totalFramesWritten;
}
return MA_SUCCESS;
}
static ma_result ma_device_read__pulse(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
{
ma_uint32 totalFramesRead;
MA_ASSERT(pDevice != NULL);
MA_ASSERT(pPCMFrames != NULL);
MA_ASSERT(frameCount > 0);
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
return MA_DEVICE_NOT_STARTED;
}
/*
If a buffer is mapped we need to read from that first. Once it's consumed we need to drop it. Note that pDevice->pulse.pMappedBufferCapture can be null in which
case it could be a hole. In this case we just write zeros into the output buffer.
*/
if (pDevice->pulse.mappedBufferFramesRemainingCapture > 0) {
ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityCapture - pDevice->pulse.mappedBufferFramesRemainingCapture;
ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingCapture, (frameCount - totalFramesRead));
void* pDst = (ma_uint8*)pPCMFrames + (totalFramesRead * bpf);
/*
This little bit of logic here is specifically for PulseAudio and it's hole management. The buffer pointer will be set to NULL
when the current fragment is a hole. For a hole we just output silence.
*/
if (pDevice->pulse.pMappedBufferCapture != NULL) {
const void* pSrc = (const ma_uint8*)pDevice->pulse.pMappedBufferCapture + (mappedBufferFramesConsumed * bpf);
MA_COPY_MEMORY(pDst, pSrc, framesToCopy * bpf);
} else {
MA_ZERO_MEMORY(pDst, framesToCopy * bpf);
#if defined(MA_DEBUG_OUTPUT)
printf("[PulseAudio] ma_device_read__pulse: Filling hole with silence.\n");
#endif
}
pDevice->pulse.mappedBufferFramesRemainingCapture -= framesToCopy;
totalFramesRead += framesToCopy;
}
/*
Getting here means we've run out of data in the currently mapped chunk. We need to drop this from the device and then try
mapping another chunk. If this fails we need to wait for data to become available.
*/
if (pDevice->pulse.mappedBufferFramesCapacityCapture > 0 && pDevice->pulse.mappedBufferFramesRemainingCapture == 0) {
int error;
#if defined(MA_DEBUG_OUTPUT)
printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_drop()\n");
#endif
error = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
if (error != 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to drop fragment.", ma_result_from_pulse(error));
}
pDevice->pulse.pMappedBufferCapture = NULL;
pDevice->pulse.mappedBufferFramesRemainingCapture = 0;
pDevice->pulse.mappedBufferFramesCapacityCapture = 0;
}
MA_ASSERT(totalFramesRead <= frameCount);
if (totalFramesRead == frameCount) {
break;
}
/* Getting here means we need to map a new buffer. If we don't have enough data we wait for more. */
for (;;) {
int error;
size_t bytesMapped;
if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
break;
}
/* If the device has been corked, don't try to continue. */
if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamCapture)) {
#if defined(MA_DEBUG_OUTPUT)
printf("[PulseAudio] ma_device_read__pulse: Corked.\n");
#endif
break;
}
MA_ASSERT(pDevice->pulse.pMappedBufferCapture == NULL); /* <-- We're about to map a buffer which means we shouldn't have an existing mapping. */
error = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &pDevice->pulse.pMappedBufferCapture, &bytesMapped);
if (error < 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to peek capture buffer.", ma_result_from_pulse(error));
}
if (bytesMapped > 0) {
pDevice->pulse.mappedBufferFramesCapacityCapture = bytesMapped / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
pDevice->pulse.mappedBufferFramesRemainingCapture = pDevice->pulse.mappedBufferFramesCapacityCapture;
#if defined(MA_DEBUG_OUTPUT)
printf("[PulseAudio] ma_device_read__pulse: Mapped. mappedBufferFramesCapacityCapture=%d, mappedBufferFramesRemainingCapture=%d\n", pDevice->pulse.mappedBufferFramesCapacityCapture, pDevice->pulse.mappedBufferFramesRemainingCaptur...
#endif
if (pDevice->pulse.pMappedBufferCapture == NULL) {
/* It's a hole. */
#if defined(MA_DEBUG_OUTPUT)
printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_peek(). Hole.\n");
#endif
}
break;
} else {
if (pDevice->pulse.pMappedBufferCapture == NULL) {
/* Nothing available yet. Need to wait for more. */
/*
I have had reports of a deadlock in this part of the code. I have reproduced this when using the "Built-in Audio Analogue Stereo" device without
an actual microphone connected. I'm experimenting here by not blocking in pa_mainloop_iterate() and instead sleep for a bit when there are no
dispatches.
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
int ossSampleRate;
int ossFragment;
MA_ASSERT(pContext != NULL);
MA_ASSERT(pConfig != NULL);
MA_ASSERT(deviceType != ma_device_type_duplex);
MA_ASSERT(pDevice != NULL);
(void)pContext;
if (deviceType == ma_device_type_capture) {
pDeviceID = pConfig->capture.pDeviceID;
shareMode = pConfig->capture.shareMode;
ossFormat = ma_format_to_oss(pConfig->capture.format);
ossChannels = (int)pConfig->capture.channels;
ossSampleRate = (int)pConfig->sampleRate;
} else {
pDeviceID = pConfig->playback.pDeviceID;
shareMode = pConfig->playback.shareMode;
ossFormat = ma_format_to_oss(pConfig->playback.format);
ossChannels = (int)pConfig->playback.channels;
ossSampleRate = (int)pConfig->sampleRate;
}
result = ma_context_open_device__oss(pContext, deviceType, pDeviceID, shareMode, &fd);
if (result != MA_SUCCESS) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
}
/*
The OSS documantation is very clear about the order we should be initializing the device's properties:
1) Format
2) Channels
3) Sample rate.
*/
/* Format. */
ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
if (ossResult == -1) {
close(fd);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED);
}
/* Channels. */
ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
if (ossResult == -1) {
close(fd);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED);
}
/* Sample Rate. */
ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
if (ossResult == -1) {
close(fd);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED);
}
/*
Buffer.
The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
it should be done before or after format/channels/rate.
OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
value.
*/
{
ma_uint32 periodSizeInFrames;
ma_uint32 periodSizeInBytes;
ma_uint32 ossFragmentSizePower;
periodSizeInFrames = pConfig->periodSizeInFrames;
if (periodSizeInFrames == 0) {
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, (ma_uint32)ossSampleRate);
}
periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
if (periodSizeInBytes < 16) {
periodSizeInBytes = 16;
}
ossFragmentSizePower = 4;
periodSizeInBytes >>= 4;
while (periodSizeInBytes >>= 1) {
ossFragmentSizePower += 1;
}
ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
if (ossResult == -1) {
close(fd);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED);
}
}
/* Internal settings. */
if (deviceType == ma_device_type_capture) {
pDevice->oss.fdCapture = fd;
pDevice->capture.internalFormat = ma_format_from_oss(ossFormat);
pDevice->capture.internalChannels = ossChannels;
pDevice->capture.internalSampleRate = ossSampleRate;
ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
pDevice->capture.internalPeriods = (ma_uint32)(ossFragment >> 16);
pDevice->capture.internalPeriodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
if (pDevice->capture.internalFormat == ma_format_unknown) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
}
} else {
pDevice->oss.fdPlayback = fd;
pDevice->playback.internalFormat = ma_format_from_oss(ossFormat);
pDevice->playback.internalChannels = ossChannels;
pDevice->playback.internalSampleRate = ossSampleRate;
ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
pDevice->playback.internalPeriods = (ma_uint32)(ossFragment >> 16);
pDevice->playback.internalPeriodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
if (pDevice->playback.internalFormat == ma_format_unknown) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
}
}
return MA_SUCCESS;
}
static ma_result ma_device_init__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pDevice != NULL);
MA_ZERO_OBJECT(&pDevice->oss);
if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_capture, pDevice);
if (result != MA_SUCCESS) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
}
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_playback, pDevice);
if (result != MA_SUCCESS) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
}
}
return MA_SUCCESS;