From 176125e71819f1bddd97b51acc3590e648a740f9 Mon Sep 17 00:00:00 2001 From: Joakim B Hernberg Date: Fri, 12 Nov 2010 12:08:57 +0100 Subject: [PATCH] WineASIO 0.0.1 --- Makefile.in | 17 + asio.c | 882 ++++++++++++++++++++++++++++++++++++++++++++++ main.c | 196 +++++++++++ readme.txt | 11 + regsvr.c | 603 +++++++++++++++++++++++++++++++ wineasio.diff.txt | 22 ++ wineasio.spec | 4 + 7 files changed, 1735 insertions(+) create mode 100644 Makefile.in create mode 100644 asio.c create mode 100644 main.c create mode 100644 readme.txt create mode 100644 regsvr.c create mode 100644 wineasio.diff.txt create mode 100644 wineasio.spec diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..50f3784 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,17 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = wineasio.dll +IMPORTLIB = libwineasio.$(IMPLIBEXT) +IMPORTS = winmm ole32 user32 advapi32 kernel32 ntdll +EXTRALIBS = -ldxguid -luuid -ljack + +C_SRCS = \ + asio.c \ + main.c \ + regsvr.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/asio.c b/asio.c new file mode 100644 index 0000000..e2a1f80 --- /dev/null +++ b/asio.c @@ -0,0 +1,882 @@ +/* + * Copyright (C) 2006 Robert Reif + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "mmsystem.h" + +#include "wine/debug.h" + +#include + +#include "asio.h" + +WINE_DEFAULT_DEBUG_CHANNEL(asio); + +/* JACK callback function */ +static int jack_process(jack_nframes_t nframes, void * arg); + +// {48D0C522-BFCC-45cc-8B84-17F25F33E6E8} +static GUID const CLSID_WineASIO = { +0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } }; + +#define twoRaisedTo32 4294967296.0 +#define twoRaisedTo32Reciprocal (1.0 / twoRaisedTo32) + +#define MAX_INPUTS 2 +#define MAX_OUTPUTS 2 + +#ifdef __i386__ /* thiscall functions are i386-specific */ + +#ifdef __GNUC__ +/* GCC erroneously warns that the newly wrapped function + * isn't used, lets help it out of it's thinking + */ +#define SUPPRESS_NOTUSED __attribute__((used)) +#else +#define SUPPRESS_NOTUSED +#endif /* __GNUC__ */ + +#define WRAP_THISCALL(type, func, parm) \ + extern type func parm; \ + __ASM_GLOBAL_FUNC( func, \ + "popl %eax\n\t" \ + "pushl %ecx\n\t" \ + "pushl %eax\n\t" \ + "jmp " __ASM_NAME("__wrapped_" #func) ); \ + SUPPRESS_NOTUSED static type __wrapped_ ## func parm +#else +#define WRAP_THISCALL(functype, function, param) \ + functype function param +#endif + +/***************************************************************************** + * IWineAsio interface + */ +#define INTERFACE IWineASIO +DECLARE_INTERFACE_(IWineASIO,IUnknown) +{ + STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD_(ASIOBool,init)(THIS_ void *sysHandle) PURE; + STDMETHOD_(void,getDriverName)(THIS_ char *name) PURE; + STDMETHOD_(long,getDriverVersion)(THIS) PURE; + STDMETHOD_(void,getErrorMessage)(THIS_ char *string) PURE; + STDMETHOD_(ASIOError,start)(THIS) PURE; + STDMETHOD_(ASIOError,stop)(THIS) PURE; + STDMETHOD_(ASIOError,getChannels)(THIS_ long *numInputChannels, long *numOutputChannels) PURE; + STDMETHOD_(ASIOError,getLatencies)(THIS_ long *inputLatency, long *outputLatency) PURE; + STDMETHOD_(ASIOError,getBufferSize)(THIS_ long *minSize, long *maxSize, long *preferredSize, long *granularity) PURE; + STDMETHOD_(ASIOError,canSampleRate)(THIS_ ASIOSampleRate sampleRate) PURE; + STDMETHOD_(ASIOError,getSampleRate)(THIS_ ASIOSampleRate *sampleRate) PURE; + STDMETHOD_(ASIOError,setSampleRate)(THIS_ ASIOSampleRate sampleRate) PURE; + STDMETHOD_(ASIOError,getClockSources)(THIS_ ASIOClockSource *clocks, long *numSources) PURE; + STDMETHOD_(ASIOError,setClockSource)(THIS_ long reference) PURE; + STDMETHOD_(ASIOError,getSamplePosition)(THIS_ ASIOSamples *sPos, ASIOTimeStamp *tStamp) PURE; + STDMETHOD_(ASIOError,getChannelInfo)(THIS_ ASIOChannelInfo *info) PURE; + STDMETHOD_(ASIOError,createBuffers)(THIS_ ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) PURE; + STDMETHOD_(ASIOError,disposeBuffers)(THIS) PURE; + STDMETHOD_(ASIOError,controlPanel)(THIS) PURE; + STDMETHOD_(ASIOError,future)(THIS_ long selector,void *opt) PURE; + STDMETHOD_(ASIOError,outputReady)(THIS) PURE; +}; +#undef INTERFACE + +typedef struct IWineASIO *LPWINEASIO, **LPLPWINEASIO; + +enum +{ + Init, + Run, + Exit +}; + +struct IWineASIOImpl +{ + /* COM stuff */ + const IWineASIOVtbl *lpVtbl; + LONG ref; + + /* ASIO stuff */ + HWND hwnd; + ASIOSampleRate sample_rate; + long input_latency; + long output_latency; + long block_frames; + ASIOTime asio_time; + long miliseconds; + ASIOTimeStamp system_time; + double sample_position; + ASIOBufferInfo *bufferInfos; + ASIOCallbacks *callbacks; + char error_message[256]; + long in_map[MAX_INPUTS]; + long out_map[MAX_OUTPUTS]; + long num_inputs; + long num_outputs; + short *input_buffers[MAX_INPUTS][2]; + short *output_buffers[MAX_OUTPUTS][2]; + long active_inputs; + long active_outputs; + BOOL time_info_mode; + BOOL tc_read; + + /* JACK stuff */ + jack_port_t *input_port[MAX_INPUTS]; + jack_port_t *output_port[MAX_OUTPUTS]; + jack_client_t *client; + long client_state; + long toggle; +}; + +typedef struct IWineASIOImpl IWineASIOImpl; + +static ULONG WINAPI IWineASIOImpl_AddRef(LPWINEASIO iface) +{ + IWineASIOImpl *This = (IWineASIOImpl *)iface; + ULONG ref = InterlockedIncrement(&(This->ref)); + TRACE("(%p) ref was %ld\n", This, ref - 1); + return ref; +} + +static ULONG WINAPI IWineASIOImpl_Release(LPWINEASIO iface) +{ + IWineASIOImpl *This = (IWineASIOImpl *)iface; + ULONG ref = InterlockedDecrement(&(This->ref)); + TRACE("(%p) ref was %ld\n", This, ref + 1); + + if (!ref) { + jack_client_close(This->client); + TRACE("JACK client closed\n"); + + HeapFree(GetProcessHeap(),0,This); + TRACE("(%p) released\n", This); + } + return ref; +} + +static HRESULT WINAPI IWineASIOImpl_QueryInterface(LPWINEASIO iface, REFIID riid, void** ppvObject) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(riid), ppvObject); + + if (ppvObject == NULL) + return E_INVALIDARG; + + if (IsEqualIID(&CLSID_WineASIO, riid)) + { + IWineASIOImpl_AddRef(iface); + + *ppvObject = This; + + return S_OK; + } + + return E_NOINTERFACE; +} + +WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *sysHandle)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + jack_status_t status; + int i; + TRACE("(%p, %p)\n", iface, sysHandle); + + This->sample_rate = 48000; + This->block_frames = 1024; + This->input_latency = This->block_frames; + This->output_latency = This->block_frames * 2; + This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate); + This->callbacks = NULL; + This->sample_position = 0; + strcpy(This->error_message, "No Error"); + This->num_inputs = 0; + This->num_outputs = 0; + This->active_inputs = 0; + This->active_outputs = 0; + This->toggle = 0; + This->client_state = Init; + This->time_info_mode = FALSE; + This->tc_read = FALSE; + + for (i = 0; i < MAX_INPUTS; i++) + { + This->in_map[i] = 0; + } + + for (i = 0; i < MAX_OUTPUTS; i++) + { + This->out_map[i] = 0; + } + + This->client = jack_client_open("Wine_ASIO_Jack_Client", JackNullOption, &status, NULL); + if (This->client == NULL) + { + WARN("failed ot open jack server\n"); + return ASIOFalse; + } + + TRACE("JACK client opened\n"); + + if (status & JackServerStarted) + TRACE("JACK server started\n"); + + jack_set_process_callback(This->client, jack_process, This); + + This->sample_rate = 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); + + for (i = 0; i < MAX_INPUTS; i++) + { + 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); + if (This->input_port[i] == 0) + break; + + This->num_inputs++; + } + + TRACE("found %ld inputs\n", This->num_inputs); + + for (i = 0; i < MAX_OUTPUTS; i++) + { + 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); + if (This->output_port[i] == 0) + break; + + This->num_outputs++; + } + + TRACE("found %ld outputs\n", This->num_outputs); + + return ASIOTrue; +} + +WRAP_THISCALL( void __stdcall, IWineASIOImpl_getDriverName, (LPWINEASIO iface, char *name)) +{ + TRACE("(%p, %p)\n", iface, name); + strcpy(name, "Wine ASIO"); +} + +WRAP_THISCALL( long __stdcall, IWineASIOImpl_getDriverVersion, (LPWINEASIO iface)) +{ + TRACE("(%p)\n", iface); + return 1; +} + +WRAP_THISCALL( void __stdcall, IWineASIOImpl_getErrorMessage, (LPWINEASIO iface, char *string)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %p)\n", iface, string); + strcpy(string, This->error_message); +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + const char ** ports; + int i; + TRACE("(%p)\n", iface); + + if (This->callbacks) + { + This->sample_position = 0; + This->system_time.lo = 0; + This->system_time.hi = 0; + + if (jack_activate(This->client)) + { + WARN("couldn't activate client\n"); + return ASE_NotPresent; + } + + ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); + + if (ports == NULL) + { + WARN("couldn't get input ports\n"); + return ASE_NotPresent; + } + + for (i = 0; i < This->active_inputs; i++) + { + if (jack_connect(This->client, ports[i], jack_port_name(This->input_port[i]))) + { + WARN("input %d connect failed\n", i); + free(ports); + return ASE_NotPresent; + } + } + + free(ports); + + ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); + + if (ports == NULL) + { + WARN("couldn't get output ports\n"); + return ASE_NotPresent; + } + + for (i = 0; i < This->active_outputs; i++) + { + if (jack_connect(This->client, jack_port_name(This->output_port[i]), ports[i])) + { + WARN("output %d connect failed\n", i); + free(ports); + return ASE_NotPresent; + } + } + + free(ports); + + TRACE("started\n"); + + return ASE_OK; + } + + return ASE_NotPresent; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_stop, (LPWINEASIO iface)) +{ + TRACE("(%p) stub!\n", iface); + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getChannels, (LPWINEASIO iface, long *numInputChannels, long *numOutputChannels)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %p, %p)\n", iface, numInputChannels, numOutputChannels); + + if (numInputChannels) + *numInputChannels = This->num_inputs; + + if (numOutputChannels) + *numOutputChannels = This->num_outputs; + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getLatencies, (LPWINEASIO iface, long *inputLatency, long *outputLatency)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %p, %p)\n", iface, inputLatency, outputLatency); + + if (inputLatency) + *inputLatency = This->input_latency; + + if (outputLatency) + *outputLatency = This->output_latency; + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getBufferSize, (LPWINEASIO iface, long *minSize, long *maxSize, long *preferredSize, long *granularity)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %p, %p, %p, %p)\n", iface, minSize, maxSize, preferredSize, granularity); + + if (minSize) + *minSize = This->block_frames; + + if (maxSize) + *maxSize = This->block_frames; + + if (preferredSize) + *preferredSize = This->block_frames; + + if (granularity) + *granularity = 0; + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_canSampleRate, (LPWINEASIO iface, ASIOSampleRate sampleRate)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %f)\n", iface, sampleRate); + + if (sampleRate == This->sample_rate) + return ASE_OK; + + return ASE_NoClock; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getSampleRate, (LPWINEASIO iface, ASIOSampleRate *sampleRate)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %p)\n", iface, sampleRate); + + if (sampleRate) + *sampleRate = This->sample_rate; + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_setSampleRate, (LPWINEASIO iface, ASIOSampleRate sampleRate)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %f)\n", iface, sampleRate); + + if (sampleRate != This->sample_rate) + return ASE_NoClock; + + if (sampleRate != This->sample_rate) + { + This->sample_rate = sampleRate; + This->asio_time.timeInfo.sampleRate = sampleRate; + This->asio_time.timeInfo.flags |= kSampleRateChanged; + This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate); + + if (This->callbacks && This->callbacks->sampleRateDidChange) + This->callbacks->sampleRateDidChange(This->sample_rate); + } + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getClockSources, (LPWINEASIO iface, ASIOClockSource *clocks, long *numSources)) +{ + TRACE("(%p, %p, %p)\n", iface, clocks, numSources); + + if (clocks && numSources) + { + clocks->index = 0; + clocks->associatedChannel = -1; + clocks->associatedGroup = -1; + clocks->isCurrentSource = ASIOTrue; + strcpy(clocks->name, "Internal"); + + *numSources = 1; + return ASE_OK; + } + + return ASE_InvalidParameter; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_setClockSource, (LPWINEASIO iface, long reference)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %ld)\n", iface, reference); + + if (reference == 0) + { + This->asio_time.timeInfo.flags |= kClockSourceChanged; + + return ASE_OK; + } + + return ASE_NotPresent; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getSamplePosition, (LPWINEASIO iface, ASIOSamples *sPos, ASIOTimeStamp *tStamp)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %p, %p)\n", iface, sPos, tStamp); + + tStamp->lo = This->system_time.lo; + tStamp->hi = This->system_time.hi; + + if (This->sample_position >= twoRaisedTo32) + { + sPos->hi = (unsigned long)(This->sample_position * twoRaisedTo32Reciprocal); + sPos->lo = (unsigned long)(This->sample_position - (sPos->hi * twoRaisedTo32)); + } + else + { + sPos->hi = 0; + sPos->lo = (unsigned long)This->sample_position; + } + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getChannelInfo, (LPWINEASIO iface, ASIOChannelInfo *info)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + int i; + char name[32]; + TRACE("(%p, %p)\n", iface, info); + + if (info->channel < 0 || (info->isInput ? info->channel >= MAX_INPUTS : info->channel >= MAX_OUTPUTS)) + return ASE_InvalidParameter; + + info->type = ASIOSTInt16LSB; + info->channelGroup = 0; + info->isActive = ASIOFalse; + + if (info->isInput) + { + for (i = 0; i < This->active_inputs; i++) + { + if (This->in_map[i] == info->channel) + { + info->isActive = ASIOTrue; + break; + } + } + + snprintf(name, sizeof(name), "Input %ld", info->channel); + } + else + { + for (i = 0; i < This->active_outputs; i++) + { + if (This->out_map[i] == info->channel) + { + info->isActive = ASIOTrue; + break; + } + } + + snprintf(name, sizeof(name), "Output %ld", info->channel); + } + + strcpy(info->name, name); + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_disposeBuffers, (LPWINEASIO iface)) +{ + int i; + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p)\n", iface); + + This->callbacks = NULL; + __wrapped_IWineASIOImpl_stop(iface); + + for (i = 0; i < This->active_inputs; i++) + { + HeapFree(GetProcessHeap(), 0, This->input_buffers[i][0]); + HeapFree(GetProcessHeap(), 0, This->input_buffers[i][1]); + } + + This->active_inputs = 0; + + for (i = 0; i < This->active_outputs; i++) + { + HeapFree(GetProcessHeap(), 0, This->output_buffers[i][0]); + HeapFree(GetProcessHeap(), 0, This->output_buffers[i][1]); + } + + This->active_outputs = 0; + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_createBuffers, (LPWINEASIO iface, ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + ASIOBufferInfo * info = bufferInfos; + int i; + TRACE("(%p, %p, %ld, %ld, %p)\n", iface, bufferInfos, numChannels, bufferSize, callbacks); + + This->active_inputs = 0; + This->active_outputs = 0; + This->block_frames = bufferSize; + This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate); + + for (i = 0; i < numChannels; i++, info++) + { + if (info->isInput) + { + if (info->channelNum < 0 || info->channelNum >= This->num_inputs) + { + WARN("invalid input channel: %ld\n", info->channelNum); + goto ERROR_PARAM; + } + + if (This->active_inputs >= This->num_inputs) + { + WARN("too many inputs\n"); + goto ERROR_PARAM; + } + + This->in_map[This->active_inputs] = info->channelNum; + This->input_buffers[This->active_inputs][0] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short)); + This->input_buffers[This->active_inputs][1] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short)); + if (This->input_buffers[This->active_inputs][0] && This->input_buffers[This->active_inputs][1]) + { + info->buffers[0] = This->input_buffers[This->active_inputs][0]; + info->buffers[1] = This->input_buffers[This->active_inputs][1]; + } + else + { + HeapFree(GetProcessHeap(), 0, This->input_buffers[This->active_inputs][0]); + info->buffers[0] = 0; + info->buffers[1] = 0; + WARN("no input buffer memory\n"); + goto ERROR_MEM; + } + This->active_inputs++; + } + else + { + if (info->channelNum < 0 || info->channelNum >= This->num_outputs) + { + WARN("invalid output channel: %ld\n", info->channelNum); + goto ERROR_PARAM; + } + + if (This->active_outputs >= This->num_outputs) + { + WARN("too many outputs\n"); + goto ERROR_PARAM; + } + + This->out_map[This->active_outputs] = info->channelNum; + This->output_buffers[This->active_outputs][0] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short)); + This->output_buffers[This->active_outputs][1] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short)); + if (This->output_buffers[This->active_outputs][0] && This->output_buffers[This->active_outputs][1]) + { + info->buffers[0] = This->output_buffers[This->active_outputs][0]; + info->buffers[1] = This->output_buffers[This->active_outputs][1]; + } + else + { + HeapFree(GetProcessHeap(), 0, This->output_buffers[This->active_outputs][0]); + info->buffers[0] = 0; + info->buffers[1] = 0; + WARN("no output buffer memory\n"); + goto ERROR_MEM; + } + This->active_outputs++; + } + } + + This->callbacks = callbacks; + + if (This->callbacks->asioMessage) + { + if (This->callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0)) + { + This->time_info_mode = TRUE; + This->asio_time.timeInfo.speed = 1; + This->asio_time.timeInfo.systemTime.hi = 0; + This->asio_time.timeInfo.systemTime.lo = 0; + This->asio_time.timeInfo.samplePosition.hi = 0; + This->asio_time.timeInfo.samplePosition.lo = 0; + This->asio_time.timeInfo.sampleRate = This->sample_rate; + This->asio_time.timeInfo. flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid; + + This->asio_time.timeCode.speed = 1; + This->asio_time.timeCode.timeCodeSamples.hi = 0; + This->asio_time.timeCode.timeCodeSamples.lo = 0; + This->asio_time.timeCode.flags = kTcValid | kTcRunning; + } + else + This->time_info_mode = FALSE; + } + else + { + This->time_info_mode = FALSE; + WARN("asioMessage callback not supplied\n"); + goto ERROR_PARAM; + } + + return ASE_OK; + +ERROR_MEM: + __wrapped_IWineASIOImpl_disposeBuffers(iface); + WARN("no memory\n"); + return ASE_NoMemory; + +ERROR_PARAM: + __wrapped_IWineASIOImpl_disposeBuffers(iface); + WARN("invalid parameter\n"); + return ASE_InvalidParameter; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_controlPanel, (LPWINEASIO iface)) +{ + TRACE("(%p) stub!\n", iface); + + return ASE_OK; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_future, (LPWINEASIO iface, long selector, void *opt)) +{ + IWineASIOImpl * This = (IWineASIOImpl*)iface; + TRACE("(%p, %ld, %p)\n", iface, selector, opt); + + switch (selector) + { + case kAsioEnableTimeCodeRead: + This->tc_read = TRUE; + return ASE_SUCCESS; + case kAsioDisableTimeCodeRead: + This->tc_read = FALSE; + return ASE_SUCCESS; + case kAsioSetInputMonitor: + return ASE_SUCCESS; + case kAsioCanInputMonitor: + return ASE_SUCCESS; + case kAsioCanTimeInfo: + return ASE_SUCCESS; + case kAsioCanTimeCode: + return ASE_SUCCESS; + } + + return ASE_NotPresent; +} + +WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_outputReady, (LPWINEASIO iface)) +{ + TRACE("(%p)\n", iface); + + return ASE_NotPresent; +} + +static const IWineASIOVtbl WineASIO_Vtbl = +{ + IWineASIOImpl_QueryInterface, + IWineASIOImpl_AddRef, + IWineASIOImpl_Release, + IWineASIOImpl_init, + IWineASIOImpl_getDriverName, + IWineASIOImpl_getDriverVersion, + IWineASIOImpl_getErrorMessage, + IWineASIOImpl_start, + IWineASIOImpl_stop, + IWineASIOImpl_getChannels, + IWineASIOImpl_getLatencies, + IWineASIOImpl_getBufferSize, + IWineASIOImpl_canSampleRate, + IWineASIOImpl_getSampleRate, + IWineASIOImpl_setSampleRate, + IWineASIOImpl_getClockSources, + IWineASIOImpl_setClockSource, + IWineASIOImpl_getSamplePosition, + IWineASIOImpl_getChannelInfo, + IWineASIOImpl_createBuffers, + IWineASIOImpl_disposeBuffers, + IWineASIOImpl_controlPanel, + IWineASIOImpl_future, + IWineASIOImpl_outputReady, +}; + +HRESULT asioCreateInstance(REFIID riid, LPVOID *ppobj) +{ + IWineASIOImpl * pobj; + TRACE("(%s, %p)\n", debugstr_guid(riid), ppobj); + + pobj = HeapAlloc(GetProcessHeap(), 0, sizeof(*pobj)); + if (pobj == NULL) { + WARN("out of memory\n"); + return E_OUTOFMEMORY; + } + + pobj->lpVtbl = &WineASIO_Vtbl; + pobj->ref = 1; + TRACE("pobj = %p\n", pobj); + *ppobj = pobj; + TRACE("return %p\n", *ppobj); + return S_OK; +} + +static void getNanoSeconds(ASIOTimeStamp* ts) +{ + double nanoSeconds = (double)((unsigned long)timeGetTime ()) * 1000000.; + ts->hi = (unsigned long)(nanoSeconds / twoRaisedTo32); + ts->lo = (unsigned long)(nanoSeconds - (ts->hi * twoRaisedTo32)); +} + +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); + + if (ts == JackTransportRolling) + { + if (This->client_state == Init) + This->client_state = Run; + + This->sample_position += nframes; + + /* get the input data from JACK and copy it to the ASIO buffers */ + for (i = 0; i < This->active_inputs; i++) + { + short * buffer = This->input_buffers[i][This->toggle]; + + in = jack_port_get_buffer(This->input_port[i], nframes); + + for (j = 0; j < nframes; j++) + buffer[j] = in[j] * 32767.0f; + } + + /* 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); + + /* 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); + + 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; + + /* FIXME why is this needed ? */ + jack_transport_start(This->client); + } + + return 0; +} + diff --git a/main.c b/main.c new file mode 100644 index 0000000..e82ad4c --- /dev/null +++ b/main.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2006 Robert Reif + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "objbase.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(asio); + +// {48D0C522-BFCC-45cc-8B84-17F25F33E6E8} +static GUID const CLSID_WineASIO = { +0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } }; + +typedef struct { + const IClassFactoryVtbl * lpVtbl; + LONG ref; +} IClassFactoryImpl; + +extern HRESULT asioCreateInstance(REFIID riid, LPVOID *ppobj); + +/******************************************************************************* + * ClassFactory + */ + +static HRESULT WINAPI +CF_QueryInterface(LPCLASSFACTORY iface, REFIID riid, LPVOID *ppobj) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + FIXME("(%p, %s, %p) stub!\n", This, debugstr_guid(riid), ppobj); + if (ppobj == NULL) + return E_POINTER; + return E_NOINTERFACE; +} + +static ULONG WINAPI CF_AddRef(LPCLASSFACTORY iface) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + ULONG ref = InterlockedIncrement(&(This->ref)); + TRACE("(%p) ref was %ld\n", This, ref - 1); + return ref; +} + +static ULONG WINAPI CF_Release(LPCLASSFACTORY iface) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + ULONG ref = InterlockedDecrement(&(This->ref)); + TRACE("(%p) ref was %ld\n", This, ref + 1); + /* static class, won't be freed */ + return ref; +} + +static HRESULT WINAPI CF_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pOuter, + REFIID riid, + LPVOID *ppobj) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj); + + if (pOuter) + return CLASS_E_NOAGGREGATION; + + if (ppobj == NULL) { + WARN("invalid parameter\n"); + return E_INVALIDARG; + } + *ppobj = NULL; + return asioCreateInstance(riid, ppobj); +} + +static HRESULT WINAPI CF_LockServer(LPCLASSFACTORY iface, BOOL dolock) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + FIXME("(%p, %d) stub!\n", This, dolock); + return S_OK; +} + +static const IClassFactoryVtbl CF_Vtbl = { + CF_QueryInterface, + CF_AddRef, + CF_Release, + CF_CreateInstance, + CF_LockServer +}; + +static IClassFactoryImpl WINEASIO_CF = { &CF_Vtbl, 1 }; + +/******************************************************************************* + * DllGetClassObject [DSOUND.@] + * Retrieves class object from a DLL object + * + * NOTES + * Docs say returns STDAPI + * + * PARAMS + * rclsid [I] CLSID for the class object + * riid [I] Reference to identifier of interface for class object + * ppv [O] Address of variable to receive interface pointer for riid + * + * RETURNS + * Success: S_OK + * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG, + * E_UNEXPECTED + */ +HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); + + if (ppv == NULL) { + WARN("invalid parameter\n"); + return E_INVALIDARG; + } + + *ppv = NULL; + + if (!IsEqualIID(riid, &IID_IClassFactory) && + !IsEqualIID(riid, &IID_IUnknown)) { + WARN("no interface for %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + + if (IsEqualGUID(rclsid, &CLSID_WineASIO)) { + CF_AddRef((IClassFactory*) &WINEASIO_CF); + *ppv = &WINEASIO_CF; + return S_OK; + } + + WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid), + debugstr_guid(riid), ppv); + return CLASS_E_CLASSNOTAVAILABLE; +} + + +/******************************************************************************* + * DllCanUnloadNow + * Determines whether the DLL is in use. + * + * RETURNS + * Success: S_OK + * Failure: S_FALSE + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + FIXME("(void): stub\n"); + return S_FALSE; +} + +/*********************************************************************** + * DllMain (ASIO.init) + */ +BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + TRACE("(%p %ld %p)\n", hInstDLL, fdwReason, lpvReserved); + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + TRACE("DLL_PROCESS_ATTACH\n"); + break; + case DLL_PROCESS_DETACH: + TRACE("DLL_PROCESS_DETACH\n"); + break; + case DLL_THREAD_ATTACH: + TRACE("DLL_THREAD_ATTACH\n"); + break; + case DLL_THREAD_DETACH: + TRACE("DLL_THREAD_DETACH\n"); + break; + default: + TRACE("UNKNOWN REASON\n"); + break; + } + return TRUE; +} + diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..b737d14 --- /dev/null +++ b/readme.txt @@ -0,0 +1,11 @@ +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 + diff --git a/regsvr.c b/regsvr.c new file mode 100644 index 0000000..3c13503 --- /dev/null +++ b/regsvr.c @@ -0,0 +1,603 @@ +/* + * self-registerable dll functions for wineasio.dll + * + * Copyright (C) 2003 John K. Hohm + * Copyright (C) 2006 Robert Reif + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#define NONAMELESSSTRUCT +#define NONAMELESSUNION +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "objbase.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(asio); + +/* + * Near the bottom of this file are the exported DllRegisterServer and + * DllUnregisterServer, which make all this worthwhile. + */ + +/*********************************************************************** + * interface for self-registering + */ +struct regsvr_interface +{ + IID const *iid; /* NULL for end of list */ + LPCSTR name; /* can be NULL to omit */ + IID const *base_iid; /* can be NULL to omit */ + int num_methods; /* can be <0 to omit */ + CLSID const *ps_clsid; /* can be NULL to omit */ + CLSID const *ps_clsid32; /* can be NULL to omit */ +}; + +static HRESULT register_interfaces(struct regsvr_interface const *list); +static HRESULT unregister_interfaces(struct regsvr_interface const *list); + +struct regsvr_coclass +{ + CLSID const *clsid; /* NULL for end of list */ + LPCSTR name; /* can be NULL to omit */ + LPCSTR ips; /* can be NULL to omit */ + LPCSTR ips32; /* can be NULL to omit */ + LPCSTR ips32_tmodel; /* can be NULL to omit */ + LPCSTR progid; /* can be NULL to omit */ + LPCSTR viprogid; /* can be NULL to omit */ + LPCSTR progid_extra; /* can be NULL to omit */ +}; + +static HRESULT register_coclasses(struct regsvr_coclass const *list); +static HRESULT unregister_coclasses(struct regsvr_coclass const *list); + +/*********************************************************************** + * static string constants + */ +static WCHAR const interface_keyname[10] = { + 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 }; +static WCHAR const base_ifa_keyname[14] = { + 'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', + 'e', 0 }; +static WCHAR const num_methods_keyname[11] = { + 'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 }; +static WCHAR const ps_clsid_keyname[15] = { + 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's', + 'i', 'd', 0 }; +static WCHAR const ps_clsid32_keyname[17] = { + 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's', + 'i', 'd', '3', '2', 0 }; +static WCHAR const clsid_keyname[6] = { + 'C', 'L', 'S', 'I', 'D', 0 }; +static WCHAR const curver_keyname[7] = { + 'C', 'u', 'r', 'V', 'e', 'r', 0 }; +static WCHAR const ips_keyname[13] = { + 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r', + 0 }; +static WCHAR const ips32_keyname[15] = { + 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r', + '3', '2', 0 }; +static WCHAR const progid_keyname[7] = { + 'P', 'r', 'o', 'g', 'I', 'D', 0 }; +static WCHAR const viprogid_keyname[25] = { + 'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p', + 'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D', + 0 }; +static char const tmodel_valuename[] = "ThreadingModel"; + +/*********************************************************************** + * static helper functions + */ +static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid); +static LONG register_key_defvalueW(HKEY base, WCHAR const *name, + WCHAR const *value); +static LONG register_key_defvalueA(HKEY base, WCHAR const *name, + char const *value); +static LONG register_progid(WCHAR const *clsid, + char const *progid, char const *curver_progid, + char const *name, char const *extra); +static LONG recursive_delete_key(HKEY key); +static LONG recursive_delete_keyA(HKEY base, char const *name); +static LONG recursive_delete_keyW(HKEY base, WCHAR const *name); + +/*********************************************************************** + * register_interfaces + */ +static HRESULT register_interfaces(struct regsvr_interface const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY interface_key; + + res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &interface_key, NULL); + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->iid; ++list) { + WCHAR buf[39]; + HKEY iid_key; + + StringFromGUID2(list->iid, buf, 39); + res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &iid_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_interface_key; + + if (list->name) { + res = RegSetValueExA(iid_key, NULL, 0, REG_SZ, + (CONST BYTE*)(list->name), + strlen(list->name) + 1); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->base_iid) { + res = register_key_guid(iid_key, base_ifa_keyname, list->base_iid); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (0 <= list->num_methods) { + static WCHAR const fmt[3] = { '%', 'd', 0 }; + HKEY key; + + res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + + wsprintfW(buf, fmt, list->num_methods); + res = RegSetValueExW(key, NULL, 0, REG_SZ, + (CONST BYTE*)buf, + (lstrlenW(buf) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->ps_clsid) { + res = register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->ps_clsid32) { + res = register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + error_close_iid_key: + RegCloseKey(iid_key); + } + +error_close_interface_key: + RegCloseKey(interface_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * unregister_interfaces + */ +static HRESULT unregister_interfaces(struct regsvr_interface const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY interface_key; + + res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, + KEY_READ | KEY_WRITE, &interface_key); + if (res == ERROR_FILE_NOT_FOUND) return S_OK; + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->iid; ++list) { + WCHAR buf[39]; + + StringFromGUID2(list->iid, buf, 39); + res = recursive_delete_keyW(interface_key, buf); + } + + RegCloseKey(interface_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * register_coclasses + */ +static HRESULT register_coclasses(struct regsvr_coclass const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY coclass_key; + + res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL); + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->clsid; ++list) { + WCHAR buf[39]; + HKEY clsid_key; + + StringFromGUID2(list->clsid, buf, 39); + res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + + if (list->name) { + res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ, + (CONST BYTE*)(list->name), + strlen(list->name) + 1); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->ips) { + res = register_key_defvalueA(clsid_key, ips_keyname, list->ips); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->ips32) { + HKEY ips32_key; + + res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, + &ips32_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ, + (CONST BYTE*)list->ips32, + lstrlenA(list->ips32) + 1); + if (res == ERROR_SUCCESS && list->ips32_tmodel) + res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ, + (CONST BYTE*)list->ips32_tmodel, + strlen(list->ips32_tmodel) + 1); + RegCloseKey(ips32_key); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->progid) { + res = register_key_defvalueA(clsid_key, progid_keyname, + list->progid); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = register_progid(buf, list->progid, NULL, + list->name, list->progid_extra); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->viprogid) { + res = register_key_defvalueA(clsid_key, viprogid_keyname, + list->viprogid); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = register_progid(buf, list->viprogid, list->progid, + list->name, list->progid_extra); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + error_close_clsid_key: + RegCloseKey(clsid_key); + } + +error_close_coclass_key: + RegCloseKey(coclass_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * unregister_coclasses + */ +static HRESULT unregister_coclasses(struct regsvr_coclass const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY coclass_key; + + res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, + KEY_READ | KEY_WRITE, &coclass_key); + if (res == ERROR_FILE_NOT_FOUND) return S_OK; + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->clsid; ++list) { + WCHAR buf[39]; + + StringFromGUID2(list->clsid, buf, 39); + res = recursive_delete_keyW(coclass_key, buf); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + + if (list->progid) { + res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + } + + if (list->viprogid) { + res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + } + } + +error_close_coclass_key: + RegCloseKey(coclass_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * regsvr_key_guid + */ +static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) +{ + WCHAR buf[39]; + + StringFromGUID2(guid, buf, 39); + return register_key_defvalueW(base, name, buf); +} + +/*********************************************************************** + * regsvr_key_defvalueW + */ +static LONG register_key_defvalueW( + HKEY base, + WCHAR const *name, + WCHAR const *value) +{ + LONG res; + HKEY key; + + res = RegCreateKeyExW(base, name, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) return res; + res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value, + (lstrlenW(value) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * regsvr_key_defvalueA + */ +static LONG register_key_defvalueA( + HKEY base, + WCHAR const *name, + char const *value) +{ + LONG res; + HKEY key; + + res = RegCreateKeyExW(base, name, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) return res; + res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value, + lstrlenA(value) + 1); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * regsvr_progid + */ +static LONG register_progid( + WCHAR const *clsid, + char const *progid, + char const *curver_progid, + char const *name, + char const *extra) +{ + LONG res; + HKEY progid_key; + + res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0, + NULL, 0, KEY_READ | KEY_WRITE, NULL, + &progid_key, NULL); + if (res != ERROR_SUCCESS) return res; + + if (name) { + res = RegSetValueExA(progid_key, NULL, 0, REG_SZ, + (CONST BYTE*)name, strlen(name) + 1); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (clsid) { + res = register_key_defvalueW(progid_key, clsid_keyname, clsid); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (curver_progid) { + res = register_key_defvalueA(progid_key, curver_keyname, + curver_progid); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (extra) { + HKEY extra_key; + + res = RegCreateKeyExA(progid_key, extra, 0, + NULL, 0, KEY_READ | KEY_WRITE, NULL, + &extra_key, NULL); + if (res == ERROR_SUCCESS) + RegCloseKey(extra_key); + } + +error_close_progid_key: + RegCloseKey(progid_key); + return res; +} + +/*********************************************************************** + * recursive_delete_key + */ +static LONG recursive_delete_key(HKEY key) +{ + LONG res; + WCHAR subkey_name[MAX_PATH]; + DWORD cName; + HKEY subkey; + + for (;;) { + cName = sizeof(subkey_name) / sizeof(WCHAR); + res = RegEnumKeyExW(key, 0, subkey_name, &cName, + NULL, NULL, NULL, NULL); + if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) { + res = ERROR_SUCCESS; /* presumably we're done enumerating */ + break; + } + res = RegOpenKeyExW(key, subkey_name, 0, + KEY_READ | KEY_WRITE, &subkey); + if (res == ERROR_FILE_NOT_FOUND) continue; + if (res != ERROR_SUCCESS) break; + + res = recursive_delete_key(subkey); + RegCloseKey(subkey); + if (res != ERROR_SUCCESS) break; + } + + if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0); + return res; +} + +/*********************************************************************** + * recursive_delete_keyA + */ +static LONG recursive_delete_keyA(HKEY base, char const *name) +{ + LONG res; + HKEY key; + + res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key); + if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; + if (res != ERROR_SUCCESS) return res; + res = recursive_delete_key(key); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * recursive_delete_keyW + */ +static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) +{ + LONG res; + HKEY key; + + res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key); + if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; + if (res != ERROR_SUCCESS) return res; + res = recursive_delete_key(key); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * coclass list + */ +// {48D0C522-BFCC-45cc-8B84-17F25F33E6E8} +static GUID const CLSID_WineASIO = { +0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } }; + +static struct regsvr_coclass const coclass_list[] = { + { &CLSID_WineASIO, + "Wine ASIO Object", + NULL, + "wineasio.dll", + "Apartment" + }, + { NULL } /* list terminator */ +}; + +/*********************************************************************** + * interface list + */ + +static struct regsvr_interface const interface_list[] = { + { NULL } /* list terminator */ +}; + +/*********************************************************************** + * register driver + */ +static HRESULT register_driver(void) +{ + LPCSTR asio_key = "Software\\ASIO\\Wine ASIO"; + LPCSTR clsid = "CLSID"; + LPCSTR wine_clsid = "{48D0C522-BFCC-45cc-8B84-17F25F33E6E8}"; + LPCSTR desc = "Description"; + LPCSTR wine_desc = "Wine ASIO Driver"; + HKEY key; + LONG rc; + + rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, asio_key, 0, KEY_READ | KEY_WRITE, &key); + + if (rc != ERROR_SUCCESS) + rc = RegCreateKeyExA(HKEY_LOCAL_MACHINE, asio_key, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &key, 0); + + if (rc == ERROR_SUCCESS) + { + rc = RegSetValueExA(key, clsid, 0, REG_SZ, wine_clsid, strlen(wine_clsid) + 1); + + if (rc == ERROR_SUCCESS) + rc = RegSetValueExA(key, desc, 0, REG_SZ, wine_desc, strlen(wine_desc) + 1); + + RegCloseKey(key); + } + + return rc; +} + +/*********************************************************************** + * DllRegisterServer (wineasio.@) + */ +HRESULT WINAPI DllRegisterServer(void) +{ + HRESULT hr; + + TRACE("\n"); + + hr = register_coclasses(coclass_list); + if (SUCCEEDED(hr)) + { + hr = register_interfaces(interface_list); + if (SUCCEEDED(hr)) + hr = register_driver(); + } + return hr; +} + +/*********************************************************************** + * register driver + */ +static HRESULT unregister_driver(void) +{ + LPCSTR asio_key = "Software\\ASIO\\Wine ASIO"; + + /* FIXME */ + return recursive_delete_keyA(HKEY_LOCAL_MACHINE, asio_key); +} + +/*********************************************************************** + * DllUnregisterServer (wineasio.@) + */ +HRESULT WINAPI DllUnregisterServer(void) +{ + HRESULT hr; + + TRACE("\n"); + + hr = unregister_coclasses(coclass_list); + if (SUCCEEDED(hr)) + { + hr = unregister_interfaces(interface_list); + if (SUCCEEDED(hr)) + hr = unregister_driver(); + } + return hr; +} diff --git a/wineasio.diff.txt b/wineasio.diff.txt new file mode 100644 index 0000000..70066ce --- /dev/null +++ b/wineasio.diff.txt @@ -0,0 +1,22 @@ +diff -p -u -r1.553 configure.ac +--- configure.ac 29 Aug 2006 12:20:36 -0000 1.553 ++++ configure.ac 1 Sep 2006 02:31:19 -0000 +@@ -1714,6 +1714,7 @@ dlls/vnetbios.vxd/Makefile + dlls/vtdapi.vxd/Makefile + dlls/vwin32.vxd/Makefile + dlls/w32skrnl/Makefile ++dlls/wineasio/Makefile + dlls/winecrt0/Makefile + dlls/wined3d/Makefile + dlls/winedos/Makefile +diff -p -u -r1.320 Makefile.in +--- dlls/Makefile.in 29 Aug 2006 12:20:36 -0000 1.320 ++++ dlls/Makefile.in 1 Sep 2006 02:31:19 -0000 +@@ -174,6 +174,7 @@ BASEDIRS = \ + vtdapi.vxd \ + vwin32.vxd \ + w32skrnl \ ++ wineasio \ + winedos \ + winemp3.acm \ + wineps.drv \ diff --git a/wineasio.spec b/wineasio.spec new file mode 100644 index 0000000..152b4ec --- /dev/null +++ b/wineasio.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllRegisterServer() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllUnregisterServer()