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
1b185878
Commit
1b185878
authored
Jan 22, 2021
by
David Reid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update the resource_manager_advanced example.
parent
81a3b5d0
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
126 additions
and
39 deletions
+126
-39
research/_examples/resource_manager_advanced.c
research/_examples/resource_manager_advanced.c
+126
-39
No files found.
research/_examples/resource_manager_advanced.c
View file @
1b185878
...
...
@@ -5,8 +5,7 @@ The resource manager can be used to create a data source whose resources are man
sources can then be read just like any other data source such as decoders and audio buffers.
In this example we use the resource manager independently of the `ma_engine` API so that we can demonstrate how it can
be used by itself without getting it confused with `ma_engine`. Audio data is mixed using the `ma_mixer` API, but you
can also use data sources with `ma_data_source_read_pcm_frames()` in the same way we do in the simple_looping example.
be used by itself without getting it confused with `ma_engine`.
The main feature of the resource manager is the ability to decode and stream audio data asynchronously. Asynchronicity
is achieved with a job system. The resource manager will issue jobs which are processed by a configurable number of job
...
...
@@ -20,29 +19,135 @@ threads to manage internally and how to implement your own custom job thread.
#include "../../miniaudio.h"
#include "../miniaudio_engine.h"
static
ma_mixer
g_mixer
;
static
ma_resource_manager_data_source
g_dataSources
[
16
];
static
ma_uint32
g_dataSourceCount
;
void
data_callback
(
ma_device
*
pDevice
,
void
*
pOutput
,
const
void
*
pInput
,
ma_uint32
frameCount
)
/*
TODO: Consider putting these public functions in miniaudio.h. Will depend on ma_mix_pcm_frames_f32()
being merged into miniaudio.h (it's currently in miniaudio_engine.h).
*/
static
ma_result
ma_data_source_read_pcm_frames_f32_ex
(
ma_data_source
*
pDataSource
,
float
*
pFramesOut
,
ma_uint64
frameCount
,
ma_uint64
*
pFramesRead
,
ma_bool32
loop
,
ma_format
dataSourceFormat
,
ma_uint32
dataSourceChannels
)
{
/* In this example we're just going to play our data sources layered on top of each other. */
ma_uint32
bpf
=
ma_get_bytes_per_frame
(
pDevice
->
playback
.
format
,
pDevice
->
playback
.
channels
);
ma_uint32
framesProcessed
=
0
;
while
(
framesProcessed
<
frameCount
)
{
ma_uint64
frameCountIn
;
ma_uint64
frameCountOut
=
(
frameCount
-
framesProcessed
);
ma_mixer_begin
(
&
g_mixer
,
NULL
,
&
frameCountOut
,
&
frameCountIn
);
{
size_t
iDataSource
;
for
(
iDataSource
=
0
;
iDataSource
<
g_dataSourceCount
;
iDataSource
+=
1
)
{
ma_mixer_mix_data_source
(
&
g_mixer
,
&
g_dataSources
[
iDataSource
],
0
,
frameCountIn
,
NULL
,
1
,
NULL
,
MA_TRUE
);
/*
This function is intended to be used when the format and channel count of the data source is
known beforehand. The idea is to avoid overhead due to redundant calls to ma_data_source_get_data_format().
*/
MA_ASSERT
(
pDataSource
!=
NULL
);
if
(
dataSourceFormat
==
ma_format_f32
)
{
/* Fast path. No conversion necessary. */
return
ma_data_source_read_pcm_frames
(
pDataSource
,
pFramesOut
,
frameCount
,
pFramesRead
,
loop
);
}
else
{
/* Slow path. Conversion necessary. */
ma_result
result
;
ma_uint64
totalFramesRead
;
ma_uint8
temp
[
MA_DATA_CONVERTER_STACK_BUFFER_SIZE
];
ma_uint64
tempCapInFrames
=
sizeof
(
temp
)
/
ma_get_bytes_per_frame
(
dataSourceFormat
,
dataSourceChannels
);
totalFramesRead
=
0
;
while
(
totalFramesRead
<
frameCount
)
{
ma_uint64
framesJustRead
;
ma_uint64
framesToRead
=
frameCount
-
totalFramesRead
;
if
(
framesToRead
>
tempCapInFrames
)
{
framesToRead
=
tempCapInFrames
;
}
result
=
ma_data_source_read_pcm_frames
(
pDataSource
,
pFramesOut
,
framesToRead
,
&
framesJustRead
,
loop
);
ma_convert_pcm_frames_format
(
ma_offset_pcm_frames_ptr_f32
(
pFramesOut
,
totalFramesRead
,
dataSourceChannels
),
ma_format_f32
,
temp
,
dataSourceFormat
,
framesJustRead
,
dataSourceChannels
,
ma_dither_mode_none
);
totalFramesRead
+=
framesJustRead
;
if
(
result
!=
MA_SUCCESS
)
{
break
;
}
}
ma_mixer_end
(
&
g_mixer
,
NULL
,
ma_offset_ptr
(
pOutput
,
framesProcessed
*
bpf
),
0
);
framesProcessed
+=
(
ma_uint32
)
frameCountOut
;
/* Safe cast. */
return
MA_SUCCESS
;
}
}
MA_API
ma_result
ma_data_source_read_pcm_frames_f32
(
ma_data_source
*
pDataSource
,
float
*
pFramesOut
,
ma_uint64
frameCount
,
ma_uint64
*
pFramesRead
,
ma_bool32
loop
)
{
ma_result
result
;
ma_format
format
;
ma_uint32
channels
;
result
=
ma_data_source_get_data_format
(
pDataSource
,
&
format
,
&
channels
,
NULL
);
if
(
result
!=
MA_SUCCESS
)
{
return
result
;
/* Failed to retrieve the data format of the data source. */
}
return
ma_data_source_read_pcm_frames_f32_ex
(
pDataSource
,
pFramesOut
,
frameCount
,
pFramesRead
,
loop
,
format
,
channels
);
}
MA_API
ma_result
ma_data_source_read_pcm_frames_and_mix_f32
(
ma_data_source
*
pDataSource
,
float
*
pFramesOut
,
ma_uint64
frameCount
,
ma_uint64
*
pFramesRead
,
ma_bool32
loop
,
float
volume
)
{
ma_result
result
;
ma_format
format
;
ma_uint32
channels
;
ma_uint64
totalFramesRead
;
if
(
pFramesRead
!=
NULL
)
{
*
pFramesRead
=
0
;
}
if
(
pDataSource
==
NULL
)
{
return
MA_INVALID_ARGS
;
}
result
=
ma_data_source_get_data_format
(
pDataSource
,
&
format
,
&
channels
,
NULL
);
if
(
result
!=
MA_SUCCESS
)
{
return
result
;
/* Failed to retrieve the data format of the data source. */
}
totalFramesRead
=
0
;
while
(
totalFramesRead
<
frameCount
)
{
float
temp
[
MA_DATA_CONVERTER_STACK_BUFFER_SIZE
/
sizeof
(
float
)];
ma_uint64
tempCapInFrames
=
ma_countof
(
temp
)
/
channels
;
ma_uint64
framesJustRead
;
ma_uint64
framesToRead
=
frameCount
-
totalFramesRead
;
if
(
framesToRead
>
tempCapInFrames
)
{
framesToRead
=
tempCapInFrames
;
}
result
=
ma_data_source_read_pcm_frames_f32_ex
(
pDataSource
,
temp
,
framesToRead
,
&
framesJustRead
,
loop
,
format
,
channels
);
ma_mix_pcm_frames_f32
(
ma_offset_pcm_frames_ptr
(
pFramesOut
,
totalFramesRead
,
ma_format_f32
,
channels
),
temp
,
framesJustRead
,
channels
,
volume
);
totalFramesRead
+=
framesJustRead
;
if
(
result
!=
MA_SUCCESS
)
{
break
;
}
}
if
(
pFramesRead
!=
NULL
)
{
*
pFramesRead
=
totalFramesRead
;
}
return
MA_SUCCESS
;
}
void
data_callback
(
ma_device
*
pDevice
,
void
*
pOutput
,
const
void
*
pInput
,
ma_uint32
frameCount
)
{
/*
In this example we're just going to play our data sources layered on top of each other. This
assumes the device's format is f32 and that the buffer is not pre-silenced.
*/
ma_uint32
iDataSource
;
MA_ASSERT
(
pDevice
->
playback
.
format
==
ma_format_f32
);
/*
If the device was configured with noPreSilencedOutputBuffer then you would need to silence the
buffer here, or make sure the first data source to be mixed is copied rather than mixed.
*/
/*ma_silence_pcm_frames(pOutput, frameCount, ma_format_f32, pDevice->playback.channels);*/
/* For each sound, mix as much data as we can. */
for
(
iDataSource
=
0
;
iDataSource
<
g_dataSourceCount
;
iDataSource
+=
1
)
{
ma_data_source_read_pcm_frames_and_mix_f32
(
&
g_dataSources
[
iDataSource
],
(
float
*
)
pOutput
,
frameCount
,
NULL
,
MA_TRUE
,
/* volume = */
1
);
}
(
void
)
pInput
;
...
...
@@ -108,13 +213,13 @@ int main(int argc, char** argv)
ma_device
device
;
ma_resource_manager_config
resourceManagerConfig
;
ma_resource_manager
resourceManager
;
ma_mixer_config
mixerConfig
;
ma_thread
jobThread
;
int
iFile
;
deviceConfig
=
ma_device_config_init
(
ma_device_type_playback
);
deviceConfig
.
dataCallback
=
data_callback
;
deviceConfig
.
pUserData
=
NULL
;
deviceConfig
.
playback
.
format
=
ma_format_f32
;
deviceConfig
.
dataCallback
=
data_callback
;
deviceConfig
.
pUserData
=
NULL
;
result
=
ma_device_init
(
NULL
,
&
deviceConfig
,
&
device
);
if
(
result
!=
MA_SUCCESS
)
{
...
...
@@ -122,18 +227,6 @@ int main(int argc, char** argv)
return
-
1
;
}
/*
Before starting the device we'll need to initialize the mixer. If we don't do this first, the data callback will be
fired and will try to use the mixer without it being initialized.
*/
mixerConfig
=
ma_mixer_config_init
(
device
.
playback
.
format
,
device
.
playback
.
channels
,
1024
,
NULL
,
NULL
);
result
=
ma_mixer_init
(
&
mixerConfig
,
&
g_mixer
);
if
(
result
!=
MA_SUCCESS
)
{
ma_device_uninit
(
&
device
);
printf
(
"Failed to initialize mixer."
);
return
-
1
;
}
/* We can start the device before loading any sounds. We'll just end up outputting silence. */
result
=
ma_device_start
(
&
device
);
...
...
@@ -219,11 +312,5 @@ int main(int argc, char** argv)
/* Uninitialize the resource manager after each data source. */
ma_resource_manager_uninit
(
&
resourceManager
);
/*
We're uninitializing the mixer last, but it doesn't matter when it's done, so long as it's after the device has
been stopped/uninitialized.
*/
ma_mixer_uninit
(
&
g_mixer
);
return
0
;
}
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