mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-04 14:39:08 +02:00
Round 3 of realtime changes.
This gets meter type VST effects working again by extending the The master now maintains his own internal buffers and sums (mixes) all playing tracks into those buffers. The buffers are then fed into the VST effect that is presented to the user. This allows the effect to provide feedback to the user if it support it. Such effects may display meters or clipping indicators. Several issues with treading have also been corrected (hopefully ;-)). These showed up mostly on Linux, but could have happened on the others as well. The realtime support is no longer limited to 2 channels per logical track. Once support for more channels is added, this should be ready for it. The rack dialog can now be toggled via the edit toolbar button. It doesn't stay pressed because the closing of the dialog would have to be communicated back to the toolbar. As the rack is updated with new or removed effects or active state changed, all effects in the active list were shutdown and all effects in the updated list were initialized. This now shuts down only the effects no longer in the list and initializes only new ones. The rack now uses wxBitmapButton instead of Audacity's AButton. The AButton has a timing issue that prevents it from being deleted while processing the click event. I looked into it, but gave up and switched to the wxBitmapButton. Unfortunately, there's a problem with the wxBitmapButton as well...at least on my setup here. Either the bitmaps are being scaled or antialiased. Will have to get feedback on this. I finally figured out why some VSTs didn't seem to do anything in realtime, at least in my case anyway. I've installed a lot of demo VSTs and while they work in "batch/offline" mode, some of them will not work in realtime since vendors tend to remove automation as one of the demo limitations. More changes coming shortly...
This commit is contained in:
parent
4b1679cf87
commit
cae6669275
@ -127,7 +127,7 @@ public:
|
|||||||
virtual bool RealtimeFinalize() = 0;
|
virtual bool RealtimeFinalize() = 0;
|
||||||
virtual bool RealtimeSuspend() = 0;
|
virtual bool RealtimeSuspend() = 0;
|
||||||
virtual bool RealtimeResume() = 0;
|
virtual bool RealtimeResume() = 0;
|
||||||
virtual sampleCount RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size) = 0;
|
virtual sampleCount RealtimeProcess(int group, float **inbuf, float **outbuf, sampleCount size) = 0;
|
||||||
|
|
||||||
virtual bool ShowInterface(void *parent) = 0;
|
virtual bool ShowInterface(void *parent) = 0;
|
||||||
};
|
};
|
||||||
|
@ -1362,7 +1362,7 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks,
|
|||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
if (mNumPlaybackChannels > 0)
|
if (mNumPlaybackChannels > 0)
|
||||||
{
|
{
|
||||||
EffectManager::Get().RealtimeInitialize(&mPlaybackTracks);
|
EffectManager::Get().RealtimeInitialize();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -3499,8 +3499,6 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
numSolo++;
|
numSolo++;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int logicalCnt = 0;
|
|
||||||
int chanCnt = 0;
|
|
||||||
WaveTrack **chans = (WaveTrack **) alloca(numPlaybackChannels * sizeof(WaveTrack *));
|
WaveTrack **chans = (WaveTrack **) alloca(numPlaybackChannels * sizeof(WaveTrack *));
|
||||||
float **tempBufs = (float **) alloca(numPlaybackChannels * sizeof(float *));
|
float **tempBufs = (float **) alloca(numPlaybackChannels * sizeof(float *));
|
||||||
for (int c = 0; c < numPlaybackChannels; c++)
|
for (int c = 0; c < numPlaybackChannels; c++)
|
||||||
@ -3508,6 +3506,9 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
tempBufs[c] = (float *) alloca(framesPerBuffer * sizeof(float));
|
tempBufs[c] = (float *) alloca(framesPerBuffer * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int group = 0;
|
||||||
|
int chanCnt = 0;
|
||||||
|
float rate = 0.0;
|
||||||
for (t = 0; t < numPlaybackTracks; t++)
|
for (t = 0; t < numPlaybackTracks; t++)
|
||||||
{
|
{
|
||||||
WaveTrack *vt = gAudioIO->mPlaybackTracks[t];
|
WaveTrack *vt = gAudioIO->mPlaybackTracks[t];
|
||||||
@ -3527,7 +3528,14 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
if (vt->GetMute() && !vt->GetSolo())
|
if (vt->GetMute() && !vt->GetSolo())
|
||||||
cut = true;
|
cut = true;
|
||||||
|
|
||||||
|
rate = vt->GetRate();
|
||||||
linkFlag = vt->GetLinked();
|
linkFlag = vt->GetLinked();
|
||||||
|
|
||||||
|
// If we have a mono track, clear the right channel
|
||||||
|
if (!linkFlag)
|
||||||
|
{
|
||||||
|
memset(tempBufs[1], 0, framesPerBuffer * sizeof(float));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ORIGINAL_DO_NOT_PLAY_ALL_MUTED_TRACKS_TO_END
|
#define ORIGINAL_DO_NOT_PLAY_ALL_MUTED_TRACKS_TO_END
|
||||||
@ -3574,7 +3582,7 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
len = EffectManager::Get().RealtimeProcess(logicalCnt++, tempBufs, len);
|
len = EffectManager::Get().RealtimeProcess(group++, chanCnt, rate, tempBufs, len);
|
||||||
#endif
|
#endif
|
||||||
// If our buffer is empty and the time indicator is past
|
// If our buffer is empty and the time indicator is past
|
||||||
// the end, then we've actually finished playing the entire
|
// the end, then we've actually finished playing the entire
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
#include "portaudio.h"
|
#include "portaudio.h"
|
||||||
#include "Audacity.h"
|
#include "Audacity.h"
|
||||||
#include "effects/Effect.h"
|
|
||||||
#include "Experimental.h"
|
#include "Experimental.h"
|
||||||
|
|
||||||
#ifdef USE_MIDI
|
#ifdef USE_MIDI
|
||||||
|
@ -409,7 +409,6 @@ bool ModuleManager::DiscoverProviders(wxArrayString & providers)
|
|||||||
|
|
||||||
for (int i = 0, cnt = provList.GetCount(); i < cnt; i++)
|
for (int i = 0, cnt = provList.GetCount(); i < cnt; i++)
|
||||||
{
|
{
|
||||||
wxPrintf(wxT("provider %s\n"), provList[i].c_str());
|
|
||||||
providers.push_back(provList[i]);
|
providers.push_back(provList[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,20 +136,21 @@ CheckListAx::CheckListAx( wxListCtrl * window ):
|
|||||||
|
|
||||||
CheckListAx::~CheckListAx()
|
CheckListAx::~CheckListAx()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckListAx::SetSelected( int item )
|
void CheckListAx::SetSelected( int item )
|
||||||
{
|
{
|
||||||
if (mLastId != -1) {
|
if (mLastId != -1)
|
||||||
|
{
|
||||||
NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE,
|
NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE,
|
||||||
mParent,
|
mParent,
|
||||||
wxOBJID_CLIENT,
|
wxOBJID_CLIENT,
|
||||||
mLastId );
|
mLastId );
|
||||||
mLastId = -1;
|
mLastId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item != -1)
|
if (item != -1)
|
||||||
{
|
{
|
||||||
NotifyEvent( wxACC_EVENT_OBJECT_FOCUS,
|
NotifyEvent( wxACC_EVENT_OBJECT_FOCUS,
|
||||||
mParent,
|
mParent,
|
||||||
wxOBJID_CLIENT,
|
wxOBJID_CLIENT,
|
||||||
@ -241,14 +242,14 @@ wxAccStatus CheckListAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *s
|
|||||||
// Returns the rectangle for this object (id = 0) or a child element (id > 0).
|
// Returns the rectangle for this object (id = 0) or a child element (id > 0).
|
||||||
// rect is in screen coordinates.
|
// rect is in screen coordinates.
|
||||||
wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId )
|
wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId )
|
||||||
{
|
{
|
||||||
if( elementId == wxACC_SELF )
|
if( elementId == wxACC_SELF )
|
||||||
{
|
{
|
||||||
rect = mParent->GetRect();
|
rect = mParent->GetRect();
|
||||||
rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) );
|
rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if( elementId <= mParent->GetItemCount() )
|
if( elementId <= mParent->GetItemCount() )
|
||||||
{
|
{
|
||||||
mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL );
|
mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL );
|
||||||
@ -1287,9 +1288,15 @@ PluginManager & PluginManager::Get()
|
|||||||
|
|
||||||
void PluginManager::Initialize()
|
void PluginManager::Initialize()
|
||||||
{
|
{
|
||||||
Load();
|
bool loaded = Load();
|
||||||
ModuleManager::Get().EarlyInit();
|
ModuleManager::Get().EarlyInit();
|
||||||
|
|
||||||
|
if (!loaded)
|
||||||
|
{
|
||||||
|
PluginRegistrationDialog dlg;
|
||||||
|
dlg.ShowModal();
|
||||||
|
}
|
||||||
|
|
||||||
CheckForUpdates();
|
CheckForUpdates();
|
||||||
|
|
||||||
bool doRescan;
|
bool doRescan;
|
||||||
@ -1333,12 +1340,12 @@ void PluginManager::Terminate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginManager::Load()
|
bool PluginManager::Load()
|
||||||
{
|
{
|
||||||
// IF already open THEN nothing to do.
|
// IF already open THEN nothing to do.
|
||||||
if (mConfig != NULL)
|
if (mConfig != NULL)
|
||||||
{
|
{
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the config
|
// Create the config
|
||||||
@ -1353,7 +1360,7 @@ void PluginManager::Load()
|
|||||||
{
|
{
|
||||||
// Must start over
|
// Must start over
|
||||||
mConfig->DeleteAll();
|
mConfig->DeleteAll();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all provider plugins first
|
// Load all provider plugins first
|
||||||
@ -1363,6 +1370,8 @@ void PluginManager::Load()
|
|||||||
LoadGroup(wxT("effects"), PluginTypeEffect);
|
LoadGroup(wxT("effects"), PluginTypeEffect);
|
||||||
LoadGroup(wxT("exporters"), PluginTypeExporter);
|
LoadGroup(wxT("exporters"), PluginTypeExporter);
|
||||||
LoadGroup(wxT("importers"), PluginTypeImporter);
|
LoadGroup(wxT("importers"), PluginTypeImporter);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginManager::LoadGroup(const wxChar * group, PluginType type)
|
void PluginManager::LoadGroup(const wxChar * group, PluginType type)
|
||||||
|
@ -223,7 +223,7 @@ public:
|
|||||||
void CheckForUpdates();
|
void CheckForUpdates();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Load();
|
bool Load();
|
||||||
void LoadGroup(const wxChar *group, PluginType type);
|
void LoadGroup(const wxChar *group, PluginType type);
|
||||||
void Save();
|
void Save();
|
||||||
void SaveGroup(const wxChar *group, PluginType type);
|
void SaveGroup(const wxChar *group, PluginType type);
|
||||||
|
@ -1291,25 +1291,13 @@ bool Effect::RealtimeInitialize()
|
|||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
if (mClient)
|
if (mClient)
|
||||||
{
|
{
|
||||||
mNumChannels = 0;
|
mNumGroups = -1;
|
||||||
return mClient->RealtimeInitialize();
|
return mClient->RealtimeInitialize();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool Effect::RealtimeAddProcessor(int numChannels, float sampleRate)
|
|
||||||
{
|
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
|
||||||
if (mClient)
|
|
||||||
{
|
|
||||||
mNumChannels = numChannels;
|
|
||||||
return mClient->RealtimeAddProcessor(numChannels, sampleRate);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Effect::RealtimeFinalize()
|
bool Effect::RealtimeFinalize()
|
||||||
{
|
{
|
||||||
@ -1347,53 +1335,111 @@ bool Effect::RealtimeResume()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleCount Effect::RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size)
|
sampleCount Effect::RealtimeProcess(int group,
|
||||||
|
int chans,
|
||||||
|
float rate,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples)
|
||||||
{
|
{
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
float **tin = inbuf;
|
//
|
||||||
if (mNumAudioIn > mNumChannels)
|
// The caller passes the number of channels to process and specifies
|
||||||
|
// the number of input and output buffers. There will always be the
|
||||||
|
// same number of output buffers as there are input buffers.
|
||||||
|
//
|
||||||
|
// Effects always require a certain number of input and output buffers,
|
||||||
|
// so if the number of channels we're curently processing are different
|
||||||
|
// that was the effect expects, then we use a few methods of satisfying
|
||||||
|
// the effects requirements.
|
||||||
|
float **clientIn = (float **) alloca(mNumAudioIn * sizeof(float *));
|
||||||
|
float **clientOut = (float **) alloca(mNumAudioOut * sizeof(float *));
|
||||||
|
float *dummybuf = (float *) alloca(numSamples * sizeof(float));
|
||||||
|
sampleCount len = 0;
|
||||||
|
int ichans = chans;
|
||||||
|
int ochans = chans;
|
||||||
|
int gchans = chans;
|
||||||
|
int indx = 0;
|
||||||
|
int ondx = 0;
|
||||||
|
|
||||||
|
// Call the client until we run out of input or output channels
|
||||||
|
while (ichans > 0 && ochans > 0)
|
||||||
{
|
{
|
||||||
float *dummybuf = (float *) alloca(size * sizeof(float));
|
// If we don't have enough input channels to accomodate the client's
|
||||||
tin = (float **) alloca(mNumAudioIn * sizeof(float *));
|
// requirements, then we replicate the input channels until the
|
||||||
for (int i = 0; i < mNumChannels; i++)
|
// client's needs are met.
|
||||||
|
if (ichans < mNumAudioIn)
|
||||||
{
|
{
|
||||||
tin[i] = inbuf[i];
|
for (int i = 0; i < mNumAudioIn; i++)
|
||||||
|
{
|
||||||
|
if (indx == ichans)
|
||||||
|
{
|
||||||
|
indx = 0;
|
||||||
|
}
|
||||||
|
clientIn[i] = inbuf[indx++];
|
||||||
|
}
|
||||||
|
|
||||||
|
// All input channels have been consumed
|
||||||
|
ichans = 0;
|
||||||
}
|
}
|
||||||
for (int i = mNumChannels; i < mNumAudioIn; i++)
|
// Otherwise fullfil the client's needs with as many input channels as possible.
|
||||||
|
// After calling the client with this set, we will loop back up to process more
|
||||||
|
// of the input/output channels.
|
||||||
|
else if (ichans >= mNumAudioIn)
|
||||||
{
|
{
|
||||||
tin[i] = dummybuf;
|
gchans = 0;
|
||||||
|
for (int i = 0; i < mNumAudioIn; i++, ichans--, gchans++)
|
||||||
|
{
|
||||||
|
clientIn[i] = inbuf[indx++];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
|
// If we don't have enough output channels to accomodate the client's
|
||||||
|
// requirements, then we provide all of the output channels and fulfill
|
||||||
|
// the client's needs with dummy buffers. These will just get tossed.
|
||||||
|
if (ochans < mNumAudioOut)
|
||||||
{
|
{
|
||||||
dummybuf[i] = 0.0;
|
for (int i = 0; i < mNumAudioOut; i++)
|
||||||
|
{
|
||||||
|
if (i < ochans)
|
||||||
|
{
|
||||||
|
clientOut[i] = outbuf[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clientOut[i] = dummybuf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All output channels have been consumed
|
||||||
|
ochans = 0;
|
||||||
}
|
}
|
||||||
|
// Otherwise fullfil the client's needs with as many output channels as possible.
|
||||||
|
// After calling the client with this set, we will loop back up to process more
|
||||||
|
// of the input/output channels.
|
||||||
|
else if (ochans >= mNumAudioOut)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mNumAudioOut; i++, ochans--)
|
||||||
|
{
|
||||||
|
clientOut[i] = outbuf[ondx++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current group hasn't yet been seen, then we must
|
||||||
|
// add a new processor to handle this channel (sub)group
|
||||||
|
if (group >= mNumGroups)
|
||||||
|
{
|
||||||
|
mClient->RealtimeAddProcessor(gchans, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally call the plugin to process the block
|
||||||
|
len = mClient->RealtimeProcess(group++, clientIn, clientOut, numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
float **tout = outbuf;
|
// Remember the number of channel groups we've seen
|
||||||
if (mNumAudioOut > mNumChannels)
|
mNumGroups++;
|
||||||
{
|
|
||||||
float *dummybuf = (float *) alloca(size * sizeof(float));
|
|
||||||
tout = (float **) alloca(mNumAudioOut * sizeof(float *));
|
|
||||||
for (int i = 0; i < mNumChannels; i++)
|
|
||||||
{
|
|
||||||
tout[i] = outbuf[i];
|
|
||||||
}
|
|
||||||
for (int i = mNumChannels; i < mNumAudioOut; i++)
|
|
||||||
{
|
|
||||||
tout[i] = dummybuf;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
dummybuf[i] = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally call the plugin to process the block
|
return len;
|
||||||
// LLL FIXME
|
|
||||||
// If the number of channels is greater than the number of in/out the effect
|
|
||||||
// can handle, then call RealtimeProcess for each "set" of tracks over its
|
|
||||||
// capabilities.
|
|
||||||
return mClient->RealtimeProcess(index, tin, tout, size);
|
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
@ -1540,6 +1586,26 @@ void Effect::Preview(bool dryOnly)
|
|||||||
mTracks = saveTracks;
|
mTracks = saveTracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Effect::GetAudioInCount()
|
||||||
|
{
|
||||||
|
if (mClient)
|
||||||
|
{
|
||||||
|
return mClient->GetAudioInCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Effect::GetAudioOutCount()
|
||||||
|
{
|
||||||
|
if (mClient)
|
||||||
|
{
|
||||||
|
return mClient->GetAudioInCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
EffectDialog::EffectDialog(wxWindow * parent,
|
EffectDialog::EffectDialog(wxWindow * parent,
|
||||||
const wxString & title,
|
const wxString & title,
|
||||||
int type,
|
int type,
|
||||||
|
@ -239,13 +239,20 @@ class AUDACITY_DLL_API Effect : public EffectHostInterface
|
|||||||
// important for sorting.
|
// important for sorting.
|
||||||
static wxString StripAmpersand(const wxString& str);
|
static wxString StripAmpersand(const wxString& str);
|
||||||
|
|
||||||
|
int GetAudioInCount();
|
||||||
|
int GetAudioOutCount();
|
||||||
|
|
||||||
// Realtime Effect Processing
|
// Realtime Effect Processing
|
||||||
bool RealtimeInitialize();
|
bool RealtimeInitialize();
|
||||||
bool RealtimeAddProcessor(int numChannels, float sampleRate);
|
|
||||||
bool RealtimeFinalize();
|
bool RealtimeFinalize();
|
||||||
bool RealtimeSuspend();
|
bool RealtimeSuspend();
|
||||||
bool RealtimeResume();
|
bool RealtimeResume();
|
||||||
sampleCount RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size);
|
sampleCount RealtimeProcess(int group,
|
||||||
|
int chans,
|
||||||
|
float rate,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples);
|
||||||
|
|
||||||
//
|
//
|
||||||
// protected virtual methods
|
// protected virtual methods
|
||||||
|
@ -175,13 +175,12 @@ EffectManager::EffectManager()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
mRealtimeMutex.Lock();
|
mRealtimeLock.Enter();
|
||||||
mRealtimeEffects = NULL;
|
mRealtimeEffects = NULL;
|
||||||
mRealtimeCount = 0;
|
mRealtimeCount = 0;
|
||||||
mRealtimeActive = false;
|
mRealtimeSuspended = true;
|
||||||
mRealtimeSuspended = false;
|
|
||||||
mRealtimeMutex.Unlock();
|
|
||||||
mRealtimeLatency = 0;
|
mRealtimeLatency = 0;
|
||||||
|
mRealtimeLock.Leave();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EXPERIMENTAL_EFFECTS_RACK)
|
#if defined(EXPERIMENTAL_EFFECTS_RACK)
|
||||||
@ -387,119 +386,228 @@ EffectRack *EffectManager::GetRack()
|
|||||||
|
|
||||||
void EffectManager::ShowRack()
|
void EffectManager::ShowRack()
|
||||||
{
|
{
|
||||||
GetRack()->Show();
|
GetRack()->Show(!GetRack()->IsShown());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
void EffectManager::RealtimeInitialize(const WaveTrackArray *tracks)
|
void EffectManager::RealtimeSetEffects(const EffectArray & effects)
|
||||||
{
|
{
|
||||||
mRealtimeMutex.Lock();
|
int newCount = (int) effects.GetCount();
|
||||||
mRealtimeTracks = tracks;
|
Effect **newEffects = new Effect *[newCount];
|
||||||
for (int e = 0; e < mRealtimeCount; e++)
|
for (int i = 0; i < newCount; i++)
|
||||||
{
|
{
|
||||||
mRealtimeEffects[e]->RealtimeInitialize();
|
newEffects[i] = effects[i];
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0, cnt = tracks->GetCount(); i < cnt; i++)
|
// Block RealtimeProcess()
|
||||||
|
RealtimeSuspend();
|
||||||
|
|
||||||
|
// Tell any effects no longer in the chain to clean up
|
||||||
|
for (int i = 0; i < mRealtimeCount; i++)
|
||||||
|
{
|
||||||
|
Effect *e = mRealtimeEffects[i];
|
||||||
|
|
||||||
|
// Scan the new chain for the effect
|
||||||
|
for (int j = 0; j < newCount; j++)
|
||||||
{
|
{
|
||||||
WaveTrack *t = (*tracks)[i];
|
// Found it so we're done
|
||||||
if (t->GetLinked())
|
if (e == newEffects[j])
|
||||||
{
|
{
|
||||||
mRealtimeEffects[e]->RealtimeAddProcessor(2, t->GetRate());
|
e = NULL;
|
||||||
i++;
|
break;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mRealtimeEffects[e]->RealtimeAddProcessor(1, t->GetRate());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must not have been in the new chain, so tell it to cleanup
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
e->RealtimeFinalize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mRealtimeActive = true;
|
// Tell any new effects to get ready
|
||||||
|
for (int i = 0; i < newCount; i++)
|
||||||
|
{
|
||||||
|
Effect *e = newEffects[i];
|
||||||
|
|
||||||
mRealtimeMutex.Unlock();
|
// Scan the old chain for the effect
|
||||||
|
for (int j = 0; j < mRealtimeCount; j++)
|
||||||
|
{
|
||||||
|
// Found it so tell effect to get ready
|
||||||
|
if (e == mRealtimeEffects[j])
|
||||||
|
{
|
||||||
|
e = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must not have been in the old chain, so tell it to initialize
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
e->RealtimeInitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rid of the old chain
|
||||||
|
if (mRealtimeEffects)
|
||||||
|
{
|
||||||
|
delete [] mRealtimeEffects;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And install the new one
|
||||||
|
mRealtimeEffects = newEffects;
|
||||||
|
mRealtimeCount = newCount;
|
||||||
|
|
||||||
|
// Allow RealtimeProcess() to, well, process
|
||||||
|
RealtimeResume();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void EffectManager::RealtimeInitialize()
|
||||||
|
{
|
||||||
|
// No need to do anything if there are no effects
|
||||||
|
if (!mRealtimeCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The audio thread should not be running yet, but protect anyway
|
||||||
|
RealtimeSuspend();
|
||||||
|
|
||||||
|
// Tell each effect to get ready for action
|
||||||
|
for (int i = 0; i < mRealtimeCount; i++)
|
||||||
|
{
|
||||||
|
mRealtimeEffects[i]->RealtimeInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get things moving
|
||||||
RealtimeResume();
|
RealtimeResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectManager::RealtimeFinalize()
|
void EffectManager::RealtimeFinalize()
|
||||||
{
|
{
|
||||||
|
// Make sure nothing is going on
|
||||||
RealtimeSuspend();
|
RealtimeSuspend();
|
||||||
|
|
||||||
mRealtimeActive = false;
|
// It is now safe to clean up
|
||||||
mRealtimeLatency = 0;
|
mRealtimeLatency = 0;
|
||||||
mRealtimeTracks = NULL;
|
|
||||||
|
|
||||||
mRealtimeMutex.Lock();
|
// Tell each effect to clean up as well
|
||||||
for (int i = 0; i < mRealtimeCount; i++)
|
for (int i = 0; i < mRealtimeCount; i++)
|
||||||
{
|
{
|
||||||
mRealtimeEffects[i]->RealtimeFinalize();
|
mRealtimeEffects[i]->RealtimeFinalize();
|
||||||
}
|
}
|
||||||
mRealtimeMutex.Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectManager::RealtimeSuspend()
|
void EffectManager::RealtimeSuspend()
|
||||||
{
|
{
|
||||||
|
mRealtimeLock.Enter();
|
||||||
|
|
||||||
|
// Already suspended...bail
|
||||||
|
if (mRealtimeSuspended)
|
||||||
|
{
|
||||||
|
mRealtimeLock.Leave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show that we aren't going to be doing anything
|
||||||
mRealtimeSuspended = true;
|
mRealtimeSuspended = true;
|
||||||
|
|
||||||
mRealtimeMutex.Lock();
|
// And make sure the effects don't either
|
||||||
for (int i = 0; i < mRealtimeCount; i++)
|
for (int i = 0; i < mRealtimeCount; i++)
|
||||||
{
|
{
|
||||||
mRealtimeEffects[i]->RealtimeSuspend();
|
mRealtimeEffects[i]->RealtimeSuspend();
|
||||||
}
|
}
|
||||||
mRealtimeMutex.Unlock();
|
|
||||||
|
mRealtimeLock.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectManager::RealtimeResume()
|
void EffectManager::RealtimeResume()
|
||||||
{
|
{
|
||||||
mRealtimeMutex.Lock();
|
mRealtimeLock.Enter();
|
||||||
|
|
||||||
|
// Already running...bail
|
||||||
|
if (!mRealtimeSuspended)
|
||||||
|
{
|
||||||
|
mRealtimeLock.Leave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the effects to get ready for more action
|
||||||
for (int i = 0; i < mRealtimeCount; i++)
|
for (int i = 0; i < mRealtimeCount; i++)
|
||||||
{
|
{
|
||||||
mRealtimeEffects[i]->RealtimeResume();
|
mRealtimeEffects[i]->RealtimeResume();
|
||||||
}
|
}
|
||||||
mRealtimeMutex.Unlock();
|
|
||||||
|
|
||||||
|
// And we should too
|
||||||
mRealtimeSuspended = false;
|
mRealtimeSuspended = false;
|
||||||
|
|
||||||
|
mRealtimeLock.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleCount EffectManager::RealtimeProcess(int index, float **buffers, sampleCount numSamples)
|
//
|
||||||
|
// This will be called in a different thread than the main GUI thread.
|
||||||
|
//
|
||||||
|
sampleCount EffectManager::RealtimeProcess(int group, int chans, float rate, float **buffers, sampleCount numSamples)
|
||||||
{
|
{
|
||||||
|
// Protect ourselves from the main thread
|
||||||
|
mRealtimeLock.Enter();
|
||||||
|
|
||||||
// Can be suspended because of the audio stream being paused or because effects
|
// Can be suspended because of the audio stream being paused or because effects
|
||||||
// have been suspended.
|
// have been suspended, so allow the samples to pass as-is.
|
||||||
if (mRealtimeSuspended)
|
if (mRealtimeSuspended || mRealtimeCount == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
mRealtimeLock.Leave();
|
||||||
|
return numSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remember when we started so we can calculate the amount of latency we
|
||||||
|
// are introducing
|
||||||
wxMilliClock_t start = wxGetLocalTimeMillis();
|
wxMilliClock_t start = wxGetLocalTimeMillis();
|
||||||
|
|
||||||
float *olc = (float *) alloca(sizeof(float) * numSamples);
|
// Allocate the in/out buffer arrays
|
||||||
float *orc = (float *) alloca(sizeof(float) * numSamples);
|
float **ibuf = (float **) alloca(chans * sizeof(float *));
|
||||||
|
float **obuf = (float **) alloca(chans * sizeof(float *));
|
||||||
|
|
||||||
float *ibuf[2] = {buffers[0], buffers[1]};
|
// And populate the input with the buffers we've been given while allocating
|
||||||
float *obuf[2] = {olc, orc};
|
// new output buffers
|
||||||
|
for (int i = 0; i < chans; i++)
|
||||||
|
{
|
||||||
|
ibuf[i] = buffers[i];
|
||||||
|
obuf[i] = (float *) alloca(numSamples * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
mRealtimeMutex.Lock();
|
// Now call each effect in the chain while swapping buffer pointers to feed the
|
||||||
|
// output of one effect as the input to the next effect
|
||||||
for (int i = 0; i < mRealtimeCount; i++)
|
for (int i = 0; i < mRealtimeCount; i++)
|
||||||
{
|
{
|
||||||
mRealtimeEffects[i]->RealtimeProcess(index, ibuf, obuf, numSamples);
|
mRealtimeEffects[i]->RealtimeProcess(group, chans, rate, ibuf, obuf, numSamples);
|
||||||
|
|
||||||
float *tbuf[2] = {ibuf[0], ibuf[1]};
|
for (int j = 0; j < chans; j++)
|
||||||
ibuf[0] = obuf[0];
|
{
|
||||||
ibuf[1] = obuf[1];
|
float *temp;
|
||||||
obuf[0] = tbuf[0];
|
temp = ibuf[j];
|
||||||
obuf[1] = tbuf[1];
|
ibuf[j] = obuf[j];
|
||||||
|
obuf[j] = temp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mRealtimeMutex.Unlock();
|
|
||||||
|
|
||||||
if (obuf[0] == buffers[0])
|
// Once we're done, we might wind up with the last effect storing its results
|
||||||
|
// in the temporary buffers. If that's the case, we need to copy it over to
|
||||||
|
// the caller's buffers. This happens when the number of effects is odd.
|
||||||
|
if (mRealtimeCount & 1)
|
||||||
{
|
{
|
||||||
memcpy(buffers[1], ibuf[1], sizeof(float) * numSamples);
|
for (int i = 0; i < chans; i++)
|
||||||
memcpy(buffers[0], ibuf[0], sizeof(float) * numSamples);
|
{
|
||||||
|
memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remember the latency
|
||||||
mRealtimeLatency = (int) (wxGetLocalTimeMillis() - start).GetValue();
|
mRealtimeLatency = (int) (wxGetLocalTimeMillis() - start).GetValue();
|
||||||
|
|
||||||
|
mRealtimeLock.Leave();
|
||||||
|
|
||||||
//
|
//
|
||||||
// This is wrong...needs to handle tails
|
// This is wrong...needs to handle tails
|
||||||
//
|
//
|
||||||
@ -511,39 +619,6 @@ int EffectManager::GetRealtimeLatency()
|
|||||||
return mRealtimeLatency;
|
return mRealtimeLatency;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectManager::SetRealtime(const EffectArray & effects)
|
|
||||||
{
|
|
||||||
Effect **rteffects = new Effect *[effects.GetCount()];
|
|
||||||
if (rteffects)
|
|
||||||
{
|
|
||||||
for (int i = 0, cnt = effects.GetCount(); i < cnt; i++)
|
|
||||||
{
|
|
||||||
rteffects[i] = effects[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
mRealtimeMutex.Lock();
|
|
||||||
Effect **rtold = mRealtimeEffects;
|
|
||||||
mRealtimeEffects = rteffects;
|
|
||||||
mRealtimeCount = effects.GetCount();
|
|
||||||
|
|
||||||
if (mRealtimeActive)
|
|
||||||
{
|
|
||||||
const WaveTrackArray *tracks = mRealtimeTracks;
|
|
||||||
RealtimeFinalize();
|
|
||||||
RealtimeInitialize(tracks);
|
|
||||||
}
|
|
||||||
|
|
||||||
mRealtimeMutex.Unlock();
|
|
||||||
|
|
||||||
if (rtold)
|
|
||||||
{
|
|
||||||
delete [] rtold;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Effect *EffectManager::GetEffect(const PluginID & ID)
|
Effect *EffectManager::GetEffect(const PluginID & ID)
|
||||||
{
|
{
|
||||||
Effect *effect;
|
Effect *effect;
|
||||||
@ -575,7 +650,7 @@ const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget
|
|||||||
{
|
{
|
||||||
if (strTarget == wxEmptyString) // set GetEffectIdentifier to wxT("") to not show an effect in Batch mode
|
if (strTarget == wxEmptyString) // set GetEffectIdentifier to wxT("") to not show an effect in Batch mode
|
||||||
{
|
{
|
||||||
return PluginID(wxEmptyString);
|
return PluginID(wxString(wxEmptyString));
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginManager & pm = PluginManager::Get();
|
PluginManager & pm = PluginManager::Get();
|
||||||
@ -589,7 +664,7 @@ const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget
|
|||||||
plug = pm.GetNextPlugin(PluginTypeEffect);
|
plug = pm.GetNextPlugin(PluginTypeEffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PluginID(wxEmptyString);
|
return PluginID(wxString(wxEmptyString));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EFFECT_CATEGORIES
|
#ifdef EFFECT_CATEGORIES
|
||||||
|
@ -94,12 +94,12 @@ class AUDACITY_DLL_API EffectManager
|
|||||||
|
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
// Realtime effect processing
|
// Realtime effect processing
|
||||||
void RealtimeInitialize(const WaveTrackArray *tracks);
|
void RealtimeSetEffects(const EffectArray & mActive);
|
||||||
|
void RealtimeInitialize();
|
||||||
void RealtimeFinalize();
|
void RealtimeFinalize();
|
||||||
void RealtimeSuspend();
|
void RealtimeSuspend();
|
||||||
void RealtimeResume();
|
void RealtimeResume();
|
||||||
sampleCount RealtimeProcess(int index, float **buffers, sampleCount numSamples);
|
sampleCount RealtimeProcess(int group, int chans, float rate, float **buffers, sampleCount numSamples);
|
||||||
void SetRealtime(const EffectArray & mActive);
|
|
||||||
int GetRealtimeLatency();
|
int GetRealtimeLatency();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -157,17 +157,11 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
wxMutex mRealtimeMutex;
|
wxCriticalSection mRealtimeLock;
|
||||||
Effect **mRealtimeEffects;
|
Effect **mRealtimeEffects;
|
||||||
int mRealtimeCount;
|
int mRealtimeCount;
|
||||||
int mRealtimeLatency;
|
int mRealtimeLatency;
|
||||||
bool mRealtimeActive;
|
|
||||||
bool mRealtimeSuspended;
|
bool mRealtimeSuspended;
|
||||||
const WaveTrackArray *mRealtimeTracks;
|
|
||||||
|
|
||||||
float **inbuffers;
|
|
||||||
float **outbuffers;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef EFFECT_CATEGORIES
|
#ifdef EFFECT_CATEGORIES
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <wx/access.h>
|
#include <wx/access.h>
|
||||||
#include <wx/defs.h>
|
#include <wx/defs.h>
|
||||||
|
#include <wx/bmpbuttn.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/dcmemory.h>
|
#include <wx/dcmemory.h>
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
@ -33,7 +34,6 @@
|
|||||||
#include "EffectRack.h"
|
#include "EffectRack.h"
|
||||||
#include "../Prefs.h"
|
#include "../Prefs.h"
|
||||||
#include "../Project.h"
|
#include "../Project.h"
|
||||||
#include "../widgets/AButton.h"
|
|
||||||
|
|
||||||
#include "../../images/EffectRack/EffectRack.h"
|
#include "../../images/EffectRack/EffectRack.h"
|
||||||
|
|
||||||
@ -80,7 +80,6 @@ EffectRack::EffectRack()
|
|||||||
wxSYSTEM_MENU |
|
wxSYSTEM_MENU |
|
||||||
wxCLOSE_BOX |
|
wxCLOSE_BOX |
|
||||||
wxCAPTION |
|
wxCAPTION |
|
||||||
// wxSIMPLE_BORDER |
|
|
||||||
wxFRAME_NO_TASKBAR |
|
wxFRAME_NO_TASKBAR |
|
||||||
wxFRAME_FLOAT_ON_PARENT)
|
wxFRAME_FLOAT_ON_PARENT)
|
||||||
{
|
{
|
||||||
@ -89,20 +88,20 @@ EffectRack::EffectRack()
|
|||||||
mLastLatency = 0;
|
mLastLatency = 0;
|
||||||
mTimer.SetOwner(this);
|
mTimer.SetOwner(this);
|
||||||
|
|
||||||
mRemovePushed = CreateImage(remove_16x16_xpm, false, true);
|
mPowerPushed = CreateBitmap(power_on_16x16_xpm, false, false);
|
||||||
mRemoveRaised = CreateImage(remove_16x16_xpm, true, true);
|
mPowerRaised = CreateBitmap(power_off_16x16_xpm, true, false);
|
||||||
mPowerPushed = CreateImage(power_on_16x16_xpm, false, false);
|
mSettingsPushed = CreateBitmap(settings_up_16x16_xpm, false, true);
|
||||||
mPowerRaised = CreateImage(power_off_16x16_xpm, true, false);
|
mSettingsRaised = CreateBitmap(settings_down_16x16_xpm, true, true);
|
||||||
mFavPushed = CreateImage(fav_down_16x16_xpm, false, false);
|
mUpDisabled = CreateBitmap(up_9x16_xpm, true, true);
|
||||||
mFavRaised = CreateImage(fav_up_16x16_xpm, true, false);
|
mUpPushed = CreateBitmap(up_9x16_xpm, false, true);
|
||||||
mSettingsPushed = CreateImage(settings_up_16x16_xpm, false, true);
|
mUpRaised = CreateBitmap(up_9x16_xpm, true, true);
|
||||||
mSettingsRaised = CreateImage(settings_down_16x16_xpm, true, true);
|
mDownDisabled = CreateBitmap(down_9x16_xpm, true, true);
|
||||||
mUpDisabled = CreateImage(up_9x16_xpm, true, true);
|
mDownPushed = CreateBitmap(down_9x16_xpm, false, true);
|
||||||
mUpPushed = CreateImage(up_9x16_xpm, false, true);
|
mDownRaised = CreateBitmap(down_9x16_xpm, true, true);
|
||||||
mUpRaised = CreateImage(up_9x16_xpm, true, true);
|
mFavPushed = CreateBitmap(fav_down_16x16_xpm, false, false);
|
||||||
mDownDisabled = CreateImage(down_9x16_xpm, true, true);
|
mFavRaised = CreateBitmap(fav_up_16x16_xpm, true, false);
|
||||||
mDownPushed = CreateImage(down_9x16_xpm, false, true);
|
mRemovePushed = CreateBitmap(remove_16x16_xpm, false, true);
|
||||||
mDownRaised = CreateImage(down_9x16_xpm, true, true);
|
mRemoveRaised = CreateBitmap(remove_16x16_xpm, true, true);
|
||||||
|
|
||||||
wxBoxSizer *bs = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *bs = new wxBoxSizer(wxVERTICAL);
|
||||||
mPanel = new wxPanel(this, wxID_ANY);
|
mPanel = new wxPanel(this, wxID_ANY);
|
||||||
@ -110,12 +109,12 @@ EffectRack::EffectRack()
|
|||||||
SetSizer(bs);
|
SetSizer(bs);
|
||||||
|
|
||||||
wxBoxSizer *hs = new wxBoxSizer(wxHORIZONTAL);
|
wxBoxSizer *hs = new wxBoxSizer(wxHORIZONTAL);
|
||||||
hs->Add(new wxButton(mPanel, wxID_APPLY, _("&Apply")), 0, wxALIGN_LEFT);
|
hs->Add(new wxButton(mPanel, wxID_APPLY, _("&Apply")), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||||
hs->AddStretchSpacer();
|
hs->AddStretchSpacer();
|
||||||
mLatency = new wxStaticText(mPanel, wxID_ANY, _("Latency: 0"));
|
mLatency = new wxStaticText(mPanel, wxID_ANY, _("Latency: 0"));
|
||||||
hs->Add(mLatency, 0, wxALIGN_CENTER);
|
hs->Add(mLatency, 0, wxALIGN_CENTER);
|
||||||
hs->AddStretchSpacer();
|
hs->AddStretchSpacer();
|
||||||
hs->Add(new wxToggleButton(mPanel, wxID_CLEAR, _("&Bypass")), 0, wxALIGN_RIGHT);
|
hs->Add(new wxToggleButton(mPanel, wxID_CLEAR, _("&Bypass")), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
bs = new wxBoxSizer(wxVERTICAL);
|
bs = new wxBoxSizer(wxVERTICAL);
|
||||||
bs->Add(hs, 0, wxEXPAND);
|
bs->Add(hs, 0, wxEXPAND);
|
||||||
@ -156,20 +155,12 @@ EffectRack::~EffectRack()
|
|||||||
|
|
||||||
for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++)
|
for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++)
|
||||||
{
|
{
|
||||||
wxSizerItem *si;
|
if (mFavState[i])
|
||||||
|
|
||||||
si = mMainSizer->GetItem(i * NUMCOLS + COL_FAV);
|
|
||||||
AButton *fav = static_cast<AButton *>(si->GetWindow());
|
|
||||||
|
|
||||||
if (fav && fav->IsDown())
|
|
||||||
{
|
{
|
||||||
si = mMainSizer->GetItem(i * NUMCOLS + COL_POWER);
|
|
||||||
AButton *power = static_cast<AButton *>(si->GetWindow());
|
|
||||||
|
|
||||||
Effect *effect = mEffects[i];
|
Effect *effect = mEffects[i];
|
||||||
gPrefs->Write(wxString::Format(wxT("/EffectsRack/Slot%02d"), i),
|
gPrefs->Write(wxString::Format(wxT("/EffectsRack/Slot%02d"), i),
|
||||||
wxString::Format(wxT("%d,%s"),
|
wxString::Format(wxT("%d,%s"),
|
||||||
power->IsDown(),
|
mPowerState[i],
|
||||||
effect->GetID().c_str()));
|
effect->GetID().c_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,99 +173,67 @@ void EffectRack::Add(Effect *effect, bool active, bool favorite)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AButton *ab;
|
wxBitmapButton *bb;
|
||||||
|
|
||||||
ab = new AButton(mPanel,
|
bb = new wxBitmapButton(mPanel, ID_POWER + mNumEffects, mPowerRaised);
|
||||||
ID_POWER + mNumEffects,
|
bb->SetBitmapSelected(mPowerRaised);
|
||||||
wxDefaultPosition,
|
bb->SetName(_("Active State"));
|
||||||
wxDefaultSize,
|
bb->SetToolTip(_("Set effect active state"));
|
||||||
mPowerRaised,
|
mPowerState.Add(active);
|
||||||
mPowerRaised,
|
|
||||||
mPowerPushed,
|
|
||||||
mPowerPushed,
|
|
||||||
true);
|
|
||||||
ab->SetToolTip(_("Set effect active state"));
|
|
||||||
if (active)
|
if (active)
|
||||||
{
|
{
|
||||||
ab->PushDown();
|
bb->SetBitmapLabel(mPowerPushed);
|
||||||
|
bb->SetBitmapSelected(mPowerPushed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ab->PopUp();
|
bb->SetBitmapLabel(mPowerRaised);
|
||||||
|
bb->SetBitmapSelected(mPowerRaised);
|
||||||
}
|
}
|
||||||
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
ab = new AButton(mPanel,
|
bb = new wxBitmapButton(mPanel, ID_EDITOR + mNumEffects, mSettingsRaised);
|
||||||
ID_EDITOR + mNumEffects,
|
bb->SetBitmapSelected(mSettingsPushed);
|
||||||
wxDefaultPosition,
|
bb->SetName(_("Show/Hide Editor"));
|
||||||
wxDefaultSize,
|
bb->SetToolTip(_("Open/close effect editor"));
|
||||||
mSettingsRaised,
|
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||||
mSettingsRaised,
|
|
||||||
mSettingsPushed,
|
|
||||||
mSettingsPushed,
|
|
||||||
false);
|
|
||||||
ab->SetToolTip(_("Open/close effect editor"));
|
|
||||||
ab->PopUp();
|
|
||||||
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
|
||||||
|
|
||||||
ab = new AButton(mPanel,
|
bb = new wxBitmapButton(mPanel, ID_UP + mNumEffects, mUpRaised);
|
||||||
ID_UP + mNumEffects,
|
bb->SetBitmapSelected(mUpPushed);
|
||||||
wxDefaultPosition,
|
bb->SetBitmapDisabled(mUpDisabled);
|
||||||
wxDefaultSize,
|
bb->SetName(_("Move Up"));
|
||||||
mUpRaised,
|
bb->SetToolTip(_("Move effect up in the rack"));
|
||||||
mUpRaised,
|
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||||
mUpPushed,
|
|
||||||
mUpDisabled,
|
|
||||||
false);
|
|
||||||
ab->SetToolTip(_("Move effect up in the rack"));
|
|
||||||
ab->PopUp();
|
|
||||||
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
|
||||||
|
|
||||||
ab = new AButton(mPanel,
|
bb = new wxBitmapButton(mPanel, ID_DOWN + mNumEffects, mDownRaised);
|
||||||
ID_DOWN + mNumEffects,
|
bb->SetBitmapSelected(mDownPushed);
|
||||||
wxDefaultPosition,
|
bb->SetBitmapDisabled(mDownDisabled);
|
||||||
wxDefaultSize,
|
bb->SetName(_("Move Down"));
|
||||||
mDownRaised,
|
bb->SetToolTip(_("Move effect down in the rack"));
|
||||||
mDownRaised,
|
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||||
mDownPushed,
|
|
||||||
mDownDisabled,
|
|
||||||
false);
|
|
||||||
ab->SetToolTip(_("Move effect down in the rack"));
|
|
||||||
ab->PopUp();
|
|
||||||
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
|
||||||
|
|
||||||
ab = new AButton(mPanel,
|
bb = new wxBitmapButton(mPanel, ID_FAV + mNumEffects, mFavRaised);
|
||||||
ID_FAV + mNumEffects,
|
bb->SetBitmapSelected(mFavPushed);
|
||||||
wxDefaultPosition,
|
bb->SetName(_("Favorite"));
|
||||||
wxDefaultSize,
|
bb->SetToolTip(_("Mark effect as a favorite"));
|
||||||
mFavRaised,
|
mFavState.Add(favorite);
|
||||||
mFavRaised,
|
|
||||||
mFavPushed,
|
|
||||||
mFavPushed,
|
|
||||||
true);
|
|
||||||
ab->SetToolTip(_("Mark effect as a favorite"));
|
|
||||||
if (favorite)
|
if (favorite)
|
||||||
{
|
{
|
||||||
ab->PushDown();
|
bb->SetBitmapLabel(mFavPushed);
|
||||||
|
bb->SetBitmapSelected(mFavPushed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ab->PopUp();
|
bb->SetBitmapLabel(mFavRaised);
|
||||||
|
bb->SetBitmapSelected(mFavRaised);
|
||||||
}
|
}
|
||||||
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
ab = new AButton(mPanel,
|
bb = new wxBitmapButton(mPanel, ID_REMOVE + mNumEffects, mRemoveRaised);
|
||||||
ID_REMOVE + mNumEffects,
|
bb->SetBitmapSelected(mRemovePushed);
|
||||||
wxDefaultPosition,
|
bb->SetName(_("Remove"));
|
||||||
wxDefaultSize,
|
bb->SetToolTip(_("Remove effect from the rack"));
|
||||||
mRemoveRaised,
|
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||||
mRemoveRaised,
|
|
||||||
mRemovePushed,
|
|
||||||
mRemovePushed,
|
|
||||||
false);
|
|
||||||
ab->SetToolTip(_("Remove effect from the rack"));
|
|
||||||
ab->PopUp();
|
|
||||||
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
|
||||||
|
|
||||||
wxStaticText *text = new wxStaticText(mPanel, ID_NAME + mNumEffects, effect->GetName());
|
wxStaticText *text = new wxStaticText(mPanel, ID_NAME + mNumEffects, effect->GetName());
|
||||||
text->SetToolTip(_("Name of the effect"));
|
text->SetToolTip(_("Name of the effect"));
|
||||||
@ -322,13 +281,15 @@ void EffectRack::OnApply(wxCommandEvent & AUNUSED(evt))
|
|||||||
|
|
||||||
for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++)
|
for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++)
|
||||||
{
|
{
|
||||||
AButton *btn = static_cast<AButton *>(FindWindowById(ID_POWER + i));
|
if (mPowerState[i])
|
||||||
if (btn->IsDown())
|
|
||||||
{
|
{
|
||||||
project->OnEffect(mEffects[i]->GetID(), true);
|
project->OnEffect(mEffects[i]->GetID(), true);
|
||||||
|
|
||||||
btn->PopUp();
|
mPowerState[i] = false;
|
||||||
btn->Refresh();
|
|
||||||
|
wxBitmapButton *btn = static_cast<wxBitmapButton *>(FindWindowById(ID_POWER + i));
|
||||||
|
btn->SetBitmapLabel(mPowerRaised);
|
||||||
|
btn->SetBitmapSelected(mPowerRaised);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,16 +304,28 @@ void EffectRack::OnBypass(wxCommandEvent & evt)
|
|||||||
|
|
||||||
void EffectRack::OnPower(wxCommandEvent & evt)
|
void EffectRack::OnPower(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
evt.Skip();
|
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
|
||||||
|
|
||||||
|
int index = GetEffectIndex(btn);
|
||||||
|
mPowerState[index] = !mPowerState[index];
|
||||||
|
if (mPowerState[index])
|
||||||
|
{
|
||||||
|
btn->SetBitmapLabel(mPowerPushed);
|
||||||
|
btn->SetBitmapSelected(mPowerPushed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
btn->SetBitmapLabel(mPowerRaised);
|
||||||
|
btn->SetBitmapSelected(mPowerRaised);
|
||||||
|
}
|
||||||
|
|
||||||
UpdateActive();
|
UpdateActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectRack::OnEditor(wxCommandEvent & evt)
|
void EffectRack::OnEditor(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
AButton *btn = static_cast<AButton *>(evt.GetEventObject());
|
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
|
||||||
|
|
||||||
btn->PopUp();
|
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
|
|
||||||
int index = GetEffectIndex(btn);
|
int index = GetEffectIndex(btn);
|
||||||
@ -366,9 +339,8 @@ void EffectRack::OnEditor(wxCommandEvent & evt)
|
|||||||
|
|
||||||
void EffectRack::OnUp(wxCommandEvent & evt)
|
void EffectRack::OnUp(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
AButton *btn = static_cast<AButton *>(evt.GetEventObject());
|
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
|
||||||
|
|
||||||
btn->PopUp();
|
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
|
|
||||||
int index = GetEffectIndex(btn);
|
int index = GetEffectIndex(btn);
|
||||||
@ -382,9 +354,8 @@ void EffectRack::OnUp(wxCommandEvent & evt)
|
|||||||
|
|
||||||
void EffectRack::OnDown(wxCommandEvent & evt)
|
void EffectRack::OnDown(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
AButton *btn = static_cast<AButton *>(evt.GetEventObject());
|
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
|
||||||
|
|
||||||
btn->PopUp();
|
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
|
|
||||||
size_t index = GetEffectIndex(btn);
|
size_t index = GetEffectIndex(btn);
|
||||||
@ -398,14 +369,26 @@ void EffectRack::OnDown(wxCommandEvent & evt)
|
|||||||
|
|
||||||
void EffectRack::OnFav(wxCommandEvent & evt)
|
void EffectRack::OnFav(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
evt.Skip();
|
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
|
||||||
|
|
||||||
|
int index = GetEffectIndex(btn);
|
||||||
|
mFavState[index] = !mFavState[index];
|
||||||
|
if (mFavState[index])
|
||||||
|
{
|
||||||
|
btn->SetBitmapLabel(mFavPushed);
|
||||||
|
btn->SetBitmapSelected(mFavPushed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
btn->SetBitmapLabel(mFavRaised);
|
||||||
|
btn->SetBitmapSelected(mFavRaised);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectRack::OnRemove(wxCommandEvent & evt)
|
void EffectRack::OnRemove(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
AButton *btn = static_cast<AButton *>(evt.GetEventObject());
|
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
|
||||||
|
|
||||||
btn->PopUp();
|
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
|
|
||||||
int index = GetEffectIndex(btn);
|
int index = GetEffectIndex(btn);
|
||||||
@ -415,6 +398,8 @@ void EffectRack::OnRemove(wxCommandEvent & evt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mEffects.RemoveAt(index);
|
mEffects.RemoveAt(index);
|
||||||
|
mPowerState.RemoveAt(index);
|
||||||
|
mFavState.RemoveAt(index);
|
||||||
|
|
||||||
if (mEffects.GetCount() == 0)
|
if (mEffects.GetCount() == 0)
|
||||||
{
|
{
|
||||||
@ -428,7 +413,9 @@ void EffectRack::OnRemove(wxCommandEvent & evt)
|
|||||||
|
|
||||||
for (int i = 0; i < NUMCOLS; i++)
|
for (int i = 0; i < NUMCOLS; i++)
|
||||||
{
|
{
|
||||||
delete mMainSizer->GetItem(index)->GetWindow();
|
wxWindow *w = mMainSizer->GetItem(index)->GetWindow();
|
||||||
|
mMainSizer->Detach(index);
|
||||||
|
delete w;
|
||||||
}
|
}
|
||||||
|
|
||||||
mMainSizer->Layout();
|
mMainSizer->Layout();
|
||||||
@ -437,7 +424,7 @@ void EffectRack::OnRemove(wxCommandEvent & evt)
|
|||||||
UpdateActive();
|
UpdateActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
wxImage EffectRack::CreateImage(const char *xpm[], bool up, bool pusher)
|
wxBitmap EffectRack::CreateBitmap(const char *xpm[], bool up, bool pusher)
|
||||||
{
|
{
|
||||||
wxMemoryDC dc;
|
wxMemoryDC dc;
|
||||||
wxBitmap pic(xpm);
|
wxBitmap pic(xpm);
|
||||||
@ -464,7 +451,7 @@ wxImage EffectRack::CreateImage(const char *xpm[], bool up, bool pusher)
|
|||||||
|
|
||||||
dc.SelectObject(wxNullBitmap);
|
dc.SelectObject(wxNullBitmap);
|
||||||
|
|
||||||
return mod.ConvertToImage();
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EffectRack::GetEffectIndex(wxWindow *win)
|
int EffectRack::GetEffectIndex(wxWindow *win)
|
||||||
@ -495,6 +482,14 @@ void EffectRack::MoveRowUp(int row)
|
|||||||
mEffects.RemoveAt(row);
|
mEffects.RemoveAt(row);
|
||||||
mEffects.Insert(effect, row - 1);
|
mEffects.Insert(effect, row - 1);
|
||||||
|
|
||||||
|
int state = mPowerState[row];
|
||||||
|
mPowerState.RemoveAt(row);
|
||||||
|
mPowerState.Insert(state, row - 1);
|
||||||
|
|
||||||
|
state = mFavState[row];
|
||||||
|
mFavState.RemoveAt(row);
|
||||||
|
mFavState.Insert(state, row - 1);
|
||||||
|
|
||||||
row *= NUMCOLS;
|
row *= NUMCOLS;
|
||||||
|
|
||||||
for (int i = 0; i < NUMCOLS; i++)
|
for (int i = 0; i < NUMCOLS; i++)
|
||||||
@ -522,16 +517,14 @@ void EffectRack::UpdateActive()
|
|||||||
{
|
{
|
||||||
for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++)
|
for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++)
|
||||||
{
|
{
|
||||||
wxSizerItem *si = mMainSizer->GetItem(i * NUMCOLS + COL_POWER);
|
if (mPowerState[i])
|
||||||
AButton *power = static_cast<AButton *>(si->GetWindow());
|
|
||||||
if (power && power->IsDown())
|
|
||||||
{
|
{
|
||||||
mActive.Add(mEffects[i]);
|
mActive.Add(mEffects[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectManager::Get().SetRealtime(mActive);
|
EffectManager::Get().RealtimeSetEffects(mActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#if defined(EXPERIMENTAL_EFFECTS_RACK)
|
#if defined(EXPERIMENTAL_EFFECTS_RACK)
|
||||||
|
|
||||||
#include <wx/access.h>
|
#include <wx/access.h>
|
||||||
|
#include <wx/bmpbuttn.h>
|
||||||
#include <wx/defs.h>
|
#include <wx/defs.h>
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
@ -39,7 +40,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
wxImage CreateImage(const char *xpm[], bool up, bool pusher);
|
wxBitmap CreateBitmap(const char *xpm[], bool up, bool pusher);
|
||||||
int GetEffectIndex(wxWindow *win);
|
int GetEffectIndex(wxWindow *win);
|
||||||
void MoveRowUp(int row);
|
void MoveRowUp(int row);
|
||||||
void UpdateActive();
|
void UpdateActive();
|
||||||
@ -60,20 +61,23 @@ private:
|
|||||||
wxStaticText *mLatency;
|
wxStaticText *mLatency;
|
||||||
int mLastLatency;
|
int mLastLatency;
|
||||||
|
|
||||||
wxImage mRemovePushed;
|
wxBitmap mPowerPushed;
|
||||||
wxImage mRemoveRaised;
|
wxBitmap mPowerRaised;
|
||||||
wxImage mPowerPushed;
|
wxBitmap mSettingsPushed;
|
||||||
wxImage mPowerRaised;
|
wxBitmap mSettingsRaised;
|
||||||
wxImage mFavPushed;
|
wxBitmap mUpPushed;
|
||||||
wxImage mFavRaised;
|
wxBitmap mUpRaised;
|
||||||
wxImage mSettingsPushed;
|
wxBitmap mUpDisabled;
|
||||||
wxImage mSettingsRaised;
|
wxBitmap mDownPushed;
|
||||||
wxImage mUpPushed;
|
wxBitmap mDownRaised;
|
||||||
wxImage mUpRaised;
|
wxBitmap mDownDisabled;
|
||||||
wxImage mUpDisabled;
|
wxBitmap mFavPushed;
|
||||||
wxImage mDownPushed;
|
wxBitmap mFavRaised;
|
||||||
wxImage mDownRaised;
|
wxBitmap mRemovePushed;
|
||||||
wxImage mDownDisabled;
|
wxBitmap mRemoveRaised;
|
||||||
|
|
||||||
|
wxArrayInt mPowerState;
|
||||||
|
wxArrayInt mFavState;
|
||||||
|
|
||||||
int mNumEffects;
|
int mNumEffects;
|
||||||
|
|
||||||
|
@ -242,7 +242,8 @@ public:
|
|||||||
|
|
||||||
bool IsRealtimeCapable()
|
bool IsRealtimeCapable()
|
||||||
{
|
{
|
||||||
return false;
|
// TODO: This should check to see if setting parameters can be
|
||||||
|
// automated. If so, then realtime is supported.
|
||||||
return mType == EffectTypeProcess;
|
return mType == EffectTypeProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +330,7 @@ void VSTEffectsModule::Terminate()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm)
|
bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm))
|
||||||
{
|
{
|
||||||
// We don't auto-register
|
// We don't auto-register
|
||||||
return true;
|
return true;
|
||||||
@ -849,6 +850,7 @@ private:
|
|||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
ID_VST_PROGRAM = 11000,
|
ID_VST_PROGRAM = 11000,
|
||||||
|
ID_VST_PROGRAMTEXT,
|
||||||
ID_VST_LOAD,
|
ID_VST_LOAD,
|
||||||
ID_VST_SAVE,
|
ID_VST_SAVE,
|
||||||
ID_VST_SLIDERS,
|
ID_VST_SLIDERS,
|
||||||
@ -949,7 +951,7 @@ OSStatus VSTEffectDialog::OnOverlayEvent(EventHandlerCallRef handler, EventRef e
|
|||||||
sizeof(evtwin),
|
sizeof(evtwin),
|
||||||
NULL,
|
NULL,
|
||||||
&evtwin);
|
&evtwin);
|
||||||
#define DEBUG_VST
|
|
||||||
#if defined(DEBUG_VST)
|
#if defined(DEBUG_VST)
|
||||||
int cls = GetEventClass(event);
|
int cls = GetEventClass(event);
|
||||||
printf("OVERLAY class %4.4s kind %d ewin %p owin %p mwin %p anf %p fnf %p\n",
|
printf("OVERLAY class %4.4s kind %d ewin %p owin %p mwin %p anf %p fnf %p\n",
|
||||||
@ -1662,7 +1664,7 @@ wxSizer *VSTEffectDialog::BuildProgramBar()
|
|||||||
int progn = mEffect->callDispatcher(effGetProgram, 0, 0, NULL, 0.0);
|
int progn = mEffect->callDispatcher(effGetProgram, 0, 0, NULL, 0.0);
|
||||||
|
|
||||||
// An unset program is perfectly valid, do not force a default.
|
// An unset program is perfectly valid, do not force a default.
|
||||||
if (progn >= 0 && progn < progs.GetCount())
|
if (progn >= 0 && progn < (int) progs.GetCount())
|
||||||
{
|
{
|
||||||
val = progs[progn];
|
val = progs[progn];
|
||||||
}
|
}
|
||||||
@ -1743,7 +1745,7 @@ void VSTEffectDialog::RefreshParameters(int skip)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTEffectDialog::OnUpdateDisplay(wxCommandEvent & evt)
|
void VSTEffectDialog::OnUpdateDisplay(wxCommandEvent & WXUNUSED(evt))
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -1793,6 +1795,7 @@ void VSTEffectDialog::OnSlider(wxCommandEvent & evt)
|
|||||||
void VSTEffectDialog::OnProgram(wxCommandEvent & evt)
|
void VSTEffectDialog::OnProgram(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
mEffect->callDispatcher(effSetProgram, 0, evt.GetInt(), NULL, 0.0);
|
mEffect->callDispatcher(effSetProgram, 0, evt.GetInt(), NULL, 0.0);
|
||||||
|
|
||||||
RefreshParameters();
|
RefreshParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1818,14 +1821,16 @@ void VSTEffectDialog::OnProgramText(wxCommandEvent & WXUNUSED(evt))
|
|||||||
mEffect->SetString(effSetProgramName, name, i);
|
mEffect->SetString(effSetProgramName, name, i);
|
||||||
|
|
||||||
// Some effects do not allow you to change the name and you can't always trust the
|
// Some effects do not allow you to change the name and you can't always trust the
|
||||||
// return value, so just get ask for the name again.
|
// return value, so just ask for the name again.
|
||||||
name = mEffect->GetString(effGetProgramNameIndexed, i);
|
name = mEffect->GetString(effGetProgramNameIndexed, i);
|
||||||
|
|
||||||
mProgram->SetString(i, name);
|
mProgram->SetString(i, name);
|
||||||
|
|
||||||
// On Windows, must reselect after doing a SetString()...at least that's
|
// On Windows, you must reselect after doing a SetString()...at least that's
|
||||||
// what seems to be required.
|
// what seems to be required.
|
||||||
|
#if defined(__WXMSW__)
|
||||||
mProgram->SetStringSelection(name);
|
mProgram->SetStringSelection(name);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Which also means we have to reposition the caret.
|
// Which also means we have to reposition the caret.
|
||||||
if (ip >= 0)
|
if (ip >= 0)
|
||||||
@ -2276,7 +2281,6 @@ bool VSTEffectDialog::LoadXML(const wxFileName & fn)
|
|||||||
|
|
||||||
void VSTEffectDialog::OnSave(wxCommandEvent & WXUNUSED(evt))
|
void VSTEffectDialog::OnSave(wxCommandEvent & WXUNUSED(evt))
|
||||||
{
|
{
|
||||||
int i = mProgram->GetCurrentSelection();
|
|
||||||
wxString path;
|
wxString path;
|
||||||
|
|
||||||
// Ask the user for the real name
|
// Ask the user for the real name
|
||||||
@ -2942,13 +2946,12 @@ private:
|
|||||||
typedef AEffect *(*vstPluginMain)(audioMasterCallback audioMaster);
|
typedef AEffect *(*vstPluginMain)(audioMasterCallback audioMaster);
|
||||||
|
|
||||||
intptr_t VSTEffect::AudioMaster(AEffect * effect,
|
intptr_t VSTEffect::AudioMaster(AEffect * effect,
|
||||||
int32_t opcode,
|
int32_t opcode,
|
||||||
int32_t index,
|
int32_t index,
|
||||||
intptr_t value,
|
intptr_t value,
|
||||||
void * ptr,
|
void * ptr,
|
||||||
float opt)
|
float opt)
|
||||||
{
|
{
|
||||||
|
|
||||||
VSTEffect *vst = (effect ? (VSTEffect *) effect->user : NULL);
|
VSTEffect *vst = (effect ? (VSTEffect *) effect->user : NULL);
|
||||||
|
|
||||||
// Handles operations during initialization...before VSTEffect has had a
|
// Handles operations during initialization...before VSTEffect has had a
|
||||||
@ -3052,6 +3055,8 @@ intptr_t VSTEffect::AudioMaster(AEffect * effect,
|
|||||||
{
|
{
|
||||||
char *s = (char *) ptr;
|
char *s = (char *) ptr;
|
||||||
if (strcmp(s, "acceptIOChanges") == 0 ||
|
if (strcmp(s, "acceptIOChanges") == 0 ||
|
||||||
|
strcmp(s, "sendVstTimeInfo") == 0 ||
|
||||||
|
strcmp(s, "startStopProcess") == 0 ||
|
||||||
strcmp(s, "sizeWindow") == 0)
|
strcmp(s, "sizeWindow") == 0)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -3069,9 +3074,15 @@ intptr_t VSTEffect::AudioMaster(AEffect * effect,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
|
||||||
|
case audioMasterBeginEdit:
|
||||||
|
case audioMasterEndEdit:
|
||||||
|
return 0;
|
||||||
|
|
||||||
case audioMasterAutomate:
|
case audioMasterAutomate:
|
||||||
if (vst)
|
if (vst)
|
||||||
|
{
|
||||||
vst->Automate(index, opt);
|
vst->Automate(index, opt);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@ -3132,6 +3143,11 @@ VSTEffect::VSTEffect(const wxString & path, VSTEffect *master)
|
|||||||
mUseBufferDelay = true;
|
mUseBufferDelay = true;
|
||||||
mReady = false;
|
mReady = false;
|
||||||
|
|
||||||
|
mMasterIn = NULL;
|
||||||
|
mMasterInLen = 0;
|
||||||
|
mMasterOut = NULL;
|
||||||
|
mMasterOutLen = 0;
|
||||||
|
|
||||||
memset(&mTimeInfo, 0, sizeof(mTimeInfo));
|
memset(&mTimeInfo, 0, sizeof(mTimeInfo));
|
||||||
mTimeInfo.samplePos = 0.0;
|
mTimeInfo.samplePos = 0.0;
|
||||||
mTimeInfo.sampleRate = 44100.0; // this is a bogus value, but it's only for the display
|
mTimeInfo.sampleRate = 44100.0; // this is a bogus value, but it's only for the display
|
||||||
@ -3153,70 +3169,9 @@ VSTEffect::~VSTEffect()
|
|||||||
Unload();
|
Unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// ============================================================================
|
||||||
// EffectClientInterface Implementation
|
// IdentInterface Implementation
|
||||||
//
|
// ============================================================================
|
||||||
void VSTEffect::SetHost(EffectHostInterface *host)
|
|
||||||
{
|
|
||||||
mHost = host;
|
|
||||||
Startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VSTEffect::Startup()
|
|
||||||
{
|
|
||||||
if (!mAEffect)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mAEffect)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mHost will be null when running in the subprocess
|
|
||||||
if (mHost)
|
|
||||||
{
|
|
||||||
mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mUserBlockSize, 8192);
|
|
||||||
mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true);
|
|
||||||
|
|
||||||
mBlockSize = mUserBlockSize;
|
|
||||||
|
|
||||||
bool haveDefaults;
|
|
||||||
mHost->GetPrivateConfig(wxT("Default"), wxT("Initialized"), haveDefaults, false);
|
|
||||||
if (!haveDefaults)
|
|
||||||
{
|
|
||||||
SaveParameters(wxT("Default"));
|
|
||||||
mHost->SetPrivateConfig(wxT("Default"), wxT("Initialized"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadParameters(wxT("Current"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VSTEffect::Shutdown()
|
|
||||||
{
|
|
||||||
SaveParameters(wxT("Current"));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectType VSTEffect::GetType()
|
|
||||||
{
|
|
||||||
if (mAudioIns == 0 && mMidiIns == 0)
|
|
||||||
{
|
|
||||||
return EffectTypeGenerate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAudioOuts == 0 && mMidiOuts == 0)
|
|
||||||
{
|
|
||||||
return EffectTypeAnalyze;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EffectTypeProcess;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString VSTEffect::GetID()
|
wxString VSTEffect::GetID()
|
||||||
{
|
{
|
||||||
@ -3270,6 +3225,26 @@ wxString VSTEffect::GetDescription()
|
|||||||
return mDescription;
|
return mDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// EffectIdentInterface Implementation
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
EffectType VSTEffect::GetType()
|
||||||
|
{
|
||||||
|
if (mAudioIns == 0 && mMidiIns == 0)
|
||||||
|
{
|
||||||
|
return EffectTypeGenerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAudioOuts == 0 && mMidiOuts == 0)
|
||||||
|
{
|
||||||
|
return EffectTypeAnalyze;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EffectTypeProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString VSTEffect::GetFamily()
|
wxString VSTEffect::GetFamily()
|
||||||
{
|
{
|
||||||
return VSTPLUGINTYPE;
|
return VSTPLUGINTYPE;
|
||||||
@ -3295,6 +3270,57 @@ bool VSTEffect::IsRealtimeCapable()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// EffectClientInterface Implementation
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void VSTEffect::SetHost(EffectHostInterface *host)
|
||||||
|
{
|
||||||
|
mHost = host;
|
||||||
|
Startup();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSTEffect::Startup()
|
||||||
|
{
|
||||||
|
if (!mAEffect)
|
||||||
|
{
|
||||||
|
Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mAEffect)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mHost will be null when running in the subprocess
|
||||||
|
if (mHost)
|
||||||
|
{
|
||||||
|
mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mUserBlockSize, 8192);
|
||||||
|
mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true);
|
||||||
|
|
||||||
|
mBlockSize = mUserBlockSize;
|
||||||
|
|
||||||
|
bool haveDefaults;
|
||||||
|
mHost->GetPrivateConfig(wxT("Default"), wxT("Initialized"), haveDefaults, false);
|
||||||
|
if (!haveDefaults)
|
||||||
|
{
|
||||||
|
SaveParameters(wxT("Default"));
|
||||||
|
mHost->SetPrivateConfig(wxT("Default"), wxT("Initialized"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadParameters(wxT("Current"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSTEffect::Shutdown()
|
||||||
|
{
|
||||||
|
SaveParameters(wxT("Current"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int VSTEffect::GetAudioInCount()
|
int VSTEffect::GetAudioInCount()
|
||||||
{
|
{
|
||||||
return mAudioIns;
|
return mAudioIns;
|
||||||
@ -3317,8 +3343,6 @@ int VSTEffect::GetMidiOutCount()
|
|||||||
|
|
||||||
sampleCount VSTEffect::GetBlockSize(sampleCount maxBlockSize)
|
sampleCount VSTEffect::GetBlockSize(sampleCount maxBlockSize)
|
||||||
{
|
{
|
||||||
sampleCount prevSize = mBlockSize;
|
|
||||||
|
|
||||||
if (mUserBlockSize > maxBlockSize)
|
if (mUserBlockSize > maxBlockSize)
|
||||||
{
|
{
|
||||||
mBlockSize = maxBlockSize;
|
mBlockSize = maxBlockSize;
|
||||||
@ -3398,16 +3422,32 @@ sampleCount VSTEffect::ProcessBlock(float **inbuf, float **outbuf, sampleCount s
|
|||||||
{
|
{
|
||||||
// Go let the plugin moleste the samples
|
// Go let the plugin moleste the samples
|
||||||
callProcessReplacing(inbuf, outbuf, size);
|
callProcessReplacing(inbuf, outbuf, size);
|
||||||
|
|
||||||
|
// And track the position
|
||||||
mTimeInfo.samplePos += ((double) size / mTimeInfo.sampleRate);
|
mTimeInfo.samplePos += ((double) size / mTimeInfo.sampleRate);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VSTEffect::GetChannelCount()
|
||||||
|
{
|
||||||
|
return mNumChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTEffect::SetChannelCount(int numChannels)
|
||||||
|
{
|
||||||
|
mNumChannels = numChannels;
|
||||||
|
}
|
||||||
|
|
||||||
bool VSTEffect::RealtimeInitialize()
|
bool VSTEffect::RealtimeInitialize()
|
||||||
{
|
{
|
||||||
// This is really just a dummy value and one to make the dialog happy since
|
// This is really just a dummy value and one to make the dialog happy since
|
||||||
// all processing is handled by slaves.
|
// all processing is handled by slaves.
|
||||||
SetSampleRate(44100);
|
SetSampleRate(44100);
|
||||||
|
mMasterIn = NULL;
|
||||||
|
mMasterInLen = 0;
|
||||||
|
mMasterOut = NULL;
|
||||||
|
mMasterOutLen = 0;
|
||||||
|
|
||||||
return ProcessInitialize();
|
return ProcessInitialize();
|
||||||
}
|
}
|
||||||
@ -3418,18 +3458,61 @@ bool VSTEffect::RealtimeAddProcessor(int numChannels, float sampleRate)
|
|||||||
mSlaves.Add(slave);
|
mSlaves.Add(slave);
|
||||||
|
|
||||||
slave->SetSampleRate(sampleRate);
|
slave->SetSampleRate(sampleRate);
|
||||||
|
slave->SetChannelCount(numChannels);
|
||||||
|
|
||||||
|
int clen = 0;
|
||||||
|
if (mAEffect->flags & effFlagsProgramChunks)
|
||||||
|
{
|
||||||
|
void *chunk = NULL;
|
||||||
|
|
||||||
|
clen = (int) callDispatcher(effGetChunk, 1, 0, &chunk, 0.0);
|
||||||
|
if (clen != 0)
|
||||||
|
{
|
||||||
|
slave->callDispatcher(effSetChunk, 1, clen, chunk, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clen == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mAEffect->numParams; i++)
|
||||||
|
{
|
||||||
|
slave->callSetParameter(i, callGetParameter(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ProcessInitialize();
|
return ProcessInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int asdf=0;
|
||||||
bool VSTEffect::RealtimeFinalize()
|
bool VSTEffect::RealtimeFinalize()
|
||||||
{
|
{
|
||||||
|
asdf=1;
|
||||||
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
|
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
|
||||||
{
|
{
|
||||||
delete mSlaves[i];
|
delete mSlaves[i];
|
||||||
}
|
}
|
||||||
mSlaves.Clear();
|
mSlaves.Clear();
|
||||||
|
|
||||||
|
if (mMasterIn)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mAudioIns; i++)
|
||||||
|
{
|
||||||
|
delete [] mMasterIn[i];
|
||||||
|
}
|
||||||
|
delete [] mMasterIn;
|
||||||
|
mMasterIn = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMasterOut)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mAudioOuts; i++)
|
||||||
|
{
|
||||||
|
delete [] mMasterOut[i];
|
||||||
|
}
|
||||||
|
delete [] mMasterOut;
|
||||||
|
mMasterOut = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return ProcessFinalize();
|
return ProcessFinalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3447,14 +3530,75 @@ bool VSTEffect::RealtimeResume()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleCount VSTEffect::RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size)
|
sampleCount VSTEffect::RealtimeProcess(int group, float **inbuf, float **outbuf, sampleCount size)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= mSlaves.GetCount())
|
if (group < 0 || group >= (int) mSlaves.GetCount())
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mSlaves[index]->ProcessBlock(inbuf, outbuf, size);
|
if (group == 0)
|
||||||
|
{
|
||||||
|
if (mMasterIn == NULL || mMasterInLen < size)
|
||||||
|
{
|
||||||
|
if (mMasterIn)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mAudioIns; i++)
|
||||||
|
{
|
||||||
|
delete [] mMasterIn[i];
|
||||||
|
}
|
||||||
|
delete [] mMasterIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMasterIn = new float *[mAudioIns];
|
||||||
|
for (int i = 0; i < mAudioIns; i++)
|
||||||
|
{
|
||||||
|
mMasterIn[i] = new float[size];
|
||||||
|
}
|
||||||
|
mMasterInLen = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < mAudioIns; i++)
|
||||||
|
{
|
||||||
|
memset(mMasterIn[i], 0, size * sizeof(float));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int chanCnt = wxMin(mSlaves[group]->GetChannelCount(), mAudioIns);
|
||||||
|
for (int c = 0; c < chanCnt; c++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
mMasterIn[c][i] += inbuf[c][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group == (int) mSlaves.GetCount() - 1)
|
||||||
|
{
|
||||||
|
if (mMasterOut == NULL || mMasterOutLen < size)
|
||||||
|
{
|
||||||
|
if (mMasterOut)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mAudioOuts; i++)
|
||||||
|
{
|
||||||
|
delete [] mMasterOut[i];
|
||||||
|
}
|
||||||
|
delete [] mMasterOut;
|
||||||
|
mMasterOut = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMasterOut = new float *[mAudioOuts];
|
||||||
|
for (int i = 0; i < mAudioOuts; i++)
|
||||||
|
{
|
||||||
|
mMasterOut[i] = new float[size];
|
||||||
|
}
|
||||||
|
mMasterOutLen = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessBlock(mMasterIn, mMasterOut, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mSlaves[group]->ProcessBlock(inbuf, outbuf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -3532,6 +3676,10 @@ bool VSTEffect::ShowInterface(void *parent)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// VSTEffect implementation
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
void VSTEffect::InterfaceClosed()
|
void VSTEffect::InterfaceClosed()
|
||||||
{
|
{
|
||||||
mDlg = NULL;
|
mDlg = NULL;
|
||||||
@ -3667,7 +3815,7 @@ bool VSTEffect::Load()
|
|||||||
// Save the library reference
|
// Save the library reference
|
||||||
mModule = lib;
|
mModule = lib;
|
||||||
}
|
}
|
||||||
|
wxLogDebug(wxT("Loaded"));
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Attempt to load it
|
// Attempt to load it
|
||||||
@ -3802,11 +3950,12 @@ void VSTEffect::Unload()
|
|||||||
|
|
||||||
if (mAEffect)
|
if (mAEffect)
|
||||||
{
|
{
|
||||||
// Turn the power off
|
// Turn the power off
|
||||||
PowerOff();
|
PowerOff();
|
||||||
|
|
||||||
// Finally, close the plugin
|
// Finally, close the plugin
|
||||||
callDispatcher(effClose, 0, 0, NULL, 0.0);
|
callDispatcher(effClose, 0, 0, NULL, 0.0);
|
||||||
|
mAEffect = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mModule)
|
if (mModule)
|
||||||
@ -4086,11 +4235,17 @@ float VSTEffect::callGetParameter(int index)
|
|||||||
|
|
||||||
void VSTEffect::callSetParameter(int index, float value)
|
void VSTEffect::callSetParameter(int index, float value)
|
||||||
{
|
{
|
||||||
mAEffect->setParameter(mAEffect, index, value);
|
if (callDispatcher(effCanBeAutomated, 0, index, NULL, 0.0))
|
||||||
|
|
||||||
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
|
|
||||||
{
|
{
|
||||||
mSlaves[i]->callSetParameter(index, value);
|
mAEffect->setParameter(mAEffect, index, value);
|
||||||
|
|
||||||
|
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
|
||||||
|
{
|
||||||
|
if (mSlaves[i]->callDispatcher(effCanBeAutomated, 0, index, NULL, 0.0))
|
||||||
|
{
|
||||||
|
mSlaves[i]->callSetParameter(index, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,10 @@ class VSTEffect : public EffectClientInterface
|
|||||||
virtual bool RealtimeFinalize();
|
virtual bool RealtimeFinalize();
|
||||||
virtual bool RealtimeSuspend();
|
virtual bool RealtimeSuspend();
|
||||||
virtual bool RealtimeResume();
|
virtual bool RealtimeResume();
|
||||||
virtual sampleCount RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size);
|
virtual sampleCount RealtimeProcess(int group,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples);
|
||||||
|
|
||||||
virtual bool ShowInterface(void *parent);
|
virtual bool ShowInterface(void *parent);
|
||||||
|
|
||||||
@ -129,8 +132,8 @@ private:
|
|||||||
static int b64decode(wxString in, void *out);
|
static int b64decode(wxString in, void *out);
|
||||||
|
|
||||||
// Realtime
|
// Realtime
|
||||||
bool IsSlave();
|
int GetChannelCount();
|
||||||
|
void SetChannelCount(int numChannels);
|
||||||
|
|
||||||
// Utility methods
|
// Utility methods
|
||||||
|
|
||||||
@ -206,6 +209,11 @@ private:
|
|||||||
// Realtime processing
|
// Realtime processing
|
||||||
VSTEffect *mMaster; // non-NULL if a slave
|
VSTEffect *mMaster; // non-NULL if a slave
|
||||||
VSTEffectArray mSlaves;
|
VSTEffectArray mSlaves;
|
||||||
|
int mNumChannels;
|
||||||
|
float **mMasterIn;
|
||||||
|
int mMasterInLen;
|
||||||
|
float **mMasterOut;
|
||||||
|
int mMasterOutLen;
|
||||||
|
|
||||||
friend class VSTEffectDialog;
|
friend class VSTEffectDialog;
|
||||||
friend class VSTEffectsModule;
|
friend class VSTEffectsModule;
|
||||||
|
@ -111,6 +111,8 @@ const int effIdentify = 22; // from http://www.asseca.org/vst-24-specs/efIdentif
|
|||||||
const int effGetChunk = 23; // from Ardour
|
const int effGetChunk = 23; // from Ardour
|
||||||
const int effSetChunk = 24; // from Ardour
|
const int effSetChunk = 24; // from Ardour
|
||||||
const int effProcessEvents = 25;
|
const int effProcessEvents = 25;
|
||||||
|
// The next one was gleaned from http://www.asseca.org/vst-24-specs/efCanBeAutomated.html
|
||||||
|
const int effCanBeAutomated = 26;
|
||||||
// The next one was gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347
|
// The next one was gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347
|
||||||
const int effGetProgramNameIndexed = 29;
|
const int effGetProgramNameIndexed = 29;
|
||||||
const int effGetEffectName = 45;
|
const int effGetEffectName = 45;
|
||||||
@ -122,6 +124,15 @@ const int effCanDo = 51; // currently unused
|
|||||||
// The next one was gleaned from http://www.asseca.org/vst-24-specs/efIdle.html
|
// The next one was gleaned from http://www.asseca.org/vst-24-specs/efIdle.html
|
||||||
const int effIdle = 53;
|
const int effIdle = 53;
|
||||||
const int effGetVstVersion = 58; // currently unused
|
const int effGetVstVersion = 58; // currently unused
|
||||||
|
// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginSetProgram.html
|
||||||
|
const int effBeginSetProgram = 67;
|
||||||
|
// The next one was gleaned from http://www.asseca.org/vst-24-specs/efEndSetProgram.html
|
||||||
|
const int effEndSetProgram = 68;
|
||||||
|
// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadBank.html
|
||||||
|
const int effBeginLoadBank = 75;
|
||||||
|
// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadProgram.html
|
||||||
|
const int effBeginLoadProgram = 76;
|
||||||
|
|
||||||
// The next two were gleaned from http://www.kvraudio.com/forum/printview.php?t=143587&start=0
|
// The next two were gleaned from http://www.kvraudio.com/forum/printview.php?t=143587&start=0
|
||||||
const int effStartProcess = 71;
|
const int effStartProcess = 71;
|
||||||
const int effStopProcess = 72;
|
const int effStopProcess = 72;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user