Browse Source

WineASIO 0.0.2

tags/v1.0.0
Joakim B Hernberg 14 years ago
parent
commit
8c4e425682
6 changed files with 2258 additions and 57 deletions
  1. +2
    -1
      Makefile.in
  2. +39
    -0
      ReadMe.txt
  3. +235
    -37
      asio.c
  4. +9
    -0
      libwineasio.def
  5. +0
    -11
      readme.txt
  6. +1973
    -8
      wineasio.diff.txt

+ 2
- 1
Makefile.in View File

@@ -5,7 +5,8 @@ VPATH = @srcdir@
MODULE = wineasio.dll
IMPORTLIB = libwineasio.$(IMPLIBEXT)
IMPORTS = winmm ole32 user32 advapi32 kernel32 ntdll
EXTRALIBS = -ldxguid -luuid -ljack
EXTRAINCL = @ASIOINCL@
EXTRALIBS = -ldxguid -luuid -ljack -lpthread

C_SRCS = \
asio.c \


+ 39
- 0
ReadMe.txt View File

@@ -0,0 +1,39 @@
This is the second release of the wine ASIO driver.

Changes:

1. The ASIO driver now requires that the Steinberg ASIO SDK be installed to get ASIO support.
This appears to be how other open source projects deal with the ASIO technology licencing.

2. The first version was able to call Windows functions from the ASIO callback which was
called from the Jack thread. This only worked for programs that didn't call Windows
functions. The ASIO callback is now called from a Windows thread.

3. You should now be able to compile a dummy driver if Jack or the ASIO SDK are not installed.

4. A buffer swap bug was fixed.

Install:

1. Download the Steinberg ASIO SDK from: http://www.steinberg.net/324_1.html and
install it somewhere like: /usr/local/asiosdk2.1.

2. Patch wine with the wineasio.diff.txt patch.

3. Run autoconf to generate a new configure.

4. Run ./configure --with-asio-sdk=/usr/local/asiosdk2.1 (or wherever you installed the SDK).

5. Do the normal make depend, make and make install

6. Register the wineasio.dll by doing:
cd wine/dlls/wineasio
regsvr32 wineasio.dll.so

To Do:
ASIO control panel to set Jack parameters (devices, sample rate, buffer size, ...).
Better integration with wine.
You tell me.

Please send feeback to: reif@earthlink.net


+ 235
- 37
asio.c View File

@@ -27,18 +27,60 @@
#include "objbase.h"
#include "mmsystem.h"

#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#include <semaphore.h>
#endif

#include "wine/library.h"
#include "wine/debug.h"

#ifdef HAVE_JACK_JACK_H
#include <jack/jack.h>
#endif

#include "asio.h"
#ifdef HAVE_ASIO_SDK_ASIO_H
#define IEEE754_64FLOAT 1
#include <common/asio.h>
#endif

WINE_DEFAULT_DEBUG_CHANNEL(asio);

#if defined(HAVE_JACK_JACK_H) && defined(HAVE_ASIO_SDK_ASIO_H) && defined(HAVE_PTHREAD_H)

#ifndef SONAME_LIBJACK
#define SONAME_LIBJACK "libjack.so"
#endif

#define MAKE_FUNCPTR(f) static typeof(f) * fp_##f = NULL;

/* Function pointers for dynamic loading of libjack */
/* these are prefixed with "fp_", ie. "fp_jack_client_new" */
MAKE_FUNCPTR(jack_activate);
MAKE_FUNCPTR(jack_deactivate);
MAKE_FUNCPTR(jack_connect);
MAKE_FUNCPTR(jack_client_open);
MAKE_FUNCPTR(jack_client_close);
MAKE_FUNCPTR(jack_transport_query);
MAKE_FUNCPTR(jack_transport_start);
MAKE_FUNCPTR(jack_set_process_callback);
MAKE_FUNCPTR(jack_get_sample_rate);
MAKE_FUNCPTR(jack_port_register);
MAKE_FUNCPTR(jack_port_get_buffer);
MAKE_FUNCPTR(jack_get_ports);
MAKE_FUNCPTR(jack_port_name);
MAKE_FUNCPTR(jack_get_buffer_size);
#undef MAKE_FUNCPTR

void *jackhandle = NULL;

/* JACK callback function */
static int jack_process(jack_nframes_t nframes, void * arg);

// {48D0C522-BFCC-45cc-8B84-17F25F33E6E8}
/* WIN32 callback function */
static DWORD CALLBACK win32_callback(LPVOID arg);

/* {48D0C522-BFCC-45cc-8B84-17F25F33E6E8} */
static GUID const CLSID_WineASIO = {
0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } };

