Import existing source tree; original VCS history is no longer available. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
293 lines
6.1 KiB
C
293 lines
6.1 KiB
C
/*(LGPL)
|
|
---------------------------------------------------------------------------
|
|
a_plugin.c - Audio Engine Plugin API
|
|
---------------------------------------------------------------------------
|
|
* Copyright (C) 2002, David Olofson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "a_plugin.h"
|
|
#include "a_tools.h"
|
|
|
|
static int usecount = 0;
|
|
|
|
|
|
#define EMULBUF_FRAMES 256
|
|
|
|
static int *emulbuf = NULL;
|
|
|
|
static int init_emulbuf(void)
|
|
{
|
|
if(!emulbuf)
|
|
emulbuf = malloc(EMULBUF_FRAMES * sizeof(int) * 2);
|
|
if(!emulbuf)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int audio_plugin_state(audio_plugin_t *p, audio_fxstates_t new_state)
|
|
{
|
|
int res, s = p->current_state;
|
|
/* We don't care about SILENT and RESTING here. */
|
|
if(s > FX_STATE_RUNNING)
|
|
s = FX_STATE_RUNNING;
|
|
|
|
/* Nor do we allow forced switches to SILENT or RESTING! */
|
|
if(new_state > FX_STATE_RUNNING)
|
|
return -1;
|
|
|
|
while(new_state > s)
|
|
{
|
|
++s;
|
|
res = p->state(p, s);
|
|
if(res < 0)
|
|
return res;
|
|
|
|
p->current_state = s;
|
|
}
|
|
while(new_state < s)
|
|
{
|
|
--s;
|
|
res = p->state(p, s);
|
|
if(res < 0)
|
|
return res;
|
|
|
|
p->current_state = s;
|
|
switch(new_state)
|
|
{
|
|
case FX_STATE_CLOSED:
|
|
free(p->ctl);
|
|
p->ctl = NULL;
|
|
--usecount;
|
|
if(usecount <= 0)
|
|
{
|
|
free(emulbuf);
|
|
emulbuf = NULL;
|
|
usecount = 0;
|
|
}
|
|
break;
|
|
case FX_STATE_OPEN:
|
|
case FX_STATE_READY:
|
|
case FX_STATE_PAUSED:
|
|
case FX_STATE_RUNNING: /* Can't happen. */
|
|
case FX_STATE_SILENT: /* Can't happen. */
|
|
case FX_STATE_RESTING: /* Can't happen. */
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int default_state(struct audio_plugin_t *p, audio_fxstates_t new_state)
|
|
{
|
|
if(new_state > p->current_state)
|
|
switch(new_state)
|
|
{
|
|
case FX_STATE_CLOSED: /* Can't happen. */
|
|
case FX_STATE_OPEN:
|
|
if(!audio_plugin_alloc_ctls(p, FXC_COUNT))
|
|
return -1;
|
|
break;
|
|
case FX_STATE_READY:
|
|
case FX_STATE_PAUSED:
|
|
case FX_STATE_RUNNING:
|
|
case FX_STATE_SILENT: /* Can't happen. */
|
|
case FX_STATE_RESTING: /* Can't happen. */
|
|
break;
|
|
}
|
|
else
|
|
switch(new_state)
|
|
{
|
|
case FX_STATE_CLOSED:
|
|
case FX_STATE_OPEN:
|
|
case FX_STATE_READY:
|
|
case FX_STATE_PAUSED:
|
|
case FX_STATE_RUNNING:
|
|
case FX_STATE_SILENT:
|
|
case FX_STATE_RESTING: /* Can't happen. */
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define SPLIT_BUFFER \
|
|
unsigned s, f; \
|
|
for(s = 0; f = (unsigned)(frames > EMULBUF_FRAMES ? \
|
|
EMULBUF_FRAMES : frames), \
|
|
s < frames; s += f)
|
|
|
|
void default_control(struct audio_plugin_t *p, unsigned ctl, int arg)
|
|
{
|
|
if((ctl < p->controls) && p->ctl)
|
|
p->ctl[ctl] = arg;
|
|
}
|
|
|
|
|
|
static void process_with_process_r(struct audio_plugin_t *p,
|
|
int *buf, unsigned frames)
|
|
{
|
|
p->process_r(p, buf, buf, frames);
|
|
}
|
|
|
|
static void process_with_process_m(struct audio_plugin_t *p,
|
|
int *buf, unsigned frames)
|
|
{
|
|
SPLIT_BUFFER
|
|
{
|
|
s32clear(emulbuf + s, f);
|
|
p->process_r(p, buf + s, emulbuf + s, f);
|
|
s32copy(emulbuf + s, buf + s, f);
|
|
}
|
|
}
|
|
|
|
|
|
static void process_r_with_process(struct audio_plugin_t *p,
|
|
int *in, int *out, unsigned frames)
|
|
{
|
|
if(in)
|
|
s32copy(in, out, frames);
|
|
else
|
|
s32clear(out, frames);
|
|
p->process(p, out, frames);
|
|
}
|
|
|
|
static void process_r_with_process_m(struct audio_plugin_t *p,
|
|
int *in, int *out, unsigned frames)
|
|
{
|
|
s32clear(out, frames);
|
|
p->process_m(p, in, out, frames);
|
|
}
|
|
|
|
|
|
static void process_m_with_process(struct audio_plugin_t *p,
|
|
int *in, int *out, unsigned frames)
|
|
{
|
|
SPLIT_BUFFER
|
|
{
|
|
if(in)
|
|
s32copy(in + s, emulbuf + s, f);
|
|
else
|
|
s32clear(emulbuf + s, f);
|
|
p->process(p, emulbuf + s, f);
|
|
s32add(emulbuf + s, out + s, f);
|
|
}
|
|
}
|
|
|
|
static void process_m_with_process_r(struct audio_plugin_t *p,
|
|
int *in, int *out, unsigned frames)
|
|
{
|
|
SPLIT_BUFFER
|
|
{
|
|
if(in)
|
|
p->process_r(p, in + s, emulbuf + s, f);
|
|
else
|
|
p->process_r(p, NULL, emulbuf + s, f);
|
|
s32add(emulbuf + s, out + s, f);
|
|
}
|
|
}
|
|
|
|
|
|
int audio_plugin_init(audio_plugin_t *p)
|
|
{
|
|
int uses_emulbuf = 0;
|
|
/*
|
|
* This may change, but right now, a plugin cannot
|
|
* generate *any* useful form of output without at
|
|
* least one process*() callback, so...
|
|
*/
|
|
if(!p->process && !p->process_r && !p->process_m)
|
|
return -1; /* Must have at least one! */
|
|
|
|
if(!p->state)
|
|
p->state = default_state;
|
|
if(!p->control)
|
|
p->control = default_control;
|
|
if(!p->process)
|
|
{
|
|
if(p->process_r)
|
|
p->process = process_with_process_r;
|
|
else if(p->process_m)
|
|
{
|
|
p->process = process_with_process_m;
|
|
uses_emulbuf = 1;
|
|
}
|
|
}
|
|
if(!p->process_r)
|
|
{
|
|
if(p->process)
|
|
p->process_r = process_r_with_process;
|
|
else if(p->process_m)
|
|
p->process_r = process_r_with_process_m;
|
|
}
|
|
if(!p->process_m)
|
|
{
|
|
if(p->process)
|
|
{
|
|
p->process_m = process_m_with_process;
|
|
uses_emulbuf = 1;
|
|
}
|
|
else if(p->process_r)
|
|
{
|
|
p->process_m = process_m_with_process_r;
|
|
uses_emulbuf = 1;
|
|
}
|
|
}
|
|
if(uses_emulbuf)
|
|
return init_emulbuf();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int audio_plugin_open(audio_plugin_t *p, unsigned max_frames, int fs, int quality)
|
|
{
|
|
int res = audio_plugin_init(p);
|
|
if(res < 0)
|
|
return res;
|
|
|
|
res = audio_plugin_state(p, FX_STATE_OPEN);
|
|
if(res < 0)
|
|
return res;
|
|
|
|
p->control(p, FXC_MAX_FRAMES, (int)max_frames);
|
|
p->control(p, FXC_SAMPLERATE, fs);
|
|
p->control(p, FXC_QUALITY, quality);
|
|
++usecount;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void audio_plugin_close(audio_plugin_t *p)
|
|
{
|
|
audio_plugin_state(p, FX_STATE_CLOSED);
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Tools for plugins (NOT for hosts!)
|
|
----------------------------------------------------------*/
|
|
|
|
int *audio_plugin_alloc_ctls(audio_plugin_t *p, unsigned count)
|
|
{
|
|
p->controls = count;
|
|
p->ctl = calloc(p->controls, sizeof(int));
|
|
return p->ctl;
|
|
}
|