13
0
livetrax/libs/fst/vstwin.c
David Robillard fe13d08874 Large nasty commit in the form of a 5000 line patch chock-full of completely
unecessary changes.  (Sorry, doing a "sprint" based thing, this is the end of the first one)

Achieved MIDI track and bus creation, associated Jack port and diskstream creation, and minimal GUI stuff for creating them.  Should be set to start work on actually recording and playing midi to/from disk now.

Relevant (significant) changes:

- Creation of a Buffer class.  Base class is type agnostic so things can point to a buffer but not care what kind it is (otherwise it'd be a template).  Derived into AudioBuffer and MidiBuffer, with a type tag because checking type is necessary in parts of the code where dynamic_cast wouldn't be wise.  Originally I considered this a hack, but passing around a type proved to be a very good solution to all the other problems (below).  There is a 1:1 mapping between jack port data types and ardour Buffer types (with a conversion function), but that's easily removed if it ever becomes necessary.  Having the type scoped in the Buffer class is maybe not the best spot for it, but whatever (this is proof of concept kinda stuff right now...)

- IO now has a "default" port type (passed to the constructor and stored as a member), used by ensure_io (and similar) to create n ports.  IO::register_***_port has a type argument that defaults to the default type if not passed.  Rationale:  previous IO API is identical, no changes needed to existing code, but path is paved for multiple port types in one IO, which we will need for eg synth plugin inserts, among other things.  This is not quite ideal (best would be to only have the two port register functions and have them take a type), but the alternative is a lot of work (namely destroying the 'ensure' functions and everything that uses them) for very little gain.  (I am convinced after quite a few tries at the whiteboard that subclassing IO in any way is not a feasible option, look at it's inheritance diagram in Doxygen and you can see why)

- AudioEngine::register_audio_input_port is now register_input_port and takes a type argument.  Ditto for output.

- (Most significant change) AudioDiskstream abstracted into Distream, and sibling MidiDiskstream created.  Very much still a work in progress, but Diskstream is there to switch references over to (most already are), which is the important part.  It is still unclear what the MIDI diskstream's relation to channels is, but I'm pretty sure they will be single channel only (so SMF Type 0) since noone can come up with a reason otherwise.

- MidiTrack creation.  Same thing as AudioTrack but with a different default type basically.  No big deal here.

- Random cleanups and variable renamings etc. because I have OCD and can't help myself. :)

Known broken:  Loading of sessions containing MIDI tracks.




git-svn-id: svn://localhost/ardour2/branches/midi@641 d708f5d6-7413-0410-9779-e7cbd77b26cf
2006-06-26 16:01:34 +00:00

584 lines
11 KiB
C

#include <stdio.h>
#include <libgen.h>
#include <windows.h>
#include <winnt.h>
#include <wine/exception.h>
#include <pthread.h>
#include <signal.h>
//#include <x11/xlib.h>
//#include <x11/xresource.h>
//#include <x11/xutil.h>
//#include <x11/xatom.h>
#include "fst.h"
struct ERect{
short top;
short left;
short bottom;
short right;
};
static pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
static FST* fst_first = NULL;
DWORD gui_thread_id = 0;
static char* message_name (int message)
{
switch (message) {
case 0x0000:
return "WM_NULL";
case 0x0001:
return "WM_CREATE";
case 0x0002:
return "WM_DESTROY";
case 0x0003:
return "WM_MOVE";
case 0x0004:
return "WM_SIZEWAIT";
case 0x0005:
return "WM_SIZE";
case 0x0006:
return "WM_ACTIVATE";
case 0x0007:
return "WM_SETFOCUS";
case 0x0008:
return "WM_KILLFOCUS";
case 0x0009:
return "WM_SETVISIBLE";
case 0x000a:
return "WM_ENABLE";
case 0x000b:
return "WM_SETREDRAW";
case 0x000c:
return "WM_SETTEXT";
case 0x000d:
return "WM_GETTEXT";
case 0x000e:
return "WM_GETTEXTLENGTH";
case 0x000f:
return "WM_PAINT";
case 0x0010:
return "WM_CLOSE";
case 0x0011:
return "WM_QUERYENDSESSION";
case 0x0012:
return "WM_QUIT";
case 0x0013:
return "WM_QUERYOPEN";
case 0x0014:
return "WM_ERASEBKGND";
case 0x0015:
return "WM_SYSCOLORCHANGE";
case 0x0016:
return "WM_ENDSESSION";
case 0x0017:
return "WM_SYSTEMERROR";
case 0x0018:
return "WM_SHOWWINDOW";
case 0x0019:
return "WM_CTLCOLOR";
case 0x001a:
return "WM_WININICHANGE";
case 0x001b:
return "WM_DEVMODECHANGE";
case 0x001c:
return "WM_ACTIVATEAPP";
case 0x001d:
return "WM_FONTCHANGE";
case 0x001e:
return "WM_TIMECHANGE";
case 0x001f:
return "WM_CANCELMODE";
case 0x0020:
return "WM_SETCURSOR";
case 0x0021:
return "WM_MOUSEACTIVATE";
case 0x0022:
return "WM_CHILDACTIVATE";
case 0x0023:
return "WM_QUEUESYNC";
case 0x0024:
return "WM_GETMINMAXINFO";
default:
break;
}
return "--- OTHER ---";
}
static LRESULT WINAPI
my_window_proc (HWND w, UINT msg, WPARAM wp, LPARAM lp)
{
FST* fst;
// if (msg != WM_TIMER) {
// fst_error ("window callback handler, msg = 0x%x (%s) win=%p\n", msg, message_name (msg), w);
// }
switch (msg) {
case WM_KEYUP:
case WM_KEYDOWN:
break;
case WM_CLOSE:
PostQuitMessage (0);
case WM_DESTROY:
case WM_NCDESTROY:
/* we should never get these */
//return 0;
break;
case WM_PAINT:
if ((fst = GetPropA (w, "fst_ptr")) != NULL) {
if (fst->window && !fst->been_activated) {
fst->been_activated = TRUE;
pthread_cond_signal (&fst->window_status_change);
pthread_mutex_unlock (&fst->lock);
}
}
break;
default:
break;
}
return DefWindowProcA (w, msg, wp, lp );
}
static FST*
fst_new ()
{
FST* fst = (FST*) calloc (1, sizeof (FST));
pthread_mutex_init (&fst->lock, NULL);
pthread_cond_init (&fst->window_status_change, NULL);
return fst;
}
static FSTHandle*
fst_handle_new ()
{
FSTHandle* fst = (FSTHandle*) calloc (1, sizeof (FSTHandle));
return fst;
}
int
fst_create_editor (FST* fst)
{
HMODULE hInst;
HWND window;
/* "guard point" to trap errors that occur during plugin loading */
/* Note: fst->lock is held while this function is called */
if (!(fst->plugin->flags & effFlagsHasEditor)) {
fst_error ("Plugin \"%s\" has no editor", fst->handle->name);
return -1;
}
if ((hInst = GetModuleHandleA (NULL)) == NULL) {
fst_error ("can't get module handle");
return 1;
}
// if ((window = CreateWindowExA (WS_EX_TOOLWINDOW | WS_EX_TRAYWINDOW, "FST", fst->handle->name,
if ((window = CreateWindowExA (0, "FST", fst->handle->name,
(WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX),
0, 0, 1, 1,
NULL, NULL,
hInst,
NULL)) == NULL) {
fst_error ("cannot create editor window");
return 1;
}
if (!SetPropA (window, "fst_ptr", fst)) {
fst_error ("cannot set fst_ptr on window");
}
fst->window = window;
fst->xid = (int) GetPropA (window, "__wine_x11_whole_window");
{
struct ERect* er;
ShowWindow (fst->window, SW_SHOW);
fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->window, 0 );
fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
fst->width = er->right-er->left;
fst->height = er->bottom-er->top;
SetWindowPos (fst->window, 0, 0, 0, er->right-er->left+8, er->bottom-er->top+26, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER);
}
return 0;
}
void
fst_destroy_editor (FST* fst)
{
pthread_mutex_lock (&fst->lock);
if (fst->window) {
fst->destroy = TRUE;
if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) {
fst_error ("could not post message to gui thread");
}
pthread_cond_wait (&fst->window_status_change, &fst->lock);
}
pthread_mutex_unlock (&fst->lock);
}
void
fst_event_loop_remove_plugin (FST* fst)
{
FST* p;
FST* prev;
for (p = fst_first, prev = NULL; p->next; prev = p, p = p->next) {
if (p == fst) {
if (prev) {
prev->next = p->next;
}
}
}
if (fst_first == fst) {
fst_first = fst_first->next;
}
}
void debreak( void ) { printf( "debreak\n" ); }
DWORD WINAPI gui_event_loop (LPVOID param)
{
MSG msg;
FST* fst;
HMODULE hInst;
HWND window;
gui_thread_id = GetCurrentThreadId ();
/* create a dummy window for timer events */
if ((hInst = GetModuleHandleA (NULL)) == NULL) {
fst_error ("can't get module handle");
return 1;
}
if ((window = CreateWindowExA (0, "FST", "dummy",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,
hInst,
NULL )) == NULL) {
fst_error ("cannot create dummy timer window");
}
if (!SetTimer (window, 1000, 100, NULL)) {
fst_error ("cannot set timer on dummy window");
}
while (GetMessageA (&msg, NULL, 0,0)) {
if( msg.message == WM_KEYDOWN ) debreak();
TranslateMessage( &msg );
DispatchMessageA (&msg);
/* handle window creation requests, destroy requests,
and run idle callbacks
*/
if( msg.message == WM_TIMER ) {
pthread_mutex_lock (&plugin_mutex);
again:
for (fst = fst_first; fst; fst = fst->next) {
if (fst->destroy) {
if (fst->window) {
fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
CloseWindow (fst->window);
fst->window = NULL;
fst->destroy = FALSE;
}
fst_event_loop_remove_plugin (fst);
fst->been_activated = FALSE;
pthread_mutex_lock (&fst->lock);
pthread_cond_signal (&fst->window_status_change);
pthread_mutex_unlock (&fst->lock);
goto again;
}
if (fst->window == NULL) {
pthread_mutex_lock (&fst->lock);
if (fst_create_editor (fst)) {
fst_error ("cannot create editor for plugin %s", fst->handle->name);
fst_event_loop_remove_plugin (fst);
pthread_cond_signal (&fst->window_status_change);
pthread_mutex_unlock (&fst->lock);
goto again;
}
/* condition/unlock handled when we receive WM_ACTIVATE */
}
fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0);
}
pthread_mutex_unlock (&plugin_mutex);
}
}
fst_error ("FST GUI event loop has quit!");
return 0;
}
int
fst_init ()
{
WNDCLASSA wc;
HMODULE hInst;
if ((hInst = GetModuleHandleA (NULL)) == NULL) {
fst_error ("can't get module handle");
return -1;
}
wc.style = 0;
wc.lpfnWndProc = my_window_proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIconA( hInst, "FST");
wc.hCursor = LoadCursorA( NULL, IDI_APPLICATION );
wc.hbrBackground = GetStockObject( BLACK_BRUSH );
wc.lpszMenuName = "MENU_FST";
wc.lpszClassName = "FST";
if (!RegisterClassA(&wc)){
return 1;
}
if (CreateThread (NULL, 0, gui_event_loop, NULL, 0, NULL) == NULL) {
fst_error ("could not create new thread proxy");
return -1;
}
return 0;
}
int
fst_run_editor (FST* fst)
{
/* Add the FST to the list of all that should be handled by the GUI thread */
pthread_mutex_lock (&plugin_mutex);
if (fst_first == NULL) {
fst_first = fst;
} else {
FST* p = fst_first;
while (p->next) {
p = p->next;
}
p->next = fst;
}
if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) {
fst_error ("could not post message to gui thread");
return -1;
}
pthread_mutex_unlock (&plugin_mutex);
/* wait for the plugin editor window to be created (or not) */
pthread_mutex_lock (&fst->lock);
if (!fst->window) {
pthread_cond_wait (&fst->window_status_change, &fst->lock);
}
pthread_mutex_unlock (&fst->lock);
if (!fst->window) {
fst_error ("no window created for VST plugin editor");
return -1;
}
return 0;
}
FSTHandle*
fst_load (const char *path)
{
char* buf;
FSTHandle* fhandle;
char* period;
fhandle = fst_handle_new ();
// XXX: Would be nice to find the correct call for this.
// if the user does not configure Z: to be / we are doomed :(
if (strstr (path, ".dll") == NULL) {
buf = (char *) malloc (strlen (path) + 7);
if( path[0] == '/' ) {
sprintf (buf, "Z:%s.dll", path);
} else {
sprintf (buf, "%s.dll", path);
}
fhandle->nameptr = strdup (path);
} else {
buf = (char *) malloc (strlen (path) + 3);
if( path[0] == '/' ) {
sprintf (buf, "Z:%s", path);
} else {
sprintf (buf, "%s", path);
}
fhandle->nameptr = strdup (path);
}
fhandle->name = basename (fhandle->nameptr);
/* strip off .dll */
if ((period = strrchr (fhandle->name, '.')) != NULL) {
*period = '\0';
}
if ((fhandle->dll = LoadLibraryA (buf)) == NULL) {
fst_unload (fhandle);
return NULL;
}
if ((fhandle->main_entry = GetProcAddress (fhandle->dll, "main")) == NULL) {
fst_unload (fhandle);
return NULL;
}
return fhandle;
}
int
fst_unload (FSTHandle* fhandle)
{
if (fhandle->plugincnt) {
return -1;
}
if (fhandle->dll) {
FreeLibrary (fhandle->dll);
fhandle->dll = NULL;
}
if (fhandle->nameptr) {
free (fhandle->nameptr);
fhandle->name = NULL;
}
free (fhandle);
return 0;
}
FST*
fst_instantiate (FSTHandle* fhandle, audioMasterCallback amc, void* userptr)
{
FST* fst = fst_new ();
if( fhandle == NULL ) {
fst_error( "the handle was NULL\n" );
return NULL;
}
if ((fst->plugin = fhandle->main_entry (amc)) == NULL) {
fst_error ("%s could not be instantiated\n", fhandle->name);
free (fst);
return NULL;
}
fst->handle = fhandle;
fst->plugin->user = userptr;
if (fst->plugin->magic != kEffectMagic) {
fst_error ("%s is not a VST plugin\n", fhandle->name);
free (fst);
return NULL;
}
fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
//fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
fst->handle->plugincnt++;
return fst;
}
void
fst_close (FST* fst)
{
fst_destroy_editor (fst);
fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
if (fst->handle->plugincnt) {
--fst->handle->plugincnt;
}
}
int
fst_get_XID (FST* fst)
{
return fst->xid;
}