@@ -48,6 +90,11 @@ static GUID const CLSID_WineASIO = {
#define MAX_INPUTS 2
#define MAX_OUTPUTS 2

/* ASIO drivers use the thiscall calling convention which only Microsoft compilers
* produce. These macros add an extra layer to fixup the registers properly for
* this calling convention.
*/

#ifdef __i386__ /* thiscall functions are i386-specific */

#ifdef __GNUC__
@@ -143,6 +190,7 @@ struct IWineASIOImpl
long active_outputs;
BOOL time_info_mode;
BOOL tc_read;
long state;

/* JACK stuff */
jack_port_t *input_port[MAX_INPUTS];
@@ -150,6 +198,15 @@ struct IWineASIOImpl
jack_client_t *client;
long client_state;
long toggle;

/* callback stuff */
HANDLE thread;
HANDLE start_event;
HANDLE stop_event;
DWORD thread_id;
sem_t semaphore1;
sem_t semaphore2;
BOOL terminate;
};

typedef struct IWineASIOImpl IWineASIOImpl;
@@ -169,9 +226,20 @@ static ULONG WINAPI IWineASIOImpl_Release(LPWINEASIO iface)
TRACE("(%p) ref was %ld\n", This, ref + 1);

if (!ref) {
jack_client_close(This->client);
fp_jack_client_close(This->client);
TRACE("JACK client closed\n");

wine_dlclose(jackhandle, NULL, 0);
jackhandle = NULL;

This->terminate = TRUE;
sem_post(&This->semaphore1);

WaitForSingleObject(This->stop_event, INFINITE);

sem_destroy(&This->semaphore1);
sem_destroy(&This->semaphore2);

HeapFree(GetProcessHeap(),0,This);
TRACE("(%p) released\n", This);
}
@@ -205,7 +273,7 @@ WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *
int i;
TRACE("(%p, %p)\n", iface, sysHandle);

This->sample_rate = 48000;
This->sample_rate = 48000.0;
This->block_frames = 1024;
This->input_latency = This->block_frames;
This->output_latency = This->block_frames * 2;
@@ -221,6 +289,26 @@ WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *
This->client_state = Init;
This->time_info_mode = FALSE;
This->tc_read = FALSE;
This->terminate = FALSE;
This->state = Init;

sem_init(&This->semaphore1, 0, 0);
sem_init(&This->semaphore2, 0, 0);

This->start_event = CreateEventW(NULL, FALSE, FALSE, NULL);
This->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
This->thread = CreateThread(NULL, 0, win32_callback, (LPVOID)This, 0, &This->thread_id);
if (This->thread)
{
WaitForSingleObject(This->start_event, INFINITE);
CloseHandle(This->start_event);
This->start_event = INVALID_HANDLE_VALUE;
}
else
{
WARN("Couldn't create thread\n");
return ASIOFalse;
}

for (i = 0; i < MAX_INPUTS; i++)
{
@@ -232,10 +320,10 @@ WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *
This->out_map[i] = 0;
}

This->client = jack_client_open("Wine_ASIO_Jack_Client", JackNullOption, &status, NULL);
This->client = fp_jack_client_open("Wine_ASIO_Jack_Client", JackNullOption, &status, NULL);
if (This->client == NULL)
{
WARN("failed ot open jack server\n");
WARN("failed to open jack server\n");
return ASIOFalse;
}

@@ -244,9 +332,9 @@ WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *
if (status & JackServerStarted)
TRACE("JACK server started\n");

jack_set_process_callback(This->client, jack_process, This);
fp_jack_set_process_callback(This->client, jack_process, This);

This->sample_rate = jack_get_sample_rate(This->client);
This->sample_rate = fp_jack_get_sample_rate(This->client);
This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate);

TRACE("sample rate: %f\n", This->sample_rate);
@@ -255,7 +343,7 @@ WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *
{
char name[32];
snprintf(name, sizeof(name), "Input%d", i);
This->input_port[i] = jack_port_register(This->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, i);
This->input_port[i] = fp_jack_port_register(This->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, i);
if (This->input_port[i] == 0)
break;

@@ -268,7 +356,7 @@ WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *
{
char name[32];
snprintf(name, sizeof(name), "Output%d", i);
This->output_port[i] = jack_port_register(This->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, i);
This->output_port[i] = fp_jack_port_register(This->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, i);
if (This->output_port[i] == 0)
break;

@@ -312,13 +400,13 @@ WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface))
This->system_time.lo = 0;
This->system_time.hi = 0;

if (jack_activate(This->client))
if (fp_jack_activate(This->client))
{
WARN("couldn't activate client\n");
return ASE_NotPresent;
}

ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput);
ports = fp_jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput);

if (ports == NULL)
{
@@ -328,7 +416,7 @@ WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface))

for (i = 0; i < This->active_inputs; i++)
{
if (jack_connect(This->client, ports[i], jack_port_name(This->input_port[i])))
if (fp_jack_connect(This->client, ports[i], fp_jack_port_name(This->input_port[i])))
{
WARN("input %d connect failed\n", i);
free(ports);
@@ -338,7 +426,7 @@ WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface))

free(ports);

ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
ports = fp_jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);

if (ports == NULL)
{
@@ -348,7 +436,7 @@ WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface))

for (i = 0; i < This->active_outputs; i++)
{
if (jack_connect(This->client, jack_port_name(This->output_port[i]), ports[i]))
if (fp_jack_connect(This->client, fp_jack_port_name(This->output_port[i]), ports[i]))
{
WARN("output %d connect failed\n", i);
free(ports);
@@ -358,6 +446,7 @@ WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface))

free(ports);

This->state = Run;
TRACE("started\n");

return ASE_OK;
@@ -368,7 +457,16 @@ WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface))

WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_stop, (LPWINEASIO iface))
{
TRACE("(%p) stub!\n", iface);
IWineASIOImpl * This = (IWineASIOImpl*)iface;
TRACE("(%p)\n", iface);

This->state = Exit;

if (fp_jack_deactivate(This->client))
{
WARN("couldn't deactivate client\n");
return ASE_NotPresent;
}

return ASE_OK;
}
@@ -786,11 +884,56 @@ static const IWineASIOVtbl WineASIO_Vtbl =
IWineASIOImpl_outputReady,
};

BOOL init_jack()
{
jackhandle = wine_dlopen(SONAME_LIBJACK, RTLD_NOW, NULL, 0);
TRACE("SONAME_LIBJACK == %s\n", SONAME_LIBJACK);
TRACE("jackhandle == %p\n", jackhandle);
if (!jackhandle)
{
FIXME("error loading the jack library %s, please install this library to use jack\n", SONAME_LIBJACK);
jackhandle = (void*)-1;
return FALSE;
}

/* setup function pointers */
#define LOAD_FUNCPTR(f) if((fp_##f = wine_dlsym(jackhandle, #f, NULL, 0)) == NULL) goto sym_not_found;
LOAD_FUNCPTR(jack_activate);
LOAD_FUNCPTR(jack_deactivate);
LOAD_FUNCPTR(jack_connect);
LOAD_FUNCPTR(jack_client_open);
LOAD_FUNCPTR(jack_client_close);
LOAD_FUNCPTR(jack_transport_query);
LOAD_FUNCPTR(jack_transport_start);
LOAD_FUNCPTR(jack_set_process_callback);
LOAD_FUNCPTR(jack_get_sample_rate);
LOAD_FUNCPTR(jack_port_register);
LOAD_FUNCPTR(jack_port_get_buffer);
LOAD_FUNCPTR(jack_get_ports);
LOAD_FUNCPTR(jack_port_name);
LOAD_FUNCPTR(jack_get_buffer_size);
#undef LOAD_FUNCPTR

return TRUE;

/* error path for function pointer loading errors */
sym_not_found:
WINE_MESSAGE("Wine cannot find certain functions that it needs inside the jack"
"library. To enable Wine to use the jack audio server please "
"install libjack\n");
wine_dlclose(jackhandle, NULL, 0);
jackhandle = NULL;
return FALSE;
}

HRESULT asioCreateInstance(REFIID riid, LPVOID *ppobj)
{
IWineASIOImpl * pobj;
TRACE("(%s, %p)\n", debugstr_guid(riid), ppobj);

if (!init_jack())
return ERROR_NOT_SUPPORTED;
pobj = HeapAlloc(GetProcessHeap(), 0, sizeof(*pobj));
if (pobj == NULL) {
WARN("out of memory\n");
@@ -817,13 +960,16 @@ static int jack_process(jack_nframes_t nframes, void * arg)
IWineASIOImpl * This = (IWineASIOImpl*)arg;
int i, j;
jack_default_audio_sample_t * in, *out;
jack_transport_state_t ts = jack_transport_query(This->client, NULL);
jack_transport_state_t ts;

ts = fp_jack_transport_query(This->client, NULL);

if (ts == JackTransportRolling)
{
if (This->client_state == Init)
This->client_state = Run;

This->toggle = This->toggle ? 0 : 1;
This->sample_position += nframes;

/* get the input data from JACK and copy it to the ASIO buffers */
@@ -831,7 +977,7 @@ static int jack_process(jack_nframes_t nframes, void * arg)
{
short * buffer = This->input_buffers[i][This->toggle];

in = jack_port_get_buffer(This->input_port[i], nframes);
in = fp_jack_port_get_buffer(This->input_port[i], nframes);

for (j = 0; j < nframes; j++)
buffer[j] = in[j] * 32767.0f;
@@ -840,43 +986,95 @@ static int jack_process(jack_nframes_t nframes, void * arg)
/* call the ASIO user callback to read the input data and fill the output data */
getNanoSeconds(&This->system_time);

if (This->time_info_mode)
{
__wrapped_IWineASIOImpl_getSamplePosition((LPWINEASIO)This, &This->asio_time.timeInfo.samplePosition, &This->asio_time.timeInfo.systemTime);
if (This->tc_read)
{
/* FIXME */
This->asio_time.timeCode.timeCodeSamples.lo = This->asio_time.timeInfo.samplePosition.lo;
This->asio_time.timeCode.timeCodeSamples.hi = 0;
}
This->callbacks->bufferSwitchTimeInfo(&This->asio_time, This->toggle, ASIOFalse);
This->asio_time.timeInfo.flags &= ~(kSampleRateChanged | kClockSourceChanged);
}
else
This->callbacks->bufferSwitch(This->toggle, ASIOFalse);
/* wake up the WIN32 thread so it can do the do it's callback */
sem_post(&This->semaphore1);

/* wait for the WIN32 thread to complete before continuing */
sem_wait(&This->semaphore2);

/* copy the ASIO data to JACK */
for (i = 0; i < This->active_outputs; i++)
{
short * buffer = This->output_buffers[i][This->toggle];

out = jack_port_get_buffer(This->output_port[i], nframes);
out = fp_jack_port_get_buffer(This->output_port[i], nframes);

for (j = 0; j < nframes; j++)
out[j] = buffer[j] / 32767.0f;
}

This->toggle = This->toggle ? 0 : 1;
}
else if (ts == JackTransportStopped)
{
if (This->client_state == Run)
This->client_state = Exit;
else
{
/* FIXME why is this needed ? */
fp_jack_transport_start(This->client);
}
}

return 0;
}

/*
* The ASIO callback can make WIN32 calls which require a WIN32 thread.
* Do the callback in this thread and then switch back to the Jack callback thread.
*/
static DWORD CALLBACK win32_callback(LPVOID arg)
{
IWineASIOImpl * This = (IWineASIOImpl*)arg;
TRACE("(%p)\n", arg);
/* let IWineASIO_Init know we are alive */
SetEvent(This->start_event);

while (1)
{
/* wait to be woken up by the JAck callback thread */
sem_wait(&This->semaphore1);

/* check for termination */
if (This->terminate)
{
SetEvent(This->stop_event);
TRACE("terminated\n");
return 0;
}

/* FIXME why is this needed ? */
jack_transport_start(This->client);
/* make sure we are in the run state */
if (This->state == Run)
{
if (This->time_info_mode)
{
__wrapped_IWineASIOImpl_getSamplePosition((LPWINEASIO)This,
&This->asio_time.timeInfo.samplePosition, &This->asio_time.timeInfo.systemTime);
if (This->tc_read)
{
/* FIXME */
This->asio_time.timeCode.timeCodeSamples.lo = This->asio_time.timeInfo.samplePosition.lo;
This->asio_time.timeCode.timeCodeSamples.hi = 0;
}
This->callbacks->bufferSwitchTimeInfo(&This->asio_time, This->toggle, ASIOFalse);
This->asio_time.timeInfo.flags &= ~(kSampleRateChanged | kClockSourceChanged);
}
else
This->callbacks->bufferSwitch(This->toggle, ASIOFalse);
}

/* let the Jack thread know we are done */
sem_post(&This->semaphore2);
}

return 0;
}

#else

HRESULT asioCreateInstance(REFIID riid, LPVOID *ppobj)
{
WARN("(%s, %p) ASIO support not compiled into wine\n", debugstr_guid(riid), ppobj);
return ERROR_NOT_SUPPORTED;
}

#endif

+ 9
- 0
libwineasio.def View File

@@ -0,0 +1,9 @@
; File generated automatically from ./wineasio.spec; do not edit!

LIBRARY wineasio.dll

EXPORTS
DllRegisterServer@0 @1 PRIVATE
DllGetClassObject@12 @2 PRIVATE
DllCanUnloadNow@0 @3 PRIVATE
DllUnregisterServer@0 @4 PRIVATE

+ 0
- 11
readme.txt View File

@@ -1,11 +0,0 @@
Instalation instructions:

Copy the wineasio directory to wine/dlls.
Install the wineasio.diff.txt patch.
Run autoconf to create a new configure.
Do a normal install.
Run regsvr32 on the wineasio.dll.so file in dlls/wineasio.
You may also need to symlink /usr/local/wine/lib/wineasio.dll.so to ~/.wine/drive_c/windows/system32.

Please send feedback to reif@earthlink.net


+ 1973
- 8
wineasio.diff.txt
File diff suppressed because it is too large
View File


Loading…
Cancel
Save