Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
miniaudio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
miniaudio
Commits
81ca0ccb
Commit
81ca0ccb
authored
Mar 08, 2018
by
David Reid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Introduce the notion of default device configurations.
parent
165c92ec
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
130 additions
and
31 deletions
+130
-31
mini_al.h
mini_al.h
+130
-31
No files found.
mini_al.h
View file @
81ca0ccb
...
...
@@ -1102,6 +1102,10 @@ struct mal_device
mal_event
stopEvent
;
mal_thread
thread
;
mal_result
workResult
;
// This is set by the worker thread after it's finished doing a job.
mal_bool32
usingDefaultFormat
:
1
;
mal_bool32
usingDefaultChannels
:
1
;
mal_bool32
usingDefaultSampleRate
:
1
;
mal_bool32
usingDefaultChannelMap
:
1
;
mal_bool32
usingDefaultBufferSize
:
1
;
mal_bool32
usingDefaultPeriods
:
1
;
mal_bool32
exclusiveMode
:
1
;
...
...
@@ -1255,7 +1259,7 @@ struct mal_device
//
// The context is used for selecting and initializing the relevant backends.
//
// Note that the location of the
device
cannot change throughout it's lifetime. Consider allocating
// Note that the location of the
context
cannot change throughout it's lifetime. Consider allocating
// the mal_context object with malloc() if this is an issue. The reason for this is that a pointer
// to the context is stored in the mal_device structure.
//
...
...
@@ -1321,27 +1325,37 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma
//
// mal_context_init(NULL, 0, NULL, &context);
//
// Do not pass in null for the context if you are needing to open multiple devices.
// Do not pass in null for the context if you are needing to open multiple devices. You can,
// however, use null when initializing the first device, and then use device.pContext for the
// initialization of other devices.
//
// The device ID (pDeviceID) can be null, in which case the default device is used. Otherwise, you
// can retrieve the ID by calling mal_enumerate_devices() and using the ID from the returned data.
// Set pDeviceID to NULL to use the default device. Do _not_ rely on the first device ID returned
// by mal_enumerate_devices() to be the default device.
//
// This will try it's hardest to create a valid device, even if it means adjusting input arguments.
// Look at pDevice->internalChannels, pDevice->internalSampleRate, etc. to determine the actual
// properties after initialization.
// The device's configuration is controlled with pConfig. This allows you to configure the sample
// format, channel count, sample rate, etc. Before calling mal_device_init(), you will most likely
// want to initialize a mal_device_config object using mal_device_config_init(),
// mal_device_config_init_playback(), etc. You can also pass in NULL for the device config in
// which case it will use defaults, but will require you to call mal_device_set_recv_callback() or
// mal_device_set_send_callback() before starting the device.
//
// If <bufferSizeInFrames> is 0, it will default to MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS. If
// <periods> is set to 0 it will default to MAL_DEFAULT_PERIODS.
// Passing in 0 to any property in pConfig will force the use of a default value. In the case of
// sample format, channel count, sample rate and channel map it will default to the values used by
// the backend's internal device. If <bufferSizeInFrames> is 0, it will default to
// MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS. If <periods> is set to 0 it will default to
// MAL_DEFAULT_PERIODS.
//
// When sending or receiving data to/from a device, mini_al will internally perform a format
// conversion to convert between the format specified by pConfig and the format used internally by
// the backend. If you pass in NULL for pConfig or 0 for the sample format, channel count,
// sample rate _and_ channel map, data transmission will run on an optimized pass-through fast path.
//
// The <periods> property controls how frequently the background thread is woken to check for more
// data. It's tied to the buffer size, so as an example, if your buffer size is equivalent to 10
// milliseconds and you have 2 periods, the CPU will wake up approximately every 5 milliseconds.
//
// Use mal_device_config_init(), mal_device_config_init_playback(), etc. to initialize a
// mal_device_config object.
//
// When compiling for UWP you must ensure you call this function on the main UI thread because the
// operating system may need to present the user with a message asking for permissions. Please refer
// to the official documentation for ActivateAudioInterfaceAsync() for more information.
...
...
@@ -1351,7 +1365,7 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma
//
// Thread Safety: UNSAFE
// It is not safe to call this function simultaneously for different devices because some backends
// depend on and mutate global state (such as OpenSL|ES). The same applies to calling this a
s
the
// depend on and mutate global state (such as OpenSL|ES). The same applies to calling this a
t
the
// same time as mal_device_uninit().
//
// Results are undefined if you try using a device before this function has returned.
...
...
@@ -1515,6 +1529,19 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format);
// Helper function for initializing a mal_context_config object.
mal_context_config
mal_context_config_init
(
mal_log_proc
onLog
);
// Initializes a default device config.
//
// A default configuration will configure the device such that the format, channel count, sample rate and channel map are
// the same as the backend's internal configuration. This means the application loses explicit control of these properties,
// but in return gets an optimized fast path for data transmission since mini_al will be releived of all format conversion
// duties. You will not typically want to use default configurations unless you have some specific low-latency requirements.
//
// mal_device_config_init(), mal_device_config_init_playback(), etc. will allow you to explicitly set the sample format,
// channel count, etc.
mal_device_config
mal_device_config_init_default
();
mal_device_config
mal_device_config_init_default_capture
(
mal_recv_proc
onRecvCallback
);
mal_device_config
mal_device_config_init_default_playback
(
mal_send_proc
onSendCallback
);
// Helper function for initializing a mal_device_config object.
//
// This is just a helper API, and as such the returned object can be safely modified as needed.
...
...
@@ -1999,6 +2026,21 @@ typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)();
#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped.
// The default format when mal_format_unknown (0) is requested when initializing a device.
#ifndef MAL_DEFAULT_FORMAT
#define MAL_DEFAULT_FORMAT mal_format_f32
#endif
// The default channel count to use when 0 is used when initializing a device.
#ifndef MAL_DEFAULT_CHANNELS
#define MAL_DEFAULT_CHANNELS 2
#endif
// The default sample rate to use when 0 is used when initializing a device.
#ifndef MAL_DEFAULT_SAMPLE_RATE
#define MAL_DEFAULT_SAMPLE_RATE 48000
#endif
// The default size of the device's buffer in milliseconds.
//
// If this is too small you may get underruns and overruns in which case you'll need to either increase
...
...
@@ -11514,10 +11556,12 @@ static mal_result mal_device__stop_backend__sdl(mal_device* pDevice)
mal_bool32
mal__is_channel_map_valid
(
const
mal_channel
*
channelMap
,
mal_uint32
channels
)
{
mal_assert
(
channels
>
0
);
// A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context.
if
(
channelMap
[
0
]
!=
MAL_CHANNEL_NONE
)
{
if
(
channels
==
0
)
{
return
MAL_FALSE
;
// No channels.
}
// A channel cannot be present in the channel map more than once.
for
(
mal_uint32
iChannel
=
0
;
iChannel
<
channels
;
++
iChannel
)
{
for
(
mal_uint32
jChannel
=
iChannel
+
1
;
jChannel
<
channels
;
++
jChannel
)
{
...
...
@@ -12229,13 +12273,23 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
if
(
pDevice
==
NULL
)
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with invalid arguments (pDevice == NULL)."
,
MAL_INVALID_ARGS
);
}
// The config is allowed to be NULL, in which case we default to mal_device_config_init_default().
mal_device_config
config
;
if
(
pConfig
==
NULL
)
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with invalid arguments (pConfig == NULL)."
,
MAL_INVALID_ARGS
);
config
=
mal_device_config_init_default
();
}
else
{
config
=
*
pConfig
;
}
// Basic config validation.
if
(
config
.
channels
>
MAL_MAX_CHANNELS
)
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with an invalid config. Channel count cannot exceed 32."
,
MAL_INVALID_DEVICE_CONFIG
);
}
if
(
!
mal__is_channel_map_valid
(
config
.
channelMap
,
config
.
channels
))
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with invalid config. Channel map is invalid."
,
MAL_INVALID_DEVICE_CONFIG
);
}
// Make a copy of the config to ensure we don't override the caller's object.
mal_device_config
config
=
*
pConfig
;
mal_zero_object
(
pDevice
);
pDevice
->
pContext
=
pContext
;
...
...
@@ -12253,20 +12307,22 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
}
// Basic config validation.
if
(
config
.
channels
==
0
)
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with an invalid config. Channel count must be greater than 0."
,
MAL_INVALID_DEVICE_CONFIG
);
// When passing in 0 for the format/channels/rate/chmap it means the device will be using whatever is chosen by the backend. If everything is set
// to defaults it means the format conversion pipeline will run on a fast path where data transfer is just passed straight through to the backend.
if
(
config
.
format
==
mal_format_unknown
)
{
config
.
format
=
MAL_DEFAULT_FORMAT
;
pDevice
->
usingDefaultFormat
=
MAL_TRUE
;
}
if
(
config
.
channels
>
MAL_MAX_CHANNELS
)
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with an invalid config. Channel count cannot exceed 18."
,
MAL_INVALID_DEVICE_CONFIG
);
if
(
config
.
channels
==
0
)
{
config
.
channels
=
MAL_DEFAULT_CHANNELS
;
pDevice
->
usingDefaultChannels
=
MAL_TRUE
;
}
if
(
config
.
sampleRate
==
0
)
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with an invalid config. Sample rate must be greater than 0."
,
MAL_INVALID_DEVICE_CONFIG
);
config
.
sampleRate
=
MAL_DEFAULT_SAMPLE_RATE
;
pDevice
->
usingDefaultSampleRate
=
MAL_TRUE
;
}
if
(
!
mal__is_channel_map_valid
(
pConfig
->
channelMap
,
pConfig
->
channels
))
{
return
mal_post_error
(
pDevice
,
"mal_device_init() called with invalid arguments. Channel map is invalid."
,
MAL_INVALID_DEVICE_CONFIG
);
if
(
config
.
channelMap
[
0
]
==
MAL_CHANNEL_NONE
)
{
pDevice
->
usingDefaultChannelMap
=
MAL_TRUE
;
}
...
...
@@ -12283,8 +12339,8 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
pDevice
->
type
=
type
;
pDevice
->
format
=
config
.
format
;
pDevice
->
channels
=
config
.
channels
;
mal_copy_memory
(
pDevice
->
channelMap
,
config
.
channelMap
,
sizeof
(
config
.
channelMap
[
0
])
*
config
.
channels
);
pDevice
->
sampleRate
=
config
.
sampleRate
;
mal_copy_memory
(
pDevice
->
channelMap
,
config
.
channelMap
,
sizeof
(
config
.
channelMap
[
0
])
*
config
.
channels
);
pDevice
->
bufferSizeInFrames
=
config
.
bufferSizeInFrames
;
pDevice
->
periods
=
config
.
periods
;
...
...
@@ -12418,6 +12474,20 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
}
}
// If the format/channels/rate is using defaults we need to set these to be the same as the internal config.
if
(
pDevice
->
usingDefaultFormat
)
{
pDevice
->
format
=
pDevice
->
internalFormat
;
}
if
(
pDevice
->
usingDefaultChannels
)
{
pDevice
->
channels
=
pDevice
->
internalChannels
;
}
if
(
pDevice
->
usingDefaultSampleRate
)
{
pDevice
->
sampleRate
=
pDevice
->
internalSampleRate
;
}
if
(
pDevice
->
usingDefaultChannelMap
)
{
mal_copy_memory
(
pDevice
->
channelMap
,
pDevice
->
internalChannelMap
,
sizeof
(
pDevice
->
channelMap
));
}
// We need a DSP object which is where samples are moved through in order to convert them to the
// format required by the backend.
...
...
@@ -12447,7 +12517,6 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
// Some backends don't require the worker thread.
if
(
pContext
->
backend
!=
mal_backend_jack
&&
pContext
->
backend
!=
mal_backend_opensl
&&
pContext
->
backend
!=
mal_backend_sdl
)
{
// The worker thread.
...
...
@@ -12774,6 +12843,31 @@ mal_context_config mal_context_config_init(mal_log_proc onLog)
return
config
;
}
mal_device_config
mal_device_config_init_default
()
{
mal_device_config
config
;
mal_zero_object
(
&
config
);
return
config
;
}
mal_device_config
mal_device_config_init_default_capture
(
mal_recv_proc
onRecvCallback
)
{
mal_device_config
config
=
mal_device_config_init_default
();
config
.
onRecvCallback
=
onRecvCallback
;
return
config
;
}
mal_device_config
mal_device_config_init_default_playback
(
mal_send_proc
onSendCallback
)
{
mal_device_config
config
=
mal_device_config_init_default
();
config
.
onSendCallback
=
onSendCallback
;
return
config
;
}
static
void
mal_get_default_device_config_channel_map
(
mal_uint32
channels
,
mal_channel
channelMap
[
MAL_MAX_CHANNELS
])
{
mal_zero_memory
(
channelMap
,
sizeof
(
mal_channel
)
*
MAL_MAX_CHANNELS
);
...
...
@@ -12857,8 +12951,7 @@ static void mal_get_default_device_config_channel_map(mal_uint32 channels, mal_c
mal_device_config
mal_device_config_init_ex
(
mal_format
format
,
mal_uint32
channels
,
mal_uint32
sampleRate
,
mal_channel
channelMap
[
MAL_MAX_CHANNELS
],
mal_recv_proc
onRecvCallback
,
mal_send_proc
onSendCallback
)
{
mal_device_config
config
;
mal_zero_object
(
&
config
);
mal_device_config
config
=
mal_device_config_init_default
();
config
.
format
=
format
;
config
.
channels
=
channels
;
...
...
@@ -12872,11 +12965,12 @@ mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channe
mal_copy_memory
(
config
.
channelMap
,
channelMap
,
sizeof
(
config
.
channelMap
));
}
return
config
;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SRC
...
...
@@ -15607,11 +15701,16 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count)
// - Add support for JACK.
// - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no
// longer required to build mini_al.
// - Introduce the notion of default device configurations. A default config uses the same configuration
// as the backend's internal device, and as such results in a pass-through data transmission pipeline.
// - Add support for passing in NULL for the device config in mal_device_init(), which uses a default
// config. This requires manually calling mal_device_set_send/recv_callback().
// - Make mal_device_init_ex() more robust.
// - Make some APIs more const-correct.
// - Fix errors with OpenAL detection.
// - Fix some memory leaks.
// - Miscellaneous bug fixes.
// - Documentation updates.
//
// v0.7 - 2018-02-25
// - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment