/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Purpose: ladspa support. * * Author: Stas Sergeev * * Linux Audio Developers Simple Plugin API... * What did they mean? Simple API for plugins, or horrible API * for simple plugins? I guess the later. * Well, maybe for Linux Audio Developers this API is simple, but * for dosemu developer its just a royal pita. */ #include #include #include #include #include #include "utils.h" #include "emu.h" #include "dosemu_debug.h" #include "init.h" #include "sound/sound.h" #define ladspa_name "ladspa" #define ladspa_longname "Sound Effect Processor: ladspa" struct lp { const char *plugin; const char *name; const enum EfpType type; }; static struct lp plugins[] = { { "filter.so", "hpf", EFP_HPF }, { NULL, NULL, EFP_NONE } }; #define MAX_LADSPAS 5 struct lads { struct lp *link; void *dl_handle; const LADSPA_Descriptor *descriptor; int in; int out; int ctrl; }; static struct lads ladspas[MAX_LADSPAS]; static int num_ladspas; #define MAX_HANDLES 5 #define MAX_CHANS SNDBUF_CHANS struct la_h { struct lads *lad; int srate; LADSPA_Data control; LADSPA_Handle handle[MAX_CHANS]; int num_chans; }; static struct la_h handles[MAX_HANDLES]; static int num_handles; static struct lads *find_lad(struct lp *p) { struct lp *plu; int i; for (i = 0; i < MAX_LADSPAS && (plu = ladspas[i].link); i++) { if (plu == p) return &ladspas[i]; } return NULL; } static int ladspa_setup(int srate, int channels, float control, void *arg) { struct la_h *lah = &handles[num_handles]; struct lads *lad = find_lad(arg); int i; if (!lad) { error("ladspa: setup failed\n"); return -1; } assert(num_handles < MAX_HANDLES); lah->lad = lad; lah->control = control; lah->srate = srate; lah->num_chans = channels; for (i = 0; i < channels; i++) { lah->handle[i] = lad->descriptor->instantiate(lad->descriptor, srate); if (!lah->handle[i]) { error("ladspa: failed to instantiate %s:%i\n", lad->link->name, i); return -1; } lad->descriptor->connect_port(lah->handle[i], lad->ctrl, &lah->control); } return num_handles++; } static int ladspa_open(void *arg) { struct lp *plu = arg; void *dl_handle; LADSPA_Descriptor_Function pfDescriptorFunction; const LADSPA_Descriptor *psDescriptor; int i; struct lads *lad = &ladspas[num_ladspas]; assert(plu->plugin && plu->name); dl_handle = loadLADSPAPluginLibrary(plu->plugin); if (!dl_handle) { error("ladspa: failed to load %s\n", plu->plugin); return 0; } pfDescriptorFunction = (LADSPA_Descriptor_Function) dlsym(dl_handle, "ladspa_descriptor"); if (!pfDescriptorFunction) { error("ladspa: %s: %s\n", plu->plugin, dlerror()); goto out_err; } for (i = 0;; i++) { psDescriptor = pfDescriptorFunction(i); if (!psDescriptor) break; if (strcmp(plu->name, psDescriptor->Label) == 0) break; } if (!psDescriptor) { error("ladspa: failed to find %s\n", plu->name); goto out_err; } assert(num_ladspas < MAX_LADSPAS); for (i = 0; i < psDescriptor->PortCount; i++) { if (LADSPA_IS_PORT_INPUT(psDescriptor->PortDescriptors[i]) && LADSPA_IS_PORT_AUDIO(psDescriptor->PortDescriptors[i])) lad->in = i; else if (LADSPA_IS_PORT_OUTPUT(psDescriptor->PortDescriptors[i]) && LADSPA_IS_PORT_AUDIO(psDescriptor->PortDescriptors[i])) lad->out = i; else if (LADSPA_IS_PORT_CONTROL(psDescriptor->PortDescriptors[i])) lad->ctrl = i; } lad->descriptor = psDescriptor; lad->dl_handle = dl_handle; lad->link = arg; num_ladspas++; return 1; out_err: unloadLADSPAPluginLibrary(dl_handle); return 0; } static void ladspa_close(void *arg) { struct lads *lad = find_lad(arg); unloadLADSPAPluginLibrary(lad->dl_handle); } static void ladspa_start(int h) { struct la_h *lah = &handles[h]; struct lads *lad = lah->lad; int i; if (lad->descriptor->activate) { for (i = 0; i < lah->num_chans; i++) lad->descriptor->activate(lah->handle[i]); } } static void ladspa_stop(int h) { struct la_h *lah = &handles[h]; struct lads *lad = lah->lad; int i; if (lad->descriptor->deactivate) { for (i = 0; i < lah->num_chans; i++) lad->descriptor->deactivate(lah->handle[i]); } } static int ladspa_cfg(void *arg) { struct lp *plu = arg; switch (plu->type) { case EFP_NONE: return 0; case EFP_HPF: return (config.pcm_hpf ? PCM_CF_ENABLED : 0); } return 0; } #define UC2F(v) ((*(unsigned char *)(v) - 128) / 128.0) #define SC2F(v) ((*(signed char *)(v)) / 128.0) #define US2F(v) ((*(unsigned short *)(v) - 32768) / 32768.0) #define SS2F(v) ((*(signed short *)(v)) / 32768.0) #define F2UC(v) ((unsigned char)(lrintf((v) * 127) + 128)) #define F2SC(v) ((signed char)(lrintf((v) * 127))) #define F2US(v) ((unsigned short)(lrintf((v) * 32767) + 32768)) #define F2SS(v) ((signed short)(lrintf((v) * 32767))) static float sample_to_float(void *data, int format) { switch (format) { case PCM_FORMAT_U8: return UC2F(data); case PCM_FORMAT_S8: return SC2F(data); case PCM_FORMAT_U16_LE: return US2F(data); case PCM_FORMAT_S16_LE: return SS2F(data); default: error("PCM: format %i is not supported\n", format); return 0; } } static void float_to_sample(float sample, sndbuf_t *buf, int format) { switch (format) { case PCM_FORMAT_U8: *buf = F2UC(sample); break; case PCM_FORMAT_S8: *buf = F2SC(sample); break; case PCM_FORMAT_U16_LE: *buf = F2US(sample); break; case PCM_FORMAT_S16_LE: *buf = F2SS(sample); break; default: error("PCM: format1 %i is not supported\n", format); } } static int ladspa_process(int h, sndbuf_t buf[][SNDBUF_CHANS], int nframes, int channels, int format, int srate) { struct la_h *lah = &handles[h]; struct lads *lad = lah->lad; LADSPA_Data tmp_in[nframes], tmp_out[nframes]; int i, j; if (srate != lah->srate) { error("ladspa: wrong sampling rate\n"); return 0; } for (i = 0; i < channels; i++) { lad->descriptor->connect_port(lah->handle[i], lad->in, tmp_in); lad->descriptor->connect_port(lah->handle[i], lad->out, tmp_out); for (j = 0; j < nframes; j++) tmp_in[j] = sample_to_float(&buf[j][i], format); lad->descriptor->run(lah->handle[i], nframes); for (j = 0; j < nframes; j++) float_to_sample(tmp_out[j], &buf[j][i], format); } return nframes; } static struct pcm_efp ladspa #ifdef __cplusplus { ladspa_name, ladspa_longname, ladspa_cfg, ladspa_open, ladspa_close, ladspa_start, ladspa_stop, ladspa_setup, ladspa_process, PCM_F_PASSTHRU | PCM_F_EXPLICIT, }; #else = { .name = ladspa_name, .longname = ladspa_longname, .get_cfg = ladspa_cfg, .open = ladspa_open, .close = ladspa_close, .start = ladspa_start, .stop = ladspa_stop, .setup = ladspa_setup, .process = ladspa_process, .flags = PCM_F_PASSTHRU | PCM_F_EXPLICIT, }; #endif CONSTRUCTOR(static void ladspa_init(void)) { struct lp *p; for (p = plugins; p->plugin; p++) pcm_register_efp(&ladspa, p->type, p); }