Commit 9c7431ab authored by David Reid's avatar David Reid

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.
parent 85580a4c
...@@ -747,6 +747,40 @@ typedef void ma_node; ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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,6 +2198,8 @@ static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const floa ...@@ -2131,6 +2198,8 @@ 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,
NULL, /* onGetRequiredInputFrameCount */
NULL, /* onGetMetadata */
1, /* 1 input bus. */ 1, /* 1 input bus. */
1, /* 1 output 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) ...@@ -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,6 +3786,8 @@ static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ...@@ -3620,6 +3786,8 @@ 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,
NULL, /* onGetRequiredInputFrameCount */
NULL, /* onGetMetadata */
0, /* 0 input buses. */ 0, /* 0 input buses. */
1, /* 1 output bus. */ 1, /* 1 output bus. */
0 0
...@@ -3744,6 +3912,8 @@ static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** pp ...@@ -3744,6 +3912,8 @@ 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,
NULL, /* onGetRequiredInputFrameCount */
NULL, /* onGetMetadata */
1, /* 1 input bus. */ 1, /* 1 input bus. */
2, /* 2 output buses. */ 2, /* 2 output buses. */
0 0
...@@ -8539,7 +8709,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo ...@@ -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 ...@@ -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 ...@@ -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 = ...@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment