a lot of VST support odds-and-ends, including preset discovery and support, extending vestige header a bit, some new thread stuff (may break compilation against JACK, and deliver partial tempo/meter info to VST plugins

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@4824 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2009-03-13 03:53:22 +00:00
parent 6a869dfb7a
commit 82da4fa3ac
13 changed files with 298 additions and 40 deletions

View file

@ -38,6 +38,8 @@
#include <gtkmm/adjustment.h>
#include <gtkmm/togglebutton.h>
#include <gtkmm/socket.h>
#include <gtkmm/combobox.h>
#include <gtkmm/liststore.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/socket.h>
@ -264,6 +266,22 @@ class VSTPluginUI : public PlugUIBase, public Gtk::VBox
bool configure_handler (GdkEventConfigure*, Gtk::Socket*);
void save_plugin_setting ();
struct PresetModelColumns : public Gtk::TreeModel::ColumnRecord {
PresetModelColumns() {
add (name);
add (number);
}
Gtk::TreeModelColumn<Glib::ustring> name;
Gtk::TreeModelColumn<int> number;
};
PresetModelColumns preset_columns;
Glib::RefPtr<Gtk::ListStore> preset_model;
Gtk::ComboBox vst_preset_combo;
void create_preset_store ();
void preset_chosen ();
};
#endif // VST_SUPPORT

View file

@ -35,11 +35,17 @@ VSTPluginUI::VSTPluginUI (boost::shared_ptr<PluginInsert> pi, boost::shared_ptr<
: PlugUIBase (pi),
vst (vp)
{
create_preset_store ();
fst_run_editor (vst->fst());
preset_box.set_spacing (6);
preset_box.set_border_width (6);
preset_box.pack_end (bypass_button, false, false, 10);
preset_box.pack_end (save_button, false, false);
preset_box.pack_end (preset_combo, false, false);
preset_box.pack_end (vst_preset_combo, false, false);
vst_preset_combo.signal_changed().connect (mem_fun (*this, &VSTPluginUI::preset_chosen));
bypass_button.set_active (!insert->active());
@ -52,6 +58,17 @@ VSTPluginUI::~VSTPluginUI ()
// nothing to do here - plugin destructor destroys the GUI
}
void
VSTPluginUI::preset_chosen ()
{
int program = vst_preset_combo.get_active_row_number ();
cerr << "switch to program " << program << endl;
// cant be done here. plugin only expects one GUI thread.
//jvst->fst->plugin->dispatcher( jvst->fst->plugin, effSetProgram, 0, program, NULL, 0.0 );
vst->fst()->want_program = program;
socket.grab_focus ();
}
int
VSTPluginUI::get_preferred_height ()
{
@ -121,6 +138,44 @@ VSTPluginUI::configure_handler (GdkEventConfigure* ev, Gtk::Socket *socket)
return false;
}
void
VSTPluginUI::create_preset_store ()
{
FST *fst = vst->fst();
int vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
preset_model = ListStore::create (preset_columns);
cerr << "There are " << fst->plugin->numPrograms << " programs\n";
for (int i = 0; i < fst->plugin->numPrograms; ++i) {
char buf[100];
TreeModel::Row row = *(preset_model->append());
snprintf (buf, 90, "preset %d", i);
if (vst_version >= 2) {
fst->plugin->dispatcher (fst->plugin, 29, i, 0, buf, 0.0);
}
row[preset_columns.name] = buf;
row[preset_columns.number] = i;
cerr << "Preset " << i << " => " << buf << endl;
}
if (fst->plugin->numPrograms > 0) {
fst->plugin->dispatcher( fst->plugin, effSetProgram, 0, 0, NULL, 0.0 );
}
vst_preset_combo.set_model (preset_model);
CellRenderer* renderer = manage (new CellRendererText());
vst_preset_combo.pack_start (*renderer, true);
vst_preset_combo.add_attribute (*renderer, "text", 0);
vst_preset_combo.set_active (0);
}
typedef int (*error_handler_t)( Display *, XErrorEvent *);
static Display *the_gtk_display;
static error_handler_t wine_error_handler;
@ -146,3 +201,4 @@ gui_init (int *argc, char **argv[])
the_gtk_display = gdk_x11_display_get_xdisplay (gdk_display_get_default());
gtk_error_handler = XSetErrorHandler( fst_xerror_handler );
}

View file

