1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 08:09:32 +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:
lllucius 2014-11-03 06:48:54 +00:00
parent 4b1679cf87
commit cae6669275
15 changed files with 721 additions and 393 deletions

View File

@ -127,7 +127,7 @@ public:
virtual bool RealtimeFinalize() = 0;
virtual bool RealtimeSuspend() = 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;
};

View File

@ -1362,7 +1362,7 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks,
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
if (mNumPlaybackChannels > 0)
{
EffectManager::Get().RealtimeInitialize(&mPlaybackTracks);
EffectManager::Get().RealtimeInitialize();
}
#endif
@ -3499,8 +3499,6 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
numSolo++;
#endif
int logicalCnt = 0;
int chanCnt = 0;
WaveTrack **chans = (WaveTrack **) alloca(numPlaybackChannels * sizeof(WaveTrack *));
float **tempBufs = (float **) alloca(numPlaybackChannels * sizeof(float *));
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));
}
int group = 0;
int chanCnt = 0;
float rate = 0.0;
for (t = 0; t < numPlaybackTracks; t++)
{
WaveTrack *vt = gAudioIO->mPlaybackTracks[t];
@ -3527,7 +3528,14 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
if (vt->GetMute() && !vt->GetSolo())
cut = true;
rate = vt->GetRate();
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
@ -3574,7 +3582,7 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
#endif
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
len = EffectManager::Get().RealtimeProcess(logicalCnt++, tempBufs, len);
len = EffectManager::Get().RealtimeProcess(group++, chanCnt, rate, tempBufs, len);
#endif
// If our buffer is empty and the time indicator is past
// the end, then we've actually finished playing the entire

View File

@ -15,7 +15,6 @@
#include "portaudio.h"
#include "Audacity.h"
#include "effects/Effect.h"
#include "Experimental.h"
#ifdef USE_MIDI

View File

@ -409,7 +409,6 @@ bool ModuleManager::DiscoverProviders(wxArrayString & providers)
for (int i = 0, cnt = provList.GetCount(); i < cnt; i++)
{
wxPrintf(wxT("provider %s\n"), provList[i].c_str());
providers.push_back(provList[i]);
}

View File

@ -136,20 +136,21 @@ CheckListAx::CheckListAx( wxListCtrl * window ):
CheckListAx::~CheckListAx()
{
}
}
void CheckListAx::SetSelected( int item )
{
if (mLastId != -1) {
if (mLastId != -1)
{
NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE,
mParent,
wxOBJID_CLIENT,
mLastId );
mLastId = -1;
}
}
if (item != -1)
{
{
NotifyEvent( wxACC_EVENT_OBJECT_FOCUS,
mParent,
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).
// rect is in screen coordinates.
wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId )
{
{
if( elementId == wxACC_SELF )
{
{
rect = mParent->GetRect();
rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) );
}
else
{
}
else
{
if( elementId <= mParent->GetItemCount() )
{
mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL );
@ -1287,9 +1288,15 @@ PluginManager & PluginManager::Get()
void PluginManager::Initialize()
{
Load();
bool loaded = Load();
ModuleManager::Get().EarlyInit();
if (!loaded)
{
PluginRegistrationDialog dlg;
dlg.ShowModal();
}
CheckForUpdates();
bool doRescan;
@ -1333,12 +1340,12 @@ void PluginManager::Terminate()
}
}
void PluginManager::Load()
bool PluginManager::Load()
{
// IF already open THEN nothing to do.
if (mConfig != NULL)
{
return;
return true;
}
// Create the config
@ -1353,7 +1360,7 @@ void PluginManager::Load()
{
// Must start over
mConfig->DeleteAll();
return;
return false;
}
// Load all provider plugins first
@ -1363,6 +1370,8 @@ void PluginManager::Load()
LoadGroup(wxT("effects"), PluginTypeEffect);
LoadGroup(wxT("exporters"), PluginTypeExporter);
LoadGroup(wxT("importers"), PluginTypeImporter);
return true;
}
void PluginManager::LoadGroup(const wxChar * group, PluginType type)

View File

@ -223,7 +223,7 @@ public:
void CheckForUpdates();
private:
void Load();
bool Load();
void LoadGroup(const wxChar *group, PluginType type);
void Save();
void SaveGroup(const wxChar *group, PluginType type);

View File

@ -1291,25 +1291,13 @@ bool Effect::RealtimeInitialize()
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
if (mClient)
{
mNumChannels = 0;
mNumGroups = -1;
return mClient->RealtimeInitialize();
}
#endif
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()
{
@ -1347,53 +1335,111 @@ bool Effect::RealtimeResume()
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)
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));
tin = (float **) alloca(mNumAudioIn * sizeof(float *));
for (int i = 0; i < mNumChannels; i++)
// If we don't have enough input channels to accomodate the client's
// requirements, then we replicate the input channels until the
// 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;
if (mNumAudioOut > mNumChannels)
{
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;
}
}
// Remember the number of channel groups we've seen
mNumGroups++;
// Finally call the plugin to process the block
// 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);
return len;
#else
return 0;
#endif
@ -1540,6 +1586,26 @@ void Effect::Preview(bool dryOnly)
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,
const wxString & title,
int type,

