rendered paste body/* * Copyright (C) 2006 Robert Reif * Portions copyright (C) 2007 Ralf Beck * Portions copyright (C) 2007 Peter L Jones * * 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 "port.h"#include "settings.h"#include <stdio.h>#include <dlfcn.h>#include <sys/time.h>#include <stdlib.h>#include <wine/windows/windef.h>#include <wine/windows/winbase.h>#include <wine/windows/objbase.h>#include <wine/windows/mmsystem.h>#include <sched.h>#include <pthread.h>#include <semaphore.h>#include "wine/library.h"#include "wine/debug.h"#include <jack/jack.h>#include <jack/thread.h>#define IEEE754_64FLOAT 1#include "asio.h"WINE_DEFAULT_DEBUG_CHANNEL(asio);/* JACK callback function */static int jack_process(jack_nframes_t nframes, void * arg);/* 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 } };#define twoRaisedTo32 4294967296.0#define twoRaisedTo32Reciprocal (1.0 / twoRaisedTo32)/* 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__/* GCC erroneously warns that the newly wrapped function * isn't used, let us help it out of its 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 IWineASIODECLARE_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 INTERFACEtypedef struct IWineASIO *LPWINEASIO, **LPLPWINEASIO;enum{ Init, Run, Exit};typedef struct _Channel { ASIOBool active; int *buffer; const char *port_name; jack_port_t *port;} Channel;typedef struct sched_param SCHED_PARAM;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 active_inputs; long active_outputs; BOOL time_info_mode; BOOL tc_read; long state; /* JACK stuff */ char *client_name; unsigned int num_inputs; unsigned int num_outputs; jack_client_t *client; long toggle; SCHED_PARAM jack_client_priority; /* callback stuff */ HANDLE thread; HANDLE start_event; HANDLE stop_event; DWORD thread_id; sem_t semaphore1; sem_t semaphore2; BOOL terminate; Channel *input; Channel *output;};typedef struct IWineASIOImpl IWineASIOImpl;static ULONG WINAPI IWineASIOImpl_AddRef(LPWINEASIO iface){ IWineASIOImpl *This = (IWineASIOImpl *)iface; ULONG ref = InterlockedIncrement(&(This->ref)); TRACE("(%p)\n", iface); TRACE("(%p) ref was %d\n", This, ref - 1); return ref;}static ULONG WINAPI IWineASIOImpl_Release(LPWINEASIO iface){ IWineASIOImpl *This = (IWineASIOImpl *)iface; ULONG ref = InterlockedDecrement(&(This->ref)); TRACE("(%p)\n", iface); TRACE("(%p) ref was %d\n", This, ref + 1); if (!ref) { jack_client_close(This->client); TRACE("JACK client closed\n"); 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); } 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;}static void set_clientname(IWineASIOImpl *This){ FILE *cmd; char *cmdline = NULL; char *ptr, *line = NULL; size_t len = 0; asprintf(&cmdline, "/proc/%d/cmdline", getpid()); cmd = fopen(cmdline, "r"); free(cmdline); getline(&line, &len, cmd); fclose(cmd); ptr = line; while(strchr(ptr, '/') || strchr(ptr, '\\')) ++ptr; line = ptr; ptr = strcasestr(line, ".exe"); if (ptr) { while (strcasestr(ptr, ".exe")) ++ptr; *(--ptr) = '\0'; } asprintf(&This->client_name, "%s_%s", DEFAULT_PREFIX, line);}static void read_config(IWineASIOImpl* This){ char *usercfg = NULL; FILE *cfg; asprintf(&usercfg, "%s/%s", getenv("HOME"), USERCFG); cfg = fopen(usercfg, "r"); if (cfg) TRACE("Config: %s\n", usercfg); else { cfg = fopen(SITECFG, "r"); if (cfg) TRACE("Config: %s\n", SITECFG); } free(usercfg); if (cfg) { char *line = NULL; size_t len = 0; ssize_t read; line = NULL; len = 0; while( (read = getline(&line, &len, cfg)) != -1) { while (isspace(line[--read])) line[read]='\0'; if ((strstr(line, ENVVAR_INPUTS) || strstr(line, ENVVAR_OUTPUTS) || strstr(line, ENVVAR_INPORTNAMEPREFIX) || strstr(line, ENVVAR_OUTPORTNAMEPREFIX) || strstr(line, ENVVAR_INMAP) || strstr(line, ENVVAR_OUTMAP) || strstr(line, This->client_name) == line ) && strchr(line, '=')) { TRACE("(%p) env: '%s'\n", This, line); putenv(line); } else { free(line); } line = NULL; len = 0; } fclose(cfg); }}static int get_numChannels(IWineASIOImpl *This, const char* inout, int defval){ int i = defval; char *envv = NULL, *envi; asprintf(&envv, "%s%s", This->client_name, inout); envi = getenv(envv); free(envv); if (envi == NULL) { asprintf(&envv, "%s%s", DEFAULT_PREFIX, inout); envi = getenv(envv); free(envv); } if (envi != NULL) i = atoi(envi); return i;}static void set_portname(IWineASIOImpl *This, const char* inout, const char* defname, int i, Channel c[]){ char *envv = NULL, *envi; asprintf(&envv, "%s_%s%d", This->client_name, inout, i); envi = getenv(envv); free(envv); if (envi == NULL) { envv = NULL; asprintf(&envv, "%s_%s%d", DEFAULT_PREFIX, inout, i); envi = getenv(envv); free(envv); } if (envi == NULL) asprintf(&envv, "%s%d", defname, i+1); else asprintf(&envv, "%s", envi); c[i].port_name = strdup(envv); free(envv);}WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *sysHandle)){ IWineASIOImpl *This = (IWineASIOImpl *)iface; jack_status_t status; int i; char *envi; TRACE("(%p, %p)\n", iface, sysHandle); This->sample_rate = 48000.0; This->block_frames = 1024; This->input_latency = This->block_frames; This->output_latency = This->block_frames; 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->time_info_mode = FALSE; This->tc_read = FALSE; This->terminate = FALSE; This->state = Init; This->jack_client_priority.sched_priority = -1; 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); set_clientname(This); // uses This->client_name read_config(This); envi = getenv(This->client_name); if (envi != NULL) { free(This->client_name); This->client_name = strdup(envi); } This->client = jack_client_open(This->client_name, JackNullOption, &status, NULL); if (This->client == NULL) { WARN("(%p) failed to open jack server\n", This); return ASIOFalse; } TRACE("JACK client opened, client name: '%s'\n", jack_get_client_name(This->client)); if (status & JackServerStarted) TRACE("(%p) JACK server started\n", This); // get maximum reccomended client priority from JACK This->jack_client_priority.sched_priority = jack_client_real_time_priority (This->client); 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("(%p) Couldn't create thread\n", This); return ASIOFalse; }// if (status & JackNameNotUnique)// {// strcpy(This->client_name, jack_get_client_name(This->client));// TRACE("unique name `%s' assigned\n", This->clientname);// } jack_set_process_callback(This->client, jack_process, This); This->sample_rate = jack_get_sample_rate(This->client); This->block_frames = jack_get_buffer_size(This->client); This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate); This->input_latency = This->block_frames; This->output_latency = This->block_frames;// TRACE("sample rate: %f\n", This->sample_rate);// TRACE("buffer size: %ld\n", This->block_frames); This->active_inputs = 0; This->num_inputs = get_numChannels(This, ENVVAR_INPUTS, DEFAULT_NUMINPUTS); This->input = HeapAlloc(GetProcessHeap(), 0, sizeof(Channel) * This->num_inputs); if (!This->input) { MESSAGE("(%p) Not enough memory for %d input channels\n", This, This->num_inputs); return ASIOFalse; } TRACE("(%p) Max inputs: %d\n", This, This->num_inputs); for (i = 0; i < This->num_inputs; i++) { This->input[i].active = ASIOFalse; This->input[i].buffer = NULL; set_portname(This, ENVVAR_INPORTNAMEPREFIX, DEFAULT_INPORT, i, This->input); TRACE("(%p) input %d: '%s'\n", This, i, This->input[i].port_name); This->input[i].port = NULL; } This->active_outputs = 0; This->num_outputs = get_numChannels(This, ENVVAR_OUTPUTS, DEFAULT_NUMOUTPUTS); This->output = HeapAlloc(GetProcessHeap(), 0, sizeof(Channel) * This->num_outputs); if (!This->output) { MESSAGE("(%p) Not enough memory for %d output channels\n", This, This->num_outputs); return ASIOFalse; } TRACE("(%p) Max outputs: %d\n", This, This->num_outputs); for (i = 0; i < This->num_outputs; i++) { This->output[i].active = ASIOFalse; This->output[i].buffer = NULL; set_portname(This, ENVVAR_OUTPORTNAMEPREFIX, DEFAULT_OUTPORT, i, This->output); TRACE("(%p) output %d: '%s'\n", This, i, This->output[i].port_name); This->output[i].port = NULL; } 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 74; // 0.7 (patch level 4)}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);}static char* get_targetname(IWineASIOImpl* This, const char* inout, int i){ char* envv = NULL, *envi; asprintf(&envv, "%s%s%d", This->client_name, inout, i); envi = getenv(envv); free(envv); if (envi == NULL) { asprintf(&envv, "%s%s%d", DEFAULT_PREFIX, inout, i); envi = getenv(envv); free(envv); } return envi;}WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface)){ IWineASIOImpl * This = (IWineASIOImpl*)iface; char *envi; const char ** ports; int numports; int i, j; 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; } // get list of port names ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); for(numports = 0; ports && ports[numports]; numports++); TRACE("(%p) inputs desired: %d; JACK outputs: %d\n", This, This->num_inputs, numports); for (i = j = 0; i < This->num_inputs; i++) { if (This->input[i].active != ASIOTrue) continue; This->input[i].port = jack_port_register(This->client, This->input[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, i); if (This->input[i].port) TRACE("(%p) Registered input port %i: '%s' (%p)\n", This, i, This->input[i].port_name, This->input[i].port); else { MESSAGE("(%p) Failed to register input port %i ('%s')\n", This, i, This->input[i].port_name); return ASE_NotPresent; } if (j < numports) { // Get the desired JACK output (source) name, if there is one, for this ASIO input envi = get_targetname(This, ENVVAR_INMAP, i); TRACE("(%p) %d: Connect JACK output '%s' to my input '%s'\n", This, i ,(envi ? envi : ports[j]) ,jack_port_name(This->input[i].port) ); if (jack_connect(This->client ,(envi ? envi : ports[j++]) ,jack_port_name(This->input[i].port))) { MESSAGE("(%p) Connect failed\n", This); } } } if (ports) free(ports); // get list of port names ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); for(numports = 0; ports && ports[numports]; numports++); TRACE("(%p) JACK inputs: %d; outputs desired: %d\n", This, numports, This->num_outputs); for (i = j = 0; i < This->num_outputs; i++) { if (This->output[i].active != ASIOTrue) continue; This->output[i].port = jack_port_register(This->client, This->output[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, i); if (This->output[i].port) TRACE("(%p) Registered output port %i: '%s' (%p)\n", This, i, This->output[i].port_name, This->output[i].port); else { MESSAGE("(%p) Failed to register output port %i ('%s')\n", This, i, This->output[i].port_name); return ASE_NotPresent; } if (j < numports) { // Get the desired JACK input (target) name, if there is one, for this ASIO output envi = get_targetname(This, ENVVAR_OUTMAP, i); TRACE("(%p) %d: Connect my output '%s' to JACK input '%s'\n", This, i ,jack_port_name(This->output[i].port) ,(envi ? envi : ports[j]) ); if (jack_connect(This->client ,jack_port_name(This->output[i].port) ,(envi ? envi : ports[j++]))) { MESSAGE("(%p) Connect failed\n", This); } } } if (ports) free(ports); This->state = Run; TRACE("started\n"); return ASE_OK; } return ASE_NotPresent;}WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_stop, (LPWINEASIO iface)){ int i; IWineASIOImpl * This = (IWineASIOImpl*)iface; TRACE("(%p)\n", iface); This->state = Exit; for (i = 0; i < This->num_inputs; i++) { if (This->input[i].port == NULL) { TRACE("(%p) Not registered input port %i\n", This, i); continue; } if (jack_port_unregister(This->client, This->input[i].port)) { TRACE("(%p) Unregistered input port %i: '%s' (%p)\n", This, i, This->input[i].port_name, This->input[i].port); } } for (i = 0; i < This->num_outputs; i++) { if (This->output[i].port == NULL) { TRACE("(%p) Not registered output port %i\n", This, i); continue; } if (jack_port_unregister(This->client, This->output[i].port)) TRACE("(%p) Unregistered output port %i: '%s' (%p)\n", This, i, This->output[i].port_name, This->output[i].port); } if (jack_deactivate(This->client)) { WARN("couldn't deactivate client\n"); return ASE_NotPresent; } 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; TRACE("inputs: %d outputs: %d\n", This->num_inputs, 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; TRACE("min: %ld max: %ld preferred: %ld granularity: 0\n", This->block_frames, This->block_frames, This->block_frames); 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; TRACE("rate: %f\n", 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; 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; TRACE("(%p, %p)\n", iface, info); if (info->channel < 0 || (info->isInput ? info->channel >= This->num_inputs : info->channel >= This->num_outputs)) return ASE_InvalidParameter;// TRACE("info->channel = %ld\n", info->channel);// TRACE("info->isInput = %ld\n", info->isInput); info->type = ASIOSTInt32LSB; // info->type = This->sample_type; info->channelGroup = 0; if (info->isInput) { info->isActive = This->input[info->channel].active; strcpy(info->name, This->input[info->channel].port_name); } else { info->isActive = This->output[info->channel].active; strcpy(info->name, This->output[info->channel].port_name); }// TRACE("info->isActive = %ld\n", info->isActive);// TRACE("info->channelGroup = %ld\n", info->channelGroup);// TRACE("info->type = %ld\n", info->type);// TRACE("info->name = %s\n", info->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 = This->active_inputs; --i >= 0; ) { if (This->input[i].active) { HeapFree(GetProcessHeap(), 0, This->input[i].buffer); This->input[i].buffer = 0; This->input[i].active = ASIOFalse; } This->active_inputs--; } for (i = This->active_outputs; --i >= 0; ) { if (This->output[i].active) { HeapFree(GetProcessHeap(), 0, This->output[i].buffer); This->output[i].buffer = 0; This->output[i].active = ASIOFalse; } This->active_outputs--; } 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, j; TRACE("(%p, %p, %ld, %ld, %p)\n", iface, bufferInfos, numChannels, bufferSize, callbacks); // Just to be on the safe side: This->active_inputs = 0; for(i = 0; i < This->num_inputs; i++) This->input[i].active = ASIOFalse; This->active_outputs = 0; for(i = 0; i < This->num_outputs; i++) This->output[i].active = ASIOFalse; 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; }// ASIOSTInt32LSB support only This->input[This->active_inputs].buffer = HeapAlloc(GetProcessHeap(), 0, 2 * This->block_frames * sizeof(int)); if (This->input[This->active_inputs].buffer) { info->buffers[0] = &This->input[This->active_inputs].buffer[0]; info->buffers[1] = &This->input[This->active_inputs].buffer[This->block_frames]; This->input[This->active_inputs].active = ASIOTrue; for (j = 0; j < This->block_frames * 2; j++) This->input[This->active_inputs].buffer[j] = 0; } else { HeapFree(GetProcessHeap(), 0, This->input[This->active_inputs].buffer); 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; }// ASIOSTInt32LSB support only This->output[This->active_outputs].buffer = HeapAlloc(GetProcessHeap(), 0, 2 * This->block_frames * sizeof(int)); if (This->output[This->active_outputs].buffer) { info->buffers[0] = &This->output[This->active_outputs].buffer[0]; info->buffers[1] = &This->output[This->active_outputs].buffer[This->block_frames]; This->output[This->active_outputs].active = ASIOTrue; for (j = 0; j < This->block_frames * 2; j++) This->output[This->active_outputs].buffer[j] = 0; } else { HeapFree(GetProcessHeap(), 0, This->output[This->active_inputs].buffer); info->buffers[0] = 0; info->buffers[1] = 0; WARN("no input 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)){ char* arg_list[] = { "qjackctl", NULL }; TRACE ("Opening ASIO control panel\n"); if (fork() == 0) { /* Code only executed by child process */ TRACE ("Calling qjackctl\n"); execvp (arg_list[0], arg_list); return ASE_OK; } 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_position_t transport; // ASIOSTInt32LSB support only int *buffer; if (This->state != Run) return 0; ts = jack_transport_query(This->client, &transport); This->sample_position = transport.frame; /* get the input data from JACK and copy it to the ASIO buffers */ for (i = 0; i < This->active_inputs; i++) { if (This->input[i].active == ASIOTrue) { buffer = &This->input[i].buffer[This->block_frames * This->toggle]; in = jack_port_get_buffer(This->input[i].port, nframes); for (j = 0; j < nframes; j++) buffer[j] = (int)(in[j] * (float)(0x7fffffff)); } } /* wake up the WIN32 thread so it can do its 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->num_outputs; i++) { if (This->output[i].active == ASIOTrue) { buffer = &This->output[i].buffer[This->block_frames * (This->toggle)]; out = jack_port_get_buffer(This->output[i].port, nframes); for (j = 0; j < nframes; j++) out[j] = ((float)(buffer[j]) / (float)(0x7fffffff)); } } This->toggle = This->toggle ? 0 : 1; 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); TRACE ("win32 callback thread started\n"); /* set the priority of the win32 callback thread as suggested by JACK */ if (This->jack_client_priority.sched_priority != -1) /* skip if not running realtime */ if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &This->jack_client_priority) == 0) TRACE ("win32 callback set to SCHED_FIFO priority %d\n", This->jack_client_priority.sched_priority); else TRACE ("Error trying to set realtime priority of win32 callback\n"); else TRACE ("Unable to set realtime priority of win32 callback, not running with realtime priority\n"); /* 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; } getNanoSeconds(&This->system_time); /* 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) { 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, ASIOTrue); } /* let the JACK thread know we are done */ sem_post(&This->semaphore2); } return 0;}