@ -1308,6 +1308,8 @@ Session::disable_record (bool rt_context, bool force)
if ((rs = (RecordState) g_atomic_int_get (&_record_status)) != Disabled) {
cerr << "disable record is doing something\n";
if ((!Config->get_latched_record_enable () && !play_loop) || force) {
g_atomic_int_set (&_record_status, Disabled);
} else {

View file

@ -49,6 +49,8 @@ using namespace ARDOUR;
using namespace sigc;
using namespace PBD;
#define DEBUG_TRANSPORT
void
Session::request_input_change_handling ()
{
@ -77,6 +79,9 @@ void
Session::request_transport_speed (float speed)
{
Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, speed);
#ifdef DEBUG_TRANSPORT
cerr << "Queued transport speed request @ " << speed << endl;
#endif
queue_event (ev);
}
@ -136,11 +141,13 @@ Session::realtime_stop (bool abort)
{
/* assume that when we start, we'll be moving forwards */
#if 1
if (_transport_speed < 0.0f) {
post_transport_work = PostTransportWork (post_transport_work | PostTransportStop | PostTransportReverse);
} else {
post_transport_work = PostTransportWork (post_transport_work | PostTransportStop);
}
#endif
if (actively_recording()) {
@ -162,21 +169,25 @@ Session::realtime_stop (bool abort)
post_transport_work = PostTransportWork (post_transport_work | PostTransportAbort);
}
#if 0
_clear_event_type (Event::StopOnce);
_clear_event_type (Event::RangeStop);
_clear_event_type (Event::RangeLocate);
disable_record (true);
reset_slave_state ();
// disable_record (true);
// reset_slave_state ();
#endif
_transport_speed = 0;
#if 0
if (Config->get_use_video_sync()) {
waiting_for_sync_offset = true;
}
transport_sub_state = ((Config->get_slave_source() == None && Config->get_auto_return()) ? AutoReturning : 0);
#endif
}
void
@ -190,6 +201,8 @@ Session::butler_transport_work ()
int on_entry = g_atomic_int_get (&butler_should_do_transport_work);
finished = true;
cerr << "PTW = " << hex << post_transport_work << dec << endl;
if (post_transport_work & PostTransportCurveRealloc) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->curve_reallocate();
@ -235,11 +248,12 @@ Session::butler_transport_work ()
}
if (post_transport_work & (PostTransportStop|PostTransportLocate)) {
non_realtime_stop (post_transport_work & PostTransportAbort, on_entry, finished);
if (!finished) {
g_atomic_int_dec_and_test (&butler_should_do_transport_work);
goto restart;
}
// cerr << "call NRS\n";
// non_realtime_stop (post_transport_work & PostTransportAbort, on_entry, finished);
// if (!finished) {
// g_atomic_int_dec_and_test (&butler_should_do_transport_work);
// goto restart;
// }
}
if (post_transport_work & PostTransportOverWrite) {
@ -294,6 +308,8 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
did_record = false;
saved = false;
cerr << "NRS\n";
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
@ -351,6 +367,8 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
_have_captured = true;
}
return;
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
(*i)->transport_stopped (*now, xnow, abort);
}
@ -377,6 +395,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
synced_to_jack()) {
if (pending_locate_flush) {
cerr << "Flush all redirects\n";
flush_all_redirects ();
}
@ -480,7 +499,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
}
if (post_transport_work & PostTransportStop) {
_play_range = false;
// _play_range = false;
/* do not turn off autoloop on stop */
@ -778,6 +797,10 @@ Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool w
void
Session::set_transport_speed (float speed, bool abort)
{
#ifdef DEBUG_TRANSPORT
cerr << "Session::set_trasport_speed, new = " << speed
<< " cur = " << _transport_speed << endl;
#endif
if (_transport_speed == speed) {
return;
}
@ -886,6 +909,10 @@ Session::set_transport_speed (float speed, bool abort)
void
Session::stop_transport (bool abort)
{
#ifdef DEBUG_TRANSPORT
cerr << "Session::stop_transport @ " << _transport_frame << endl;
#endif
if (_transport_speed == 0.0f) {
return;
}
@ -921,12 +948,16 @@ Session::stop_transport (bool abort)
}
realtime_stop (abort);
schedule_butler_transport_work ();
// schedule_butler_transport_work ();
}
void
Session::start_transport ()
{
#ifdef DEBUG_TRANSPORT
cerr << "Session::start_transport @ " << _transport_frame << endl;
#endif
_last_roll_location = _transport_frame;
have_looped = false;
@ -977,7 +1008,7 @@ Session::post_transport ()
if (post_transport_work & PostTransportStop) {
transport_sub_state = 0;
// transport_sub_state = 0;
}
if (post_transport_work & PostTransportLocate) {

View file

@ -24,11 +24,12 @@
#include <fst/vestige/aeffectx.h>
#include <ardour/session.h>
#include <ardour/tempo.h>
#include <ardour/vst_plugin.h>
#include "i18n.h"
// #define DEBUG_CALLBACKS
#define DEBUG_CALLBACKS
#ifdef DEBUG_CALLBACKS
#define SHOW_CALLBACK printf
@ -49,14 +50,15 @@ long Session::vst_callback (AEffect* effect,
VSTPlugin* plug;
Session* session;
SHOW_CALLBACK ("am callback, opcode = %d", opcode);
if (effect && effect->user) {
plug = (VSTPlugin*) (effect->user);
session = &plug->session();
SHOW_CALLBACK ("am callback %d, opcode = %ld, plugin = \"%s\" ", pthread_self(), opcode, plug->name());
} else {
plug = 0;
session = 0;
SHOW_CALLBACK ("am callback %d, opcode = %ld", pthread_self(), opcode);
}
switch(opcode){
@ -113,7 +115,30 @@ long Session::vst_callback (AEffect* effect,
if (session) {
_timeInfo.samplePos = session->transport_frame();
_timeInfo.sampleRate = session->frame_rate();
_timeInfo.flags = 0;
cerr << "pos = " << _timeInfo.samplePos << " SR = " << _timeInfo.sampleRate
<< " asked for " << std::hex << value << std::dec << endl;
if (value & (kVstTempoValid)) {
const Tempo& t (session->tempo_map().tempo_at (session->transport_frame()));
_timeInfo.tempo = t.beats_per_minute ();
_timeInfo.flags |= (kVstTempoValid);
cerr << "Tempo = " << _timeInfo.tempo << endl;
}
if (value & (kVstBarsValid)) {
const Meter& m (session->tempo_map().meter_at (session->transport_frame()));
_timeInfo.timeSigNumerator = m.beats_per_bar ();
_timeInfo.timeSigDenominator = m.note_divisor ();
_timeInfo.flags |= (kVstBarsValid);
cerr << "Meter = " << _timeInfo.timeSigNumerator << '/' << _timeInfo.timeSigDenominator << endl;
}
if (session->transport_speed() != 0.0f) {
_timeInfo.flags |= kVstTransportPlaying;
}
}
return (long)&_timeInfo;
case audioMasterProcessEvents:
@ -128,7 +153,13 @@ long Session::vst_callback (AEffect* effect,
case audioMasterTempoAt:
SHOW_CALLBACK ("amc: audioMasterTempoAt\n");
// returns tempo (in bpm * 10000) at sample frame location passed in <value>
return 0;
if (session) {
const Tempo& t (session->tempo_map().tempo_at (value));
return t.beats_per_minute() * 1000;
} else {
return 0;
}
break;
case audioMasterGetNumAutomatableParameters:
SHOW_CALLBACK ("amc: audioMasterGetNumAutomatableParameters\n");
@ -306,7 +337,7 @@ long Session::vst_callback (AEffect* effect,
return 0;
default:
SHOW_CALLBACK ("VST master dispatcher: undefed: %d, %d\n", opcode, effKeysRequired);
SHOW_CALLBACK ("VST master dispatcher: undefed: %d\n", opcode);
break;
}

View file

@ -70,13 +70,16 @@ VSTPlugin::VSTPlugin (AudioEngine& e, Session& session, FSTHandle* h)
/* set rate and blocksize */
cerr << name() << " dispatch effSetSampleRate\n";
_plugin->dispatcher (_plugin, effSetSampleRate, 0, 0, NULL,
(float) session.frame_rate());
cerr << name() << " dispatch effSetBlockSize\n";
_plugin->dispatcher (_plugin, effSetBlockSize, 0,
session.get_block_size(), NULL, 0.0f);
/* set program to zero */
cerr << name() << " dispatch effSetProgram\n";
_plugin->dispatcher (_plugin, effSetProgram, 0, 0, NULL, 0.0f);
Plugin::setup_controls ();
@ -106,6 +109,7 @@ void
VSTPlugin::set_block_size (nframes_t nframes)
{
deactivate ();
cerr << name() << " dispatch effSetBlockSize\n";
_plugin->dispatcher (_plugin, effSetBlockSize, 0, nframes, NULL, 0.0f);
activate ();
}
@ -150,6 +154,7 @@ VSTPlugin::get_state()
void* data;
long data_size;
cerr << name() << " dispatch = GetChunk\n";
if ((data_size = _plugin->dispatcher (_plugin, 23 /* effGetChunk */, 0, 0, &data, false)) == 0) {
return *root;
}
@ -253,6 +258,7 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
desc.min_unbound = false;
desc.max_unbound = false;
cerr << name() << " dispatch ParamProps\n";
if (_plugin->dispatcher (_plugin, effGetParameterProperties, which, 0, &prop, 0)) {
#ifdef VESTIGE_COMPLETE
@ -300,6 +306,7 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
char label[64];
label[0] = '\0';
cerr << name() << " dispatch effGetParamName\n";
_plugin->dispatcher (_plugin, effGetParamName, which, 0, label, 0);
desc.label = label;
@ -345,6 +352,7 @@ string
VSTPlugin::describe_parameter (uint32_t param)
{
char name[64];
cerr << this->name() << " dispatch effGetParamName\n";
_plugin->dispatcher (_plugin, effGetParamName, param, 0, name, 0);
return name;
}
@ -378,6 +386,7 @@ VSTPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in_
float *outs[_plugin->numOutputs];
int32_t i;
for (i = 0; i < (int32_t) _plugin->numInputs; ++i) {
ins[i] = bufs[min((uint32_t) in_index,maxbuf - 1)] + offset;
in_index++;
@ -398,20 +407,24 @@ VSTPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in_
/* we already know it can support processReplacing */
cerr << "++++++++++ " << name() << " RUN in " << pthread_self() << endl;
_plugin->processReplacing (_plugin, ins, outs, nframes);
cerr << "\tdone\n";
return 0;
}
void
VSTPlugin::deactivate ()
{
cerr << "\n\n\n ************ " << name() << " DEACTIVATE in " << pthread_self() << endl;
_plugin->dispatcher (_plugin, effMainsChanged, 0, 0, NULL, 0.0f);
}
void
VSTPlugin::activate ()
{
cerr << "\n\n\n ************ " << name() << " ACTIVATE in " << pthread_self() << endl;
_plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f);
}
@ -463,6 +476,7 @@ VSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const
{
char *first_nonws;
cerr << name() << " dispatch paramDisplay\n";
_plugin->dispatcher (_plugin, 7 /* effGetParamDisplay */, param, 0, buf, 0);
if (buf[0] == '\0') {

View file

@ -17,8 +17,9 @@ if fst['VST']:
b = fst.Object ('fstinfofile', 'fstinfofile.c')
c = fst.Object ('vstwin', 'vstwin.c')
d = fst.Object ('vsti', 'vsti.c')
e = fst.Object ('thread', 'thread.c')
Default([a,b,c,d])
Default([a,b,c,d,e])
env.Alias('tarball', env.Distribute (env['DISTTREE'],
fst_src + ['SConscript',

View file

@ -133,6 +133,9 @@ extern int fst_load_state (FST * fst, char * filename);
*/
extern int fst_save_state (FST * fst, char * filename);
extern int wine_pthread_create (pthread_t* thread_id, const pthread_attr_t* attr, void *(*function)(void*), void* arg);
#ifdef __cplusplus
}
#endif

89
libs/fst/thread.c Normal file
View file

@ -0,0 +1,89 @@
#include <stdio.h>
#include <sys/types.h>
#include <pthread.h>
#include <windows.h>
typedef struct {
void* (*thread_function)(void*);
void* thread_arg;
pthread_t thread_id;
pthread_mutex_t init_lock;
pthread_cond_t init_cond;
pthread_attr_t attr;
} real_thread_info_t;
static DWORD WINAPI
fake_thread_proxy (LPVOID parameter)
{
real_thread_info_t* rti = (real_thread_info_t*) parameter;
fprintf (stderr, "WINDOWS THREAD, @ pthread = %p\n", pthread_self());
pthread_mutex_lock (&rti->init_lock);
rti->thread_id = pthread_self();
pthread_cond_signal (&rti->init_cond);
pthread_mutex_unlock (&rti->init_lock);
#if 0
if (pthread_attr_get_schedparam (&rti->attr)) {
pthread_set_schedparam (pthread_self(), policy, sched_param);
}
#endif
/* XXX no way to use pthread API to set contention scope,
because that has to be done before a thread is created.
But ... its only meaningful for an M:N thread implemenation
so its not important for the only platform where
this code matters (Linux running Wine) because Linux
uses a 1:1 thread design.
*/
return (DWORD) rti->thread_function (rti->thread_arg);
}
int
wine_pthread_create (pthread_t* thread_id, const pthread_attr_t* attr, void *(*function)(void*), void* arg)
{
DWORD tid;
size_t stack_size;
fprintf (stderr, "****** Lets make a windows pthread\n");
real_thread_info_t* rti = (real_thread_info_t*) malloc (sizeof (real_thread_info_t));
rti->thread_function = function;
rti->thread_arg = arg;
if (attr) {
rti->attr = *attr;
}
fprintf (stderr, "\tset up the locks\n");
pthread_mutex_init (&rti->init_lock, NULL);
pthread_cond_init (&rti->init_cond, NULL);
pthread_mutex_lock (&rti->init_lock);
fprintf (stderr, "\tget the stacksize\n");
if (attr) {
if (pthread_attr_getstacksize (attr, &stack_size) != 0) {
stack_size = 0;
}
} else {
stack_size = 0;
}
fprintf (stderr, "\tget that sucker started in the proxy, stacksize = %u\n", stack_size);
if (CreateThread (0, stack_size, fake_thread_proxy, rti, 0, &tid) == NULL) {
return -1;
}
pthread_cond_wait (&rti->init_cond, &rti->init_lock);
pthread_mutex_unlock (&rti->init_lock);
fprintf (stderr, "\tlet it run\n");
*thread_id = rti->thread_id;
return 0;
}

View file

@ -129,8 +129,18 @@
#define kVstMidiType 1
#define kVstTransportPlaying (1 << 1)
#define kVstParameterUsesFloatStep (1 << 2)
/* validity flags for a VstTimeINfo structure this info comes from the web */
#define kVstNanosValid (1 << 8)
#define kVstPpqPosValid (1 << 9)
#define kVstTempoValid (1 << 10)
#define kVstBarsValid (1 << 11)
#define kVstCyclePosValid (1 << 12)
#define kVstTimeSigValid (1 << 13)
#define kVstSmpteValid (1 << 14)
#define kVstClockValid (1 << 15)
#define kVstTransportChanged 1
typedef struct VstMidiEvent
@ -253,30 +263,26 @@ typedef struct AEffect
typedef struct VstTimeInfo
{
// 00
double samplePos;
// 08
double sampleRate;
// unconfirmed 10 18
char empty1[8 + 8];
// 20?
double tempo;
// unconfirmed 28 30 38
char empty2[8 + 8 + 8];
// 40?
int timeSigNumerator;
// 44?
int timeSigDenominator;
// unconfirmed 48 4c 50
char empty3[4 + 4 + 4];
// 54
int flags;
/* info from online documentation of VST provided by Steinberg */
double samplePos;
double sampleRate;
double nanoSeconds;
double ppqPos;
double tempo;
double barStartPos;
double cycleStartPos;
double cycleEndPos;
double timeSigNumerator;
double timeSigDenominator;
long smpteOffset;
long smpteFrameRate;
long samplesToNextClock;
long flags;
} VstTimeInfo;
typedef long int (* audioMasterCallback)( AEffect * , long int , long int ,
long int , void * , float );
// we don't use it, may be noise

View file

@ -1,4 +1,6 @@
#include <stdio.h>
#include <jack/jack.h>
#include <jack/thread.h>
#include <libgen.h>
#include <windows.h>
#include <winnt.h>
@ -182,6 +184,7 @@ again:
/* condition/unlock: it was signalled & unlocked in fst_create_editor() */
}
if(fst->want_program != -1 ) {
fprintf (stderr, "switching to program %d\n", fst->want_program);
fst->plugin->dispatcher (fst->plugin, effSetProgram, 0, fst->want_program, NULL, 0);
fst->want_program = -1;
}
@ -263,6 +266,9 @@ fst_init (void* possible_hmodule)
fst_error ("could not create new thread proxy");
return -1;
}
jack_set_thread_creator (wine_pthread_create);
return 0;
}

View file

@ -16,6 +16,7 @@ winmain.c
#libs/fst/fstinfofile.o
#libs/fst/vstwin.o
#libs/fst/vsti.o
#libs/fst/thread.o
"""
)

View file

@ -3,4 +3,4 @@
. ../gtk2_ardour/ardev_common.sh
export LD_LIBRARY_PATH=gtk2_ardour:$LD_LIBRARY_PATH
exec wine ./vst/ardour_vst.exe.so "$@"
wine-pthread ./vst/ardour_vst.exe.so "$@"