View File

@ -239,13 +239,20 @@ class AUDACITY_DLL_API Effect : public EffectHostInterface
// important for sorting.
static wxString StripAmpersand(const wxString& str);
int GetAudioInCount();
int GetAudioOutCount();
// Realtime Effect Processing
bool RealtimeInitialize();
bool RealtimeAddProcessor(int numChannels, float sampleRate);
bool RealtimeFinalize();
bool RealtimeSuspend();
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

View File

@ -175,13 +175,12 @@ EffectManager::EffectManager()
#endif
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
mRealtimeMutex.Lock();
mRealtimeLock.Enter();
mRealtimeEffects = NULL;
mRealtimeCount = 0;
mRealtimeActive = false;
mRealtimeSuspended = false;
mRealtimeMutex.Unlock();
mRealtimeSuspended = true;
mRealtimeLatency = 0;
mRealtimeLock.Leave();
#endif
#if defined(EXPERIMENTAL_EFFECTS_RACK)
@ -387,119 +386,228 @@ EffectRack *EffectManager::GetRack()
void EffectManager::ShowRack()
{
GetRack()->Show();
GetRack()->Show(!GetRack()->IsShown());
}
#endif
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
void EffectManager::RealtimeInitialize(const WaveTrackArray *tracks)
void EffectManager::RealtimeSetEffects(const EffectArray & effects)
{
mRealtimeMutex.Lock();
mRealtimeTracks = tracks;
for (int e = 0; e < mRealtimeCount; e++)
int newCount = (int) effects.GetCount();
Effect **newEffects = new Effect *[newCount];
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];
if (t->GetLinked())
// Found it so we're done
if (e == newEffects[j])
{
mRealtimeEffects[e]->RealtimeAddProcessor(2, t->GetRate());
i++;
e = NULL;
break;
}
else
}
// Must not have been in the new chain, so tell it to cleanup
if (e)
{
e->RealtimeFinalize();
}
}
// Tell any new effects to get ready
for (int i = 0; i < newCount; i++)
{
Effect *e = newEffects[i];
// 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])
{
mRealtimeEffects[e]->RealtimeAddProcessor(1, t->GetRate());
e = NULL;
}
}
// Must not have been in the old chain, so tell it to initialize
if (e)
{
e->RealtimeInitialize();
}
}
mRealtimeActive = true;
// Get rid of the old chain
if (mRealtimeEffects)
{
delete [] mRealtimeEffects;
}
mRealtimeMutex.Unlock();
// 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();
}
void EffectManager::RealtimeFinalize()
{
// Make sure nothing is going on
RealtimeSuspend();
mRealtimeActive = false;
// It is now safe to clean up
mRealtimeLatency = 0;
mRealtimeTracks = NULL;
mRealtimeMutex.Lock();
// Tell each effect to clean up as well
for (int i = 0; i < mRealtimeCount; i++)
{
mRealtimeEffects[i]->RealtimeFinalize();
}
mRealtimeMutex.Unlock();
}
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;
mRealtimeMutex.Lock();
// And make sure the effects don't either
for (int i = 0; i < mRealtimeCount; i++)
{
mRealtimeEffects[i]->RealtimeSuspend();
}
mRealtimeMutex.Unlock();
mRealtimeLock.Leave();
}
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++)
{
mRealtimeEffects[i]->RealtimeResume();
}
mRealtimeMutex.Unlock();
// And we should too
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
// have been suspended.
if (mRealtimeSuspended)
// have been suspended, so allow the samples to pass as-is.
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();
float *olc = (float *) alloca(sizeof(float) * numSamples);
float *orc = (float *) alloca(sizeof(float) * numSamples);
// Allocate the in/out buffer arrays
float **ibuf = (float **) alloca(chans * sizeof(float *));
float **obuf = (float **) alloca(chans * sizeof(float *));
float *ibuf[2] = {buffers[0], buffers[1]};
float *obuf[2] = {olc, orc};
// And populate the input with the buffers we've been given while allocating
// 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++)
{
mRealtimeEffects[i]->RealtimeProcess(index, ibuf, obuf, numSamples);
mRealtimeEffects[i]->RealtimeProcess(group, chans, rate, ibuf, obuf, numSamples);
float *tbuf[2] = {ibuf[0], ibuf[1]};
ibuf[0] = obuf[0];
ibuf[1] = obuf[1];
obuf[0] = tbuf[0];
obuf[1] = tbuf[1];
for (int j = 0; j < chans; j++)
{
float *temp;
temp = ibuf[j];
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);
memcpy(buffers[0], ibuf[0], sizeof(float) * numSamples);
for (int i = 0; i < chans; i++)
{
memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
}
}
// Remember the latency
mRealtimeLatency = (int) (wxGetLocalTimeMillis() - start).GetValue();
mRealtimeLock.Leave();
//
// This is wrong...needs to handle tails
//
@ -511,39 +619,6 @@ int EffectManager::GetRealtimeLatency()
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 *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
{
return PluginID(wxEmptyString);
return PluginID(wxString(wxEmptyString));
}
PluginManager & pm = PluginManager::Get();
@ -589,7 +664,7 @@ const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget
plug = pm.GetNextPlugin(PluginTypeEffect);
}
return PluginID(wxEmptyString);
return PluginID(wxString(wxEmptyString));
}
#ifdef EFFECT_CATEGORIES

