An Example

Here's an example of working with a resource in an addon. Lets start with creating resources in your addon. One approach is to create a const structure containing your resources definitions, and then use that as a template to create a resources structure for each context, if your addon uses contexts. You would use contexts if you needed more than one instance of the same addon. Lets create a context structure first:

typedef struct my_context
{
    int32_t volume;             // volume, 0-100
    int64_t position;           // position, in microseconds
    AOResource_t *resources;
};

We'll need typing info for our volume and position resources, such as minimum, maximum, and increment values. In an AOResource_t there is pre-defined typing info in the type element flags. When the type flag for an AOResource_t is AOR_TYPE_LONG, then value is an int32_t and the resource info is a pointer to int32_t min, max, and step values:

// volume range from 0 to 100, in increments of one
static const int32_t volumerange[] = { 0, 100, 1 };

When type is AOR_TYPE_LONGLONG, the resource value is an int64_t and info is a pointer to int64_t min, max, and step values:

// position range from 0 to 86400000000 (86400 seconds, or 24 hours.)
static const int64_t posrange[] = { 0, 86400000000, 1 };

The AOResource_t structure is defined as:

typedef struct
{
    char *name;
    char *description;
    void *value;
    void *info;
    int32_t type;
} AOResource_t;

For more information about the AOResource_t structure, see AOResource_t.

Now we can define our const AOResource_t resources structure:

static const AOResource_t resources[] =
{
    { "Volume", "Current Volume", (void*)offsetof(my_context,volume),
        &volumerange, AOR_TYPE_LONG | AOR_TYPE_READABLE | AOR_TYPE_WRITABLE },
    { "Position", "Current Position", (void*)offsetof(my_context,position),
        &posrange, AOR_TYPE_LONGLONG | AOR_TYPE_READABLE | AOR_TYPE_WRITABLE },
    { 0 }
};

As you can see, the pointer to the current value of the resource is not valid; its an offset into the context. When we create a new context, we'll have to adjust this pointer accordingly. Assuming we use the AODeConstructor interface, our create function might look like:

static void *Create(const AOICtrl_t *interfaces)
{
    my_context *ctx = (my_context*)calloc( 1, sizeof(my_context) );
    int32_t n;
    AOResource_t *res;
    
    // allocate new resource structure, and copy const version
    // into it:
    ctx->res = (AOResource_t*)malloc( sizeof(resources) );
    memcpy( ctx->res, &resources, sizeof(resources) );
    
    for (res=ctx->res;res->name;res++)
    {
        char *p = (char *)ctx;
    
        // Add the address of the context to the offset, making
        // the value pointer now point to the correct location
        // in our context:
        res->value = (void*)(&p[(int32_t)res->value]);
    }
    
    // initialize our context elements, if necessary
    ctx->volume = 50;
    
    return ctx;
}

static void Destroy(void *p)
{
    my_context *ctx = (my_context*)p;
    
    free(ctx->res);
    free(ctx);
}

static AODeConstructor media_filter =
{
    Create,
    Destroy
};

If we want the outside world to be able to access our resources, we'll need to implement the AOResourceAccess interface. This is quite easy as well:

static const AOResource_t *GetResources( void *handle )
{
    my_context *ctx = (my_context*)handle;
    
    return handle->resources;
}

static int32_t SetResource( void *handle,
                            const char *res,
                            const void *data )
{
    my_context *ctx = (my_context*)handle;
    
    // first resource is volume
    if ( strcmp( res, ctx->resources[0].name ) == 0 )
    {
        ctx->volume = *((int32_t*)data);
        
        // do any other volume control stuff here
        
        // return success
        return 0;
    }
    else
    
    // second resource is position
    if ( strcmp( res, ctx->resources[1].name ) == 0 )
    {
        ctx->position = *((int64_t*)data);
        
        // do any other positioning stuff here
        
        // return success
        return 0;
    }
    
    // no matching resource, return error
    return -1;
}

static AOResourceAccess resource_access =
{
    GetResources,
    SetResource,
};

At the end of our addon, we put them all together in our interfaces list:

#ifdef VARIANT_dll
AOInterface_t interfaces[] =
#else
AOInterface_t my_addon_interfaces[] =
#endif
{
    { "Name", 1, "my_addon" },
    { "AODeConstructor", AODECONSTRUCTOR_VERSION, &media_filter },
    { "AOResourceAccess", AORESOURCEACCESS_VERSION, &resource_access },
    { 0, 0, 0 },
};

We use the #ifdef in order to build a shared (DLL) and static (library) version of our addon with the same source code. This way we can link directly with the static version if we want our application to be completely self contained, and use the DLL if not.