mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 01:04:02 +02:00
Add support for metadata for nodes.
This is useful for retrieving information about some aspect of the node. A good example is human readable names associated with the node and it's input and output buses. This is useful for user interfaces where a brief description of the node such as "Low Pass Filter" can be drawn on the screen. It's also useful for buses to be named, such as the source/carrier and excite/modulator on a vocoder effect which would also need to be visible on a UI.
This commit is contained in:
+196
-12
@@ -747,6 +747,40 @@ typedef void ma_node;
|
|||||||
#define MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES 0x00000008
|
#define MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES 0x00000008
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Metadata codes.
|
||||||
|
|
||||||
|
You can retrieve metadata about a node as a whole, or individual input and output buses. The codes
|
||||||
|
used to retrieve that data is encoded. To retrieve the name of a node, do this:
|
||||||
|
|
||||||
|
ma_node_get_metadata(&myNode, MA_NODE_METADATA_NAME, &metadata);
|
||||||
|
|
||||||
|
To retrieve the name of the first input bus, do this:
|
||||||
|
|
||||||
|
ma_node_get_metadata(&myNode, MA_NODE_METADATA_NAME | MA_NODE_METADATA_INPUT_BUS, &metadata);
|
||||||
|
|
||||||
|
The same applies for the output bus, only used use MA_NODE_METADATA_OUTPUT_BUS instead. To retrieve
|
||||||
|
the name of the second input bus, encode the index as well:
|
||||||
|
|
||||||
|
ma_node_get_metadata(&myNode, MA_NODE_METADATA_NAME | MA_NODE_METADATA_INPUT_BUS | 1, &metadata);
|
||||||
|
|
||||||
|
You can mix an match. To do the same, but for the second output bus:
|
||||||
|
|
||||||
|
ma_node_get_metadata(&myNode, MA_NODE_METADATA_NAME | MA_NODE_METADATA_OUTPUT_BUS | 1, &metadata);
|
||||||
|
|
||||||
|
If some type of metadata does not make sense or is not supported, MA_NO_DATA_AVAILABLE is returned.
|
||||||
|
*/
|
||||||
|
#define MA_NODE_METADATA_NAME 0x00001000
|
||||||
|
#define MA_NODE_METADATA_INPUT_BUS 0x00000100
|
||||||
|
#define MA_NODE_METADATA_OUTPUT_BUS 0x00000200
|
||||||
|
|
||||||
|
/* Masks for selecting sections of the metadata code. */
|
||||||
|
#define MA_NODE_METADATA_PROPERTY_MASK 0xFFFFF000
|
||||||
|
#define MA_NODE_METADATA_BUS_TYPE_MASK 0x00000F00
|
||||||
|
#define MA_NODE_METADATA_BUS_INDEX_MASK 0x000000FF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* The playback state of a node. Either started or stopped. */
|
/* The playback state of a node. Either started or stopped. */
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@@ -755,6 +789,23 @@ typedef enum
|
|||||||
} ma_node_state;
|
} ma_node_state;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ma_node_metadata_type_integer,
|
||||||
|
ma_node_metadata_type_string
|
||||||
|
} ma_node_metadata_type;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ma_node_metadata_type type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char* str; /* Some constant string. */
|
||||||
|
} value;
|
||||||
|
} ma_node_metadata;
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -770,6 +821,21 @@ typedef struct
|
|||||||
*/
|
*/
|
||||||
void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
|
void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
|
||||||
|
|
||||||
|
/*
|
||||||
|
A callback for retrieving the number of a input frames that are required to output the
|
||||||
|
specified number of output frames. You would only want to implement this when the node performs
|
||||||
|
resampling. This is optional, even for nodes that perform resampling, but it does offer a
|
||||||
|
small reduction in latency as it allows miniaudio to calculate the exact number of input frames
|
||||||
|
to read at a time instead of having to estimate.
|
||||||
|
*/
|
||||||
|
ma_uint32 (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Retrieves some metadata about a node. Returns MA_NO_DATA_AVAILABLE if the metadata code is
|
||||||
|
invalid. This is optional.
|
||||||
|
*/
|
||||||
|
ma_result (* onGetMetadata)(ma_node* pNode, ma_uint32 metadataCode, ma_node_metadata* pMetadata);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
|
The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
|
||||||
parameters of the callbacks above.
|
parameters of the callbacks above.
|
||||||
@@ -886,7 +952,10 @@ MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state stat
|
|||||||
MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd);
|
MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd);
|
||||||
MA_API ma_uint64 ma_node_get_time(const ma_node* pNode);
|
MA_API ma_uint64 ma_node_get_time(const ma_node* pNode);
|
||||||
MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);
|
MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);
|
||||||
|
MA_API ma_result ma_node_get_metadata(ma_node* pNode, ma_uint32 metadataCode, ma_node_metadata* pMetadata);
|
||||||
|
MA_API const char* ma_node_get_name(ma_node* pNode);
|
||||||
|
MA_API const char* ma_node_get_input_bus_name(ma_node* pNode, ma_uint32 inputBusIndex);
|
||||||
|
MA_API const char* ma_node_get_output_bus_name(ma_node* pNode, ma_uint32 outputBusIndex);
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -2118,8 +2187,6 @@ static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const floa
|
|||||||
(void)ppFramesOut;
|
(void)ppFramesOut;
|
||||||
(void)pFrameCountOut;
|
(void)pFrameCountOut;
|
||||||
|
|
||||||
printf("TESTING\n");
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* The data has already been mixed. We just need to move it to the output buffer. */
|
/* The data has already been mixed. We just need to move it to the output buffer. */
|
||||||
if (ppFramesIn != NULL) {
|
if (ppFramesIn != NULL) {
|
||||||
@@ -2131,8 +2198,10 @@ static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const floa
|
|||||||
static ma_node_vtable g_node_graph_endpoint_vtable =
|
static ma_node_vtable g_node_graph_endpoint_vtable =
|
||||||
{
|
{
|
||||||
ma_node_graph_endpoint_process_pcm_frames,
|
ma_node_graph_endpoint_process_pcm_frames,
|
||||||
1, /* 1 input bus. */
|
NULL, /* onGetRequiredInputFrameCount */
|
||||||
1, /* 1 output bus. */
|
NULL, /* onGetMetadata */
|
||||||
|
1, /* 1 input bus. */
|
||||||
|
1, /* 1 output bus. */
|
||||||
MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */
|
MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3244,6 +3313,103 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime)
|
|||||||
return MA_SUCCESS;
|
return MA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_node_get_metadata(ma_node* pNode, ma_uint32 metadataCode, ma_node_metadata* pMetadata)
|
||||||
|
{
|
||||||
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
||||||
|
ma_uint32 propertyCode;
|
||||||
|
ma_uint32 busType;
|
||||||
|
ma_uint32 busIndex;
|
||||||
|
|
||||||
|
if (pMetadata == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(pMetadata);
|
||||||
|
|
||||||
|
if (pNode == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
propertyCode = (metadataCode & MA_NODE_METADATA_PROPERTY_MASK);
|
||||||
|
busType = (metadataCode & MA_NODE_METADATA_BUS_TYPE_MASK);
|
||||||
|
busIndex = (metadataCode & MA_NODE_METADATA_BUS_INDEX_MASK);
|
||||||
|
|
||||||
|
if (busType == MA_NODE_METADATA_INPUT_BUS && busIndex >= ma_node_get_input_bus_count(pNode)) {
|
||||||
|
return MA_INVALID_ARGS; /* Invalid input bus index. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (busType == MA_NODE_METADATA_OUTPUT_BUS && busIndex >= ma_node_get_output_bus_count(pNode)) {
|
||||||
|
return MA_INVALID_ARGS; /* Invalid output bus index. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pNodeBase->vtable->onGetMetadata) {
|
||||||
|
return pNodeBase->vtable->onGetMetadata(pNode, metadataCode, pMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Getting here means we need to fall back to defaults. */
|
||||||
|
if (propertyCode == MA_NODE_METADATA_NAME) {
|
||||||
|
pMetadata->type = ma_node_metadata_type_string;
|
||||||
|
|
||||||
|
if (busType == MA_NODE_METADATA_INPUT_BUS) {
|
||||||
|
switch (busIndex) {
|
||||||
|
case 0: pMetadata->value.str = "Input Bus 0"; break;
|
||||||
|
case 1: pMetadata->value.str = "Input Bus 1"; break;
|
||||||
|
}
|
||||||
|
} else if (busType == MA_NODE_METADATA_OUTPUT_BUS) {
|
||||||
|
switch (busIndex) {
|
||||||
|
case 0: pMetadata->value.str = "Output Bus 0"; break;
|
||||||
|
case 1: pMetadata->value.str = "Output Bus 1"; break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pMetadata->value.str = "Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Getting here means we don't know how to handle defaults. */
|
||||||
|
return MA_NO_DATA_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API const char* ma_node_get_name(ma_node* pNode)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_node_metadata metadata;
|
||||||
|
|
||||||
|
result = ma_node_get_metadata(pNode, MA_NODE_METADATA_NAME, &metadata);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return "Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.value.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API const char* ma_node_get_input_bus_name(ma_node* pNode, ma_uint32 inputBusIndex)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_node_metadata metadata;
|
||||||
|
|
||||||
|
result = ma_node_get_metadata(pNode, MA_NODE_METADATA_NAME | MA_NODE_METADATA_INPUT_BUS | inputBusIndex, &metadata);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return "Input Bus";
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.value.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API const char* ma_node_get_output_bus_name(ma_node* pNode, ma_uint32 outputBusIndex)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_node_metadata metadata;
|
||||||
|
|
||||||
|
result = ma_node_get_metadata(pNode, MA_NODE_METADATA_NAME | MA_NODE_METADATA_OUTPUT_BUS | outputBusIndex, &metadata);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return "Output Bus";
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.value.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
||||||
@@ -3620,8 +3786,10 @@ static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float**
|
|||||||
static ma_node_vtable g_ma_data_source_node_vtable =
|
static ma_node_vtable g_ma_data_source_node_vtable =
|
||||||
{
|
{
|
||||||
ma_data_source_node_process_pcm_frames,
|
ma_data_source_node_process_pcm_frames,
|
||||||
0, /* 0 input buses. */
|
NULL, /* onGetRequiredInputFrameCount */
|
||||||
1, /* 1 output bus. */
|
NULL, /* onGetMetadata */
|
||||||
|
0, /* 0 input buses. */
|
||||||
|
1, /* 1 output bus. */
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3744,8 +3912,10 @@ static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** pp
|
|||||||
static ma_node_vtable g_ma_splitter_node_vtable =
|
static ma_node_vtable g_ma_splitter_node_vtable =
|
||||||
{
|
{
|
||||||
ma_splitter_node_process_pcm_frames,
|
ma_splitter_node_process_pcm_frames,
|
||||||
1, /* 1 input bus. */
|
NULL, /* onGetRequiredInputFrameCount */
|
||||||
2, /* 2 output buses. */
|
NULL, /* onGetMetadata */
|
||||||
|
1, /* 1 input bus. */
|
||||||
|
2, /* 2 output buses. */
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -8539,7 +8709,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo
|
|||||||
*pFrameCountOut = totalFramesProcessedOut;
|
*pFrameCountOut = totalFramesProcessedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
||||||
{
|
{
|
||||||
/* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
|
/* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
|
||||||
ma_result result = MA_SUCCESS;
|
ma_result result = MA_SUCCESS;
|
||||||
@@ -8649,7 +8819,7 @@ void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFr
|
|||||||
ma_engine_node_update_pitch_if_required(&pSound->engineNode);
|
ma_engine_node_update_pitch_if_required(&pSound->engineNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
||||||
{
|
{
|
||||||
/* For groups, the input data has already been read and we just need to apply the effect. */
|
/* For groups, the input data has already been read and we just need to apply the effect. */
|
||||||
ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
|
ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
|
||||||
@@ -8661,10 +8831,22 @@ void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFr
|
|||||||
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
|
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ma_uint32 ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount)
|
||||||
|
{
|
||||||
|
ma_uint64 result = ma_engine_node_get_required_input_frame_count(pNode, outputFrameCount);
|
||||||
|
if (result > 0xFFFFFFFF) {
|
||||||
|
result = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ma_uint32)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ma_node_vtable g_ma_engine_node_vtable__sound =
|
static ma_node_vtable g_ma_engine_node_vtable__sound =
|
||||||
{
|
{
|
||||||
ma_engine_node_process_pcm_frames__sound,
|
ma_engine_node_process_pcm_frames__sound,
|
||||||
|
NULL, /* onGetRequiredInputFrameCount */
|
||||||
|
NULL, /* onGetMetadata */
|
||||||
0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
|
0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
|
||||||
1, /* Sounds have one output bus. */
|
1, /* Sounds have one output bus. */
|
||||||
0 /* Default flags. */
|
0 /* Default flags. */
|
||||||
@@ -8673,9 +8855,11 @@ static ma_node_vtable g_ma_engine_node_vtable__sound =
|
|||||||
static ma_node_vtable g_ma_engine_node_vtable__group =
|
static ma_node_vtable g_ma_engine_node_vtable__group =
|
||||||
{
|
{
|
||||||
ma_engine_node_process_pcm_frames__group,
|
ma_engine_node_process_pcm_frames__group,
|
||||||
|
ma_engine_node_get_required_input_frame_count__group,
|
||||||
|
NULL, /* onGetMetadata */
|
||||||
1, /* Groups have one input bus. */
|
1, /* Groups have one input bus. */
|
||||||
1, /* Groups have one output bus. */
|
1, /* Groups have one output bus. */
|
||||||
0 /* Default flags. */
|
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
|
||||||
};
|
};
|
||||||
|
|
||||||
MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
|
MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
|
||||||
|
|||||||
Reference in New Issue
Block a user