View File

@ -94,12 +94,12 @@ class AUDACITY_DLL_API EffectManager
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
// Realtime effect processing
void RealtimeInitialize(const WaveTrackArray *tracks);
void RealtimeSetEffects(const EffectArray & mActive);
void RealtimeInitialize();
void RealtimeFinalize();
void RealtimeSuspend();
void RealtimeResume();
sampleCount RealtimeProcess(int index, float **buffers, sampleCount numSamples);
void SetRealtime(const EffectArray & mActive);
sampleCount RealtimeProcess(int group, int chans, float rate, float **buffers, sampleCount numSamples);
int GetRealtimeLatency();
#endif
@ -157,17 +157,11 @@ private:
#endif
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
wxMutex mRealtimeMutex;
wxCriticalSection mRealtimeLock;
Effect **mRealtimeEffects;
int mRealtimeCount;
int mRealtimeLatency;
bool mRealtimeActive;
bool mRealtimeSuspended;
const WaveTrackArray *mRealtimeTracks;
float **inbuffers;
float **outbuffers;
#endif
#ifdef EFFECT_CATEGORIES

View File

@ -17,6 +17,7 @@
#include <wx/access.h>
#include <wx/defs.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/dcmemory.h>
#include <wx/frame.h>
@ -33,7 +34,6 @@
#include "EffectRack.h"
#include "../Prefs.h"
#include "../Project.h"
#include "../widgets/AButton.h"
#include "../../images/EffectRack/EffectRack.h"
@ -80,7 +80,6 @@ EffectRack::EffectRack()
wxSYSTEM_MENU |
wxCLOSE_BOX |
wxCAPTION |
// wxSIMPLE_BORDER |
wxFRAME_NO_TASKBAR |
wxFRAME_FLOAT_ON_PARENT)
{
@ -89,20 +88,20 @@ EffectRack::EffectRack()
mLastLatency = 0;
mTimer.SetOwner(this);
mRemovePushed = CreateImage(remove_16x16_xpm, false, true);
mRemoveRaised = CreateImage(remove_16x16_xpm, true, true);
mPowerPushed = CreateImage(power_on_16x16_xpm, false, false);
mPowerRaised = CreateImage(power_off_16x16_xpm, true, false);
mFavPushed = CreateImage(fav_down_16x16_xpm, false, false);
mFavRaised = CreateImage(fav_up_16x16_xpm, true, false);
mSettingsPushed = CreateImage(settings_up_16x16_xpm, false, true);
mSettingsRaised = CreateImage(settings_down_16x16_xpm, true, true);
mUpDisabled = CreateImage(up_9x16_xpm, true, true);
mUpPushed = CreateImage(up_9x16_xpm, false, true);
mUpRaised = CreateImage(up_9x16_xpm, true, true);
mDownDisabled = CreateImage(down_9x16_xpm, true, true);
mDownPushed = CreateImage(down_9x16_xpm, false, true);
mDownRaised = CreateImage(down_9x16_xpm, true, true);
mPowerPushed = CreateBitmap(power_on_16x16_xpm, false, false);
mPowerRaised = CreateBitmap(power_off_16x16_xpm, true, false);
mSettingsPushed = CreateBitmap(settings_up_16x16_xpm, false, true);
mSettingsRaised = CreateBitmap(settings_down_16x16_xpm, true, true);
mUpDisabled = CreateBitmap(up_9x16_xpm, true, true);
mUpPushed = CreateBitmap(up_9x16_xpm, false, true);
mUpRaised = CreateBitmap(up_9x16_xpm, true, true);
mDownDisabled = CreateBitmap(down_9x16_xpm, true, true);
mDownPushed = CreateBitmap(down_9x16_xpm, false, true);
mDownRaised = CreateBitmap(down_9x16_xpm, true, true);
mFavPushed = CreateBitmap(fav_down_16x16_xpm, false, false);
mFavRaised = CreateBitmap(fav_up_16x16_xpm, true, false);
mRemovePushed = CreateBitmap(remove_16x16_xpm, false, true);
mRemoveRaised = CreateBitmap(remove_16x16_xpm, true, true);
wxBoxSizer *bs = new wxBoxSizer(wxVERTICAL);
mPanel = new wxPanel(this, wxID_ANY);
@ -110,12 +109,12 @@ EffectRack::EffectRack()
SetSizer(bs);
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();
mLatency = new wxStaticText(mPanel, wxID_ANY, _("Latency: 0"));
hs->Add(mLatency, 0, wxALIGN_CENTER);
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->Add(hs, 0, wxEXPAND);
@ -156,20 +155,12 @@ EffectRack::~EffectRack()
for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++)
{
wxSizerItem *si;
si = mMainSizer->GetItem(i * NUMCOLS + COL_FAV);
AButton *fav = static_cast<AButton *>(si->GetWindow());
if (fav && fav->IsDown())
if (mFavState[i])
{
si = mMainSizer->GetItem(i * NUMCOLS + COL_POWER);
AButton *power = static_cast<AButton *>(si->GetWindow());
Effect *effect = mEffects[i];
gPrefs->Write(wxString::Format(wxT("/EffectsRack/Slot%02d"), i),
wxString::Format(wxT("%d,%s"),
power->IsDown(),
mPowerState[i],
effect->GetID().c_str()));
}
}
@ -182,99 +173,67 @@ void EffectRack::Add(Effect *effect, bool active, bool favorite)
return;
}
AButton *ab;
wxBitmapButton *bb;
ab = new AButton(mPanel,
ID_POWER + mNumEffects,
wxDefaultPosition,
wxDefaultSize,
mPowerRaised,
mPowerRaised,
mPowerPushed,
mPowerPushed,
true);
ab->SetToolTip(_("Set effect active state"));
bb = new wxBitmapButton(mPanel, ID_POWER + mNumEffects, mPowerRaised);
bb->SetBitmapSelected(mPowerRaised);
bb->SetName(_("Active State"));
bb->SetToolTip(_("Set effect active state"));
mPowerState.Add(active);
if (active)
{
ab->PushDown();
bb->SetBitmapLabel(mPowerPushed);
bb->SetBitmapSelected(mPowerPushed);
}
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,
ID_EDITOR + mNumEffects,
wxDefaultPosition,
wxDefaultSize,
mSettingsRaised,
mSettingsRaised,
mSettingsPushed,
mSettingsPushed,
false);
ab->SetToolTip(_("Open/close effect editor"));
ab->PopUp();
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
bb = new wxBitmapButton(mPanel, ID_EDITOR + mNumEffects, mSettingsRaised);
bb->SetBitmapSelected(mSettingsPushed);
bb->SetName(_("Show/Hide Editor"));
bb->SetToolTip(_("Open/close effect editor"));
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
ab = new AButton(mPanel,
ID_UP + mNumEffects,
wxDefaultPosition,
wxDefaultSize,
mUpRaised,
mUpRaised,
mUpPushed,
mUpDisabled,
false);
ab->SetToolTip(_("Move effect up in the rack"));
ab->PopUp();
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
bb = new wxBitmapButton(mPanel, ID_UP + mNumEffects, mUpRaised);
bb->SetBitmapSelected(mUpPushed);
bb->SetBitmapDisabled(mUpDisabled);
bb->SetName(_("Move Up"));
bb->SetToolTip(_("Move effect up in the rack"));
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
ab = new AButton(mPanel,
ID_DOWN + mNumEffects,
wxDefaultPosition,
wxDefaultSize,
mDownRaised,
mDownRaised,
mDownPushed,
mDownDisabled,
false);
ab->SetToolTip(_("Move effect down in the rack"));
ab->PopUp();
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
bb = new wxBitmapButton(mPanel, ID_DOWN + mNumEffects, mDownRaised);
bb->SetBitmapSelected(mDownPushed);
bb->SetBitmapDisabled(mDownDisabled);
bb->SetName(_("Move Down"));
bb->SetToolTip(_("Move effect down in the rack"));
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
ab = new AButton(mPanel,
ID_FAV + mNumEffects,
wxDefaultPosition,
wxDefaultSize,
mFavRaised,
mFavRaised,
mFavPushed,
mFavPushed,
true);
ab->SetToolTip(_("Mark effect as a favorite"));
bb = new wxBitmapButton(mPanel, ID_FAV + mNumEffects, mFavRaised);
bb->SetBitmapSelected(mFavPushed);
bb->SetName(_("Favorite"));
bb->SetToolTip(_("Mark effect as a favorite"));
mFavState.Add(favorite);
if (favorite)
{
ab->PushDown();
bb->SetBitmapLabel(mFavPushed);
bb->SetBitmapSelected(mFavPushed);
}
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,
ID_REMOVE + mNumEffects,
wxDefaultPosition,
wxDefaultSize,
mRemoveRaised,
mRemoveRaised,
mRemovePushed,
mRemovePushed,
false);
ab->SetToolTip(_("Remove effect from the rack"));
ab->PopUp();
mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
bb = new wxBitmapButton(mPanel, ID_REMOVE + mNumEffects, mRemoveRaised);
bb->SetBitmapSelected(mRemovePushed);
bb->SetName(_("Remove"));
bb->SetToolTip(_("Remove effect from the rack"));
mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
wxStaticText *text = new wxStaticText(mPanel, ID_NAME + mNumEffects, effect->GetName());
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++)
{
AButton *btn = static_cast<AButton *>(FindWindowById(ID_POWER + i));
if (btn->IsDown())
if (mPowerState[i])
{
project->OnEffect(mEffects[i]->GetID(), true);
btn->PopUp();
btn->Refresh();
mPowerState[i] = false;
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)
{
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();
}
void EffectRack::OnEditor(wxCommandEvent & evt)
{
AButton *btn = static_cast<AButton *>(evt.GetEventObject());
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
btn->PopUp();
evt.Skip();
int index = GetEffectIndex(btn);
@ -366,9 +339,8 @@ void EffectRack::OnEditor(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();
int index = GetEffectIndex(btn);
@ -382,9 +354,8 @@ void EffectRack::OnUp(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();
size_t index = GetEffectIndex(btn);
@ -398,14 +369,26 @@ void EffectRack::OnDown(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)
{
AButton *btn = static_cast<AButton *>(evt.GetEventObject());
wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
btn->PopUp();
evt.Skip();
int index = GetEffectIndex(btn);
@ -415,6 +398,8 @@ void EffectRack::OnRemove(wxCommandEvent & evt)
}
mEffects.RemoveAt(index);
mPowerState.RemoveAt(index);
mFavState.RemoveAt(index);
if (mEffects.GetCount() == 0)
{
@ -428,7 +413,9 @@ void EffectRack::OnRemove(wxCommandEvent & evt)
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();
@ -437,7 +424,7 @@ void EffectRack::OnRemove(wxCommandEvent & evt)
UpdateActive();
}
wxImage EffectRack::CreateImage(const char *xpm[], bool up, bool pusher)
wxBitmap EffectRack::CreateBitmap(const char *xpm[], bool up, bool pusher)
{
wxMemoryDC dc;
wxBitmap pic(xpm);
@ -464,7 +451,7 @@ wxImage EffectRack::CreateImage(const char *xpm[], bool up, bool pusher)
dc.SelectObject(wxNullBitmap);
return mod.ConvertToImage();
return mod;
}
int EffectRack::GetEffectIndex(wxWindow *win)
@ -495,6 +482,14 @@ void EffectRack::MoveRowUp(int row)
mEffects.RemoveAt(row);
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;
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++)
{
wxSizerItem *si = mMainSizer->GetItem(i * NUMCOLS + COL_POWER);
AButton *power = static_cast<AButton *>(si->GetWindow());
if (power && power->IsDown())
if (mPowerState[i])
{
mActive.Add(mEffects[i]);
}
}
}
EffectManager::Get().SetRealtime(mActive);
EffectManager::Get().RealtimeSetEffects(mActive);
}
#endif

View File

@ -19,6 +19,7 @@
#if defined(EXPERIMENTAL_EFFECTS_RACK)
#include <wx/access.h>
#include <wx/bmpbuttn.h>
#include <wx/defs.h>
#include <wx/frame.h>
#include <wx/image.h>
@ -39,7 +40,7 @@ public:
private:
wxImage CreateImage(const char *xpm[], bool up, bool pusher);
wxBitmap CreateBitmap(const char *xpm[], bool up, bool pusher);
int GetEffectIndex(wxWindow *win);
void MoveRowUp(int row);
void UpdateActive();
@ -60,20 +61,23 @@ private:
wxStaticText *mLatency;
int mLastLatency;
wxImage mRemovePushed;
wxImage mRemoveRaised;
wxImage mPowerPushed;
wxImage mPowerRaised;
wxImage mFavPushed;
wxImage mFavRaised;
wxImage mSettingsPushed;
wxImage mSettingsRaised;
wxImage mUpPushed;
wxImage mUpRaised;
wxImage mUpDisabled;
wxImage mDownPushed;
wxImage mDownRaised;
wxImage mDownDisabled;
wxBitmap mPowerPushed;
wxBitmap mPowerRaised;
wxBitmap mSettingsPushed;
wxBitmap mSettingsRaised;
wxBitmap mUpPushed;
wxBitmap mUpRaised;
wxBitmap mUpDisabled;
wxBitmap mDownPushed;
wxBitmap mDownRaised;
wxBitmap mDownDisabled;
wxBitmap mFavPushed;
wxBitmap mFavRaised;
wxBitmap mRemovePushed;
wxBitmap mRemoveRaised;
wxArrayInt mPowerState;
wxArrayInt mFavState;
int mNumEffects;

View File

@ -242,7 +242,8 @@ public:
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;
}
@ -329,7 +330,7 @@ void VSTEffectsModule::Terminate()
return;
}
bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm)
bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm))
{
// We don't auto-register
return true;
@ -849,6 +850,7 @@ private:
enum
{
ID_VST_PROGRAM = 11000,
ID_VST_PROGRAMTEXT,
ID_VST_LOAD,
ID_VST_SAVE,
ID_VST_SLIDERS,
@ -949,7 +951,7 @@ OSStatus VSTEffectDialog::OnOverlayEvent(EventHandlerCallRef handler, EventRef e
sizeof(evtwin),
NULL,
&evtwin);
#define DEBUG_VST
#if defined(DEBUG_VST)
int cls = GetEventClass(event);
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);
// 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];
}
@ -1743,7 +1745,7 @@ void VSTEffectDialog::RefreshParameters(int skip)
}
}
void VSTEffectDialog::OnUpdateDisplay(wxCommandEvent & evt)
void VSTEffectDialog::OnUpdateDisplay(wxCommandEvent & WXUNUSED(evt))
{
int i;
@ -1793,6 +1795,7 @@ void VSTEffectDialog::OnSlider(wxCommandEvent & evt)
void VSTEffectDialog::OnProgram(wxCommandEvent & evt)
{
mEffect->callDispatcher(effSetProgram, 0, evt.GetInt(), NULL, 0.0);
RefreshParameters();
}
@ -1818,14 +1821,16 @@ void VSTEffectDialog::OnProgramText(wxCommandEvent & WXUNUSED(evt))
mEffect->SetString(effSetProgramName, name, i);
// 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);
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.
#if defined(__WXMSW__)
mProgram->SetStringSelection(name);
#endif
// Which also means we have to reposition the caret.
if (ip >= 0)
@ -2276,7 +2281,6 @@ bool VSTEffectDialog::LoadXML(const wxFileName & fn)
void VSTEffectDialog::OnSave(wxCommandEvent & WXUNUSED(evt))
{
int i = mProgram->GetCurrentSelection();
wxString path;
// Ask the user for the real name
@ -2942,13 +2946,12 @@ private:
typedef AEffect *(*vstPluginMain)(audioMasterCallback audioMaster);
intptr_t VSTEffect::AudioMaster(AEffect * effect,
int32_t opcode,
int32_t index,
intptr_t value,
void * ptr,
float opt)
int32_t opcode,
int32_t index,
intptr_t value,
void * ptr,
float opt)
{
VSTEffect *vst = (effect ? (VSTEffect *) effect->user : NULL);
// Handles operations during initialization...before VSTEffect has had a
@ -3052,6 +3055,8 @@ intptr_t VSTEffect::AudioMaster(AEffect * effect,
{
char *s = (char *) ptr;
if (strcmp(s, "acceptIOChanges") == 0 ||
strcmp(s, "sendVstTimeInfo") == 0 ||
strcmp(s, "startStopProcess") == 0 ||
strcmp(s, "sizeWindow") == 0)
{
return 1;
@ -3069,9 +3074,15 @@ intptr_t VSTEffect::AudioMaster(AEffect * effect,
}
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
case audioMasterBeginEdit:
case audioMasterEndEdit:
return 0;
case audioMasterAutomate:
if (vst)
{
vst->Automate(index, opt);
}
return 0;
#else
@ -3132,6 +3143,11 @@ VSTEffect::VSTEffect(const wxString & path, VSTEffect *master)
mUseBufferDelay = true;
mReady = false;
mMasterIn = NULL;
mMasterInLen = 0;
mMasterOut = NULL;
mMasterOutLen = 0;
memset(&mTimeInfo, 0, sizeof(mTimeInfo));
mTimeInfo.samplePos = 0.0;
mTimeInfo.sampleRate = 44100.0; // this is a bogus value, but it's only for the display
@ -3153,70 +3169,9 @@ VSTEffect::~VSTEffect()
Unload();
}
//
// 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;
}
EffectType VSTEffect::GetType()
{
if (mAudioIns == 0 && mMidiIns == 0)
{
return EffectTypeGenerate;
}
if (mAudioOuts == 0 && mMidiOuts == 0)
{
return EffectTypeAnalyze;
}
return EffectTypeProcess;
}
// ============================================================================
// IdentInterface Implementation
// ============================================================================
wxString VSTEffect::GetID()
{
@ -3270,6 +3225,26 @@ wxString VSTEffect::GetDescription()
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()
{
return VSTPLUGINTYPE;
@ -3295,6 +3270,57 @@ bool VSTEffect::IsRealtimeCapable()
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()
{
return mAudioIns;
@ -3317,8 +3343,6 @@ int VSTEffect::GetMidiOutCount()
sampleCount VSTEffect::GetBlockSize(sampleCount maxBlockSize)
{
sampleCount prevSize = mBlockSize;
if (mUserBlockSize > maxBlockSize)
{
mBlockSize = maxBlockSize;
@ -3398,16 +3422,32 @@ sampleCount VSTEffect::ProcessBlock(float **inbuf, float **outbuf, sampleCount s
{
// Go let the plugin moleste the samples
callProcessReplacing(inbuf, outbuf, size);
// And track the position
mTimeInfo.samplePos += ((double) size / mTimeInfo.sampleRate);
return size;
}
int VSTEffect::GetChannelCount()
{
return mNumChannels;
}
void VSTEffect::SetChannelCount(int numChannels)
{
mNumChannels = numChannels;
}
bool VSTEffect::RealtimeInitialize()
{
// This is really just a dummy value and one to make the dialog happy since
// all processing is handled by slaves.
SetSampleRate(44100);
mMasterIn = NULL;
mMasterInLen = 0;
mMasterOut = NULL;
mMasterOutLen = 0;
return ProcessInitialize();
}
@ -3418,18 +3458,61 @@ bool VSTEffect::RealtimeAddProcessor(int numChannels, float sampleRate)
mSlaves.Add(slave);
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();
}
static int asdf=0;
bool VSTEffect::RealtimeFinalize()
{
asdf=1;
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
{
delete mSlaves[i];
}
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();
}
@ -3447,14 +3530,75 @@ bool VSTEffect::RealtimeResume()
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 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
}
// ============================================================================
// VSTEffect implementation
// ============================================================================
void VSTEffect::InterfaceClosed()
{
mDlg = NULL;
@ -3667,7 +3815,7 @@ bool VSTEffect::Load()
// Save the library reference
mModule = lib;
}
wxLogDebug(wxT("Loaded"));
#else
// Attempt to load it
@ -3802,11 +3950,12 @@ void VSTEffect::Unload()
if (mAEffect)
{
// Turn the power off
// Turn the power off
PowerOff();
// Finally, close the plugin
callDispatcher(effClose, 0, 0, NULL, 0.0);
mAEffect = NULL;
}
if (mModule)
@ -4086,11 +4235,17 @@ float VSTEffect::callGetParameter(int index)
void VSTEffect::callSetParameter(int index, float value)
{
mAEffect->setParameter(mAEffect, index, value);
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
if (callDispatcher(effCanBeAutomated, 0, index, NULL, 0.0))
{
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);
}
}
}
}

View File

@ -99,7 +99,10 @@ class VSTEffect : public EffectClientInterface
virtual bool RealtimeFinalize();
virtual bool RealtimeSuspend();
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);
@ -129,8 +132,8 @@ private:
static int b64decode(wxString in, void *out);
// Realtime
bool IsSlave();
int GetChannelCount();
void SetChannelCount(int numChannels);
// Utility methods
@ -206,6 +209,11 @@ private:
// Realtime processing
VSTEffect *mMaster; // non-NULL if a slave
VSTEffectArray mSlaves;
int mNumChannels;
float **mMasterIn;
int mMasterInLen;
float **mMasterOut;
int mMasterOutLen;
friend class VSTEffectDialog;
friend class VSTEffectsModule;

View File

@ -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 effSetChunk = 24; // from Ardour
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
const int effGetProgramNameIndexed = 29;
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
const int effIdle = 53;
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
const int effStartProcess = 71;
const int effStopProcess = 72;