1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-31 22:23:54 +01:00

The fabled realtime effects...

I've made it where you can enable and disable via experimentals:

EXPERIMENTAL_REALTIME_EFFECTS
EXPERIMENTAL_EFFECTS_RACK

You will notice that, as of now, the only effects currently set up for
realtime are VSTs.  Now that this is in, I will start converting the
rest.

As I start to convert the effects, the astute of you may notice that
they no longer directly access tracks or any "internal" Audacity
objects.  This isolates the effects from changes in Audacity and makes
it much easier to add new ones.

Anyway, all 3 platforms can now display VST effects in graphical mode.
Yes, that means Linux too.  There are quite a few VSTs for Linux if
you search for them.

The so-called "rack" definitely needs some discussion, work, and attention
from someone much better at graphics than me.  I'm not really sure it should
stay in as-is.  I'd originally planned for it to be simply a utility window
where you can store your (preconfigured) favorite effects.  It should probably
revert back to that idea.

You may notice that this DOES include the API work I did.  The realtime effects
were too tied to it and I didn't want to redo the whole thing.  As I mentioned
elsewhere, the API stuff may or may not be very future proof.

So, let the critter complaints commence.  I absolute KNOW there will be some.
(I know I'll be hearing from the Linux peeps pretty darn quickly.  ;-))
This commit is contained in:
lllucius
2014-10-26 03:24:10 +00:00
parent 789824617c
commit 1eeb4d979a
85 changed files with 14177 additions and 7350 deletions

View File

@@ -1,313 +0,0 @@
/**********************************************************************
Audacity: A Digital Audio Editor
LoadModules.cpp
Dominic Mazzoni
James Crook
*******************************************************************//*!
\file LoadModules.cpp
\brief Based on LoadLadspa, this code loads pluggable Audacity
extension modules. It also has the code to (a) invoke a script
server and (b) invoke a function returning a replacement window,
i.e. an alternative to the usual interface, for Audacity.
*//*******************************************************************/
#include <wx/dynlib.h>
#include <wx/list.h>
#include <wx/log.h>
#include <wx/msgdlg.h>
#include <wx/string.h>
#include <wx/filename.h>
#include "Audacity.h"
#include "AudacityApp.h"
#include "Internat.h"
#include "commands/ScriptCommandRelay.h"
#include <NonGuiThread.h> // header from libwidgetextra
#ifdef EXPERIMENTAL_MODULE_PREFS
#include "Prefs.h"
#include "./prefs/ModulePrefs.h"
#endif
#include "LoadModules.h"
#include "widgets/MultiDialog.h"
#define initFnName "ExtensionModuleInit"
#define versionFnName "GetVersionString"
#define scriptFnName "RegScriptServerFunc"
#define mainPanelFnName "MainPanelFunc"
typedef wxWindow * pwxWindow;
typedef int (*tModuleInit)(int);
//typedef wxString (*tVersionFn)();
typedef wxChar * (*tVersionFn)();
typedef pwxWindow (*tPanelFn)(int);
// This variable will hold the address of a subroutine in
// a DLL that can hijack the normal panel.
static tPanelFn pPanelHijack=NULL;
// Next two commented out lines are handy when investigating
// strange DLL behaviour. Instead of dynamic linking,
// link the library which has the replacement panel statically.
// Give the address of the routine here.
// This is a great help in identifying missing
// symbols which otherwise cause a dll to unload after loading
// without an explanation as to why!
//extern wxWindow * MainPanelFunc( int i );
//tPanelFn pPanelHijack=&MainPanelFunc;
/// IF pPanelHijack has been found in a module DLL
/// THEN when this function is called we'll go and
/// create that window instead of the normal one.
wxWindow * MakeHijackPanel()
{
if( pPanelHijack == NULL )
return NULL;
return pPanelHijack(0);
}
// This variable will hold the address of a subroutine in a DLL that
// starts a thread and reads script commands.
static tpRegScriptServerFunc scriptFn;
Module::Module(const wxString & name)
{
mName = name;
mLib = new wxDynamicLibrary();
mDispatch = NULL;
}
Module::~Module()
{
delete mLib;
}
bool Module::Load()
{
if (mLib->IsLoaded()) {
if (mDispatch) {
return true;
}
return false;
}
if (!mLib->Load(mName, wxDL_LAZY)) {
return false;
}
// Check version string matches. (For now, they must match exactly)
tVersionFn versionFn = (tVersionFn)(mLib->GetSymbol(wxT(versionFnName)));
if (versionFn == NULL){
wxString ShortName = wxFileName( mName ).GetName();
wxMessageBox(wxString::Format(_("The module %s does not provide a version string.\nIt will not be loaded."), ShortName.c_str()), _("Module Unsuitable"));
wxLogMessage(wxString::Format(_("The module %s does not provide a version string. It will not be loaded."), mName.c_str()));
mLib->Unload();
return false;
}
wxString moduleVersion = versionFn();
if( !moduleVersion.IsSameAs(AUDACITY_VERSION_STRING)) {
wxString ShortName = wxFileName( mName ).GetName();
wxMessageBox(wxString::Format(_("The module %s is matched with Audacity version %s.\n\nIt will not be loaded."), ShortName.c_str(), moduleVersion.c_str()), _("Module Unsuitable"));
wxLogMessage(wxString::Format(_("The module %s is matched with Audacity version %s. It will not be loaded."), mName.c_str(), moduleVersion.c_str()));
mLib->Unload();
return false;
}
mDispatch = (fnModuleDispatch) mLib->GetSymbol(wxT(ModuleDispatchName));
if (!mDispatch) {
// Module does not provide a dispatch function...
// That can be OK, as long as we never try to call it.
return true;
}
// However if we do have it and it does not work,
// then the module is bad.
bool res = ((mDispatch(ModuleInitialize))!=0);
if (res) {
return true;
}
mDispatch = NULL;
return false;
}
void Module::Unload()
{
if (mLib->IsLoaded()) {
mDispatch(ModuleTerminate);
}
mLib->Unload();
}
int Module::Dispatch(ModuleDispatchTypes type)
{
if (mLib->IsLoaded())
if( mDispatch != NULL )
return mDispatch(type);
return 0;
}
void * Module::GetSymbol(wxString name)
{
return mLib->GetSymbol(name);
}
//
// Module Manager
//
ModuleManager *ModuleManager::mInstance;
bool ModuleManager::OnInit()
{
mInstance = this;
return true;
}
void ModuleManager::OnExit()
{
size_t cnt = mModules.GetCount();
for (size_t ndx = 0; ndx < cnt; ndx++) {
delete (Module *) mModules[ndx];
}
mModules.Clear();
}
void ModuleManager::Initialize(CommandHandler &cmdHandler)
{
wxArrayString audacityPathList = wxGetApp().audacityPathList;
wxArrayString pathList;
wxArrayString files;
wxString pathVar;
size_t i;
// Code from LoadLadspa that might be useful in load modules.
pathVar = wxGetenv(wxT("AUDACITY_MODULES_PATH"));
if (pathVar != wxT(""))
wxGetApp().AddMultiPathsToPathList(pathVar, pathList);
for (i = 0; i < audacityPathList.GetCount(); i++) {
wxString prefix = audacityPathList[i] + wxFILE_SEP_PATH;
wxGetApp().AddUniquePathToPathList(prefix + wxT("modules"),
pathList);
}
#if defined(__WXMSW__)
wxGetApp().FindFilesInPathList(wxT("*.dll"), pathList, files);
#else
wxGetApp().FindFilesInPathList(wxT("*.so"), pathList, files);
#endif
for (i = 0; i < files.GetCount(); i++) {
// As a courtesy to some modules that might be bridges to
// open other modules, we set the current working
// directory to be the module's directory.
wxString saveOldCWD = ::wxGetCwd();
wxString prefix = ::wxPathOnly(files[i]);
::wxSetWorkingDirectory(prefix);
#ifdef EXPERIMENTAL_MODULE_PREFS
int iModuleStatus = ModulePrefs::GetModuleStatus( files[i] );
if( iModuleStatus == kModuleDisabled )
continue;
if( iModuleStatus == kModuleFailed )
continue;
// New module? You have to go and explicitly enable it.
if( iModuleStatus == kModuleNew ){
// To ensure it is noted in config file and so
// appears on modules page.
ModulePrefs::SetModuleStatus( files[i], kModuleNew);
continue;
}
if( iModuleStatus == kModuleAsk )
#endif
// JKC: I don't like prompting for the plug-ins individually
// I think it would be better to show the module prefs page,
// and let the user decide for each one.
{
wxString ShortName = wxFileName( files[i] ).GetName();
wxString msg;
msg.Printf(_("Module \"%s\" found."), ShortName.c_str());
msg += _("\n\nOnly use modules from trusted sources");
const wxChar *buttons[] = {_("Yes"), _("No"), NULL}; // could add a button here for 'yes and remember that', and put it into the cfg file. Needs more thought.
int action;
action = ShowMultiDialog(msg, _("Audacity Module Loader"), buttons, _("Try and load this module?"), false);
#ifdef EXPERIMENTAL_MODULE_PREFS
// If we're not prompting always, accept the answer permanantly
if( iModuleStatus == kModuleNew ){
iModuleStatus = (action==1)?kModuleDisabled : kModuleEnabled;
ModulePrefs::SetModuleStatus( files[i], iModuleStatus );
}
#endif
if(action == 1){ // "No"
continue;
}
}
#ifdef EXPERIMENTAL_MODULE_PREFS
// Before attempting to load, we set the state to bad.
// That way, if we crash, we won't try again.
ModulePrefs::SetModuleStatus( files[i], kModuleFailed );
#endif
Module *module = new Module(files[i]);
if (module->Load()) // it will get rejected if there are version problems
{
mInstance->mModules.Add(module);
// We've loaded and initialised OK.
// So look for special case functions:
wxLogNull logNo; // Don't show wxWidgets errors if we can't do these. (Was: Fix bug 544.)
// (a) for scripting.
if( scriptFn == NULL )
scriptFn = (tpRegScriptServerFunc)(module->GetSymbol(wxT(scriptFnName)));
// (b) for hijacking the entire Audacity panel.
if( pPanelHijack==NULL )
{
pPanelHijack = (tPanelFn)(module->GetSymbol(wxT(mainPanelFnName)));
}
#ifdef EXPERIMENTAL_MODULE_PREFS
// Loaded successfully, restore the status.
ModulePrefs::SetModuleStatus( files[i], iModuleStatus);
#endif
}
else {
// No need to save status, as we already set kModuleFailed.
delete module;
}
::wxSetWorkingDirectory(saveOldCWD);
}
// After loading all the modules, we may have a registered scripting function.
if(scriptFn)
{
ScriptCommandRelay::SetCommandHandler(cmdHandler);
ScriptCommandRelay::SetRegScriptServerFunc(scriptFn);
NonGuiThread::StartChild(&ScriptCommandRelay::Run);
}
}
int ModuleManager::Dispatch(ModuleDispatchTypes type)
{
size_t cnt = mInstance->mModules.GetCount();
for (size_t ndx = 0; ndx < cnt; ndx++) {
Module *module = (Module *)mInstance->mModules[ndx];
module->Dispatch(type);
}
return 0;
}
IMPLEMENT_DYNAMIC_CLASS(ModuleManager, wxModule);