Initial Midi Out on MacOSX
Emmanuel Maillard
mahanuu at free.fr
Thu Jan 18 14:06:55 CST 2007
Hi,
This patch is initial Midi Out support on MacOS X.
This is only support Midi Synth for now.
Thanks
Emmanuel
Changelog:
Initial Midi Out support on MacOS X
-------------- next part --------------
From nobody Mon Sep 17 00:00:00 2001
From: Emmanuel Maillard <mahanuu at free.fr>
Date: Tue Jan 16 23:45:00 2007 +0100
Subject: [PATCH] - Initial Midi Out support on MacOS X
---
configure |15474 ++++++++++++-------------
configure.ac | 2
dlls/winecoreaudio.drv/Makefile.in | 4
dlls/winecoreaudio.drv/audiounit.c | 112
dlls/winecoreaudio.drv/coreaudio.c | 4
dlls/winecoreaudio.drv/coreaudio.h | 5
dlls/winecoreaudio.drv/coremidi.c | 54
dlls/winecoreaudio.drv/coremidi.h | 70
dlls/winecoreaudio.drv/midi.c | 579 +
dlls/winecoreaudio.drv/winecoreaudio.drv.spec | 1
10 files changed, 8534 insertions(+), 7771 deletions(-)
create mode 100644 dlls/winecoreaudio.drv/coremidi.c
create mode 100644 dlls/winecoreaudio.drv/coremidi.h
create mode 100644 dlls/winecoreaudio.drv/midi.c
8ecfd644ee70dcd86fff973833f565dac6862694
diff --git a/configure.ac b/configure.ac
index 46d815e..fbc5094 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1024,7 +1024,7 @@ case $host_os in
if test "$ac_cv_header_CoreAudio_CoreAudio_h" = "yes" -a "$ac_cv_header_AudioUnit_AudioUnit_h" = "yes"
then
dnl CoreServices needed by AudioUnit
- AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices")
+ AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices -framework AudioToolbox -framework CoreMIDI")
fi
case $host_cpu in
*powerpc*)
diff --git a/dlls/winecoreaudio.drv/Makefile.in b/dlls/winecoreaudio.drv/Makefile.in
index a54c476..c31933d 100644
--- a/dlls/winecoreaudio.drv/Makefile.in
+++ b/dlls/winecoreaudio.drv/Makefile.in
@@ -9,7 +9,9 @@ EXTRALIBS = $(LIBUUID) @COREAUDIO@
C_SRCS = \
audio.c \
audiounit.c \
- coreaudio.c
+ coreaudio.c \
+ coremidi.c \
+ midi.c
@MAKE_DLL_RULES@
diff --git a/dlls/winecoreaudio.drv/audiounit.c b/dlls/winecoreaudio.drv/audiounit.c
index b6da953..3d9eb25 100644
--- a/dlls/winecoreaudio.drv/audiounit.c
+++ b/dlls/winecoreaudio.drv/audiounit.c
@@ -22,9 +22,11 @@ #include "config.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(wave);
+WINE_DECLARE_DEBUG_CHANNEL(midi);
#ifdef HAVE_AUDIOUNIT_AUDIOUNIT_H
#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
extern OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
@@ -312,4 +314,114 @@ error:
return 0;
}
+/*
+ * Midi Synth Unit
+ */
+int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth)
+{
+ OSStatus err;
+ ComponentDescription desc;
+ AUNode synthNode;
+ AUNode outNode;
+
+ err = NewAUGraph(graph);
+ if (err != noErr)
+ {
+ ERR_(midi)("NewAUGraph return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ /* create synth node */
+ desc.componentType = kAudioUnitType_MusicDevice;
+ desc.componentSubType = kAudioUnitSubType_DLSSynth;
+
+ err = AUGraphNewNode(*graph, &desc, 0, NULL, &synthNode);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphNewNode cannot create synthNode : %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ /* create out node */
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+
+ err = AUGraphNewNode(*graph, &desc, 0, NULL, &outNode);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphNewNode cannot create outNode %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ err = AUGraphOpen(*graph);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphOpen return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ /* connecting the nodes */
+ err = AUGraphConnectNodeInput(*graph, synthNode, 0, outNode, 0);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphConnectNodeInput cannot connect synthNode to outNode : %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ /* Get the synth unit */
+ err = AUGraphGetNodeInfo(*graph, synthNode, 0, 0, 0, synth);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphGetNodeInfo return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ return 1;
+}
+
+int SynthUnit_Initialize(AudioUnit synth, AUGraph graph)
+{
+ OSStatus err = noErr;
+
+ err = AUGraphInitialize(graph);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphInitialize(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ err = AUGraphStart(graph);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphStart(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ return 1;
+}
+
+int SynthUnit_Close(AUGraph graph)
+{
+ OSStatus err = noErr;
+
+ err = AUGraphStop(graph);
+ if (err != noErr)
+ {
+ ERR_(midi)("AUGraphStop(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ err = DisposeAUGraph(graph);
+ if (err != noErr)
+ {
+ ERR_(midi)("DisposeAUGraph(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return 0;
+ }
+
+ return 1;
+}
#endif
diff --git a/dlls/winecoreaudio.drv/coreaudio.c b/dlls/winecoreaudio.drv/coreaudio.c
index 6fd21d7..a5c3648 100644
--- a/dlls/winecoreaudio.drv/coreaudio.c
+++ b/dlls/winecoreaudio.drv/coreaudio.c
@@ -1,7 +1,7 @@
/*
* Wine Driver for CoreAudio
*
- * Copyright 2005 Emmanuel Maillard
+ * Copyright 2005, 2006 Emmanuel Maillard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -43,6 +43,7 @@ static LRESULT CoreAudio_drvLoad(void)
{
TRACE("()\n");
CoreAudio_WaveInit();
+ CoreAudio_MidiInit();
return 1;
}
@@ -53,6 +54,7 @@ static LRESULT CoreAudio_drvFree(void)
{
TRACE("()\n");
CoreAudio_WaveRelease();
+ CoreAudio_MidiRelease();
return 1;
}
diff --git a/dlls/winecoreaudio.drv/coreaudio.h b/dlls/winecoreaudio.drv/coreaudio.h
index 312ba0e..9114e97 100644
--- a/dlls/winecoreaudio.drv/coreaudio.h
+++ b/dlls/winecoreaudio.drv/coreaudio.h
@@ -1,6 +1,6 @@
/* Definition for CoreAudio drivers : wine multimedia system
*
- * Copyright 2005 Emmanuel Maillard
+ * Copyright 2005, 2006 Emmanuel Maillard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,6 +23,7 @@ #define __WINE_COREAUDIO_H
extern LONG CoreAudio_WaveInit(void);
extern void CoreAudio_WaveRelease(void);
-/* extern BOOL CoreAudio_MidiInit(void); */
+extern LONG CoreAudio_MidiInit(void);
+extern LONG CoreAudio_MidiRelease(void);
#endif /* __WINE_COREAUDIO_H */
diff --git a/dlls/winecoreaudio.drv/coremidi.c b/dlls/winecoreaudio.drv/coremidi.c
new file mode 100644
index 0000000..3df5d0d
--- /dev/null
+++ b/dlls/winecoreaudio.drv/coremidi.c
@@ -0,0 +1,54 @@
+/*
+ * Wine Midi driver for MacOSX
+ *
+ * Copyright 2006 Emmanuel Maillard
+ *
+ * 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/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(midi);
+
+#ifdef HAVE_COREAUDIO_COREAUDIO_H
+#include <CoreMIDI/CoreMIDI.h>
+
+#include "coremidi.h"
+
+MIDIClientRef CoreMidi_CreateClient(CFStringRef name)
+{
+ MIDIClientRef client = NULL;
+
+ if (MIDIClientCreate(name, NULL /* FIXME use notify proc ? */, NULL, &client) != noErr)
+ return NULL;
+
+ return client;
+}
+
+void CoreMidi_GetObjectName(MIDIObjectRef obj, char *name, int size)
+{
+ OSStatus err = noErr;
+ CFStringRef cfname;
+
+ err = MIDIObjectGetStringProperty(obj, kMIDIPropertyName, &cfname);
+ if (err == noErr)
+ {
+ CFStringGetCString(cfname, name, size, kCFStringEncodingASCII);
+ CFRelease(cfname);
+ }
+}
+#endif /* HAVE_COREAUDIO_COREAUDIO_H */
diff --git a/dlls/winecoreaudio.drv/coremidi.h b/dlls/winecoreaudio.drv/coremidi.h
new file mode 100644
index 0000000..c0f636a
--- /dev/null
+++ b/dlls/winecoreaudio.drv/coremidi.h
@@ -0,0 +1,70 @@
+/*
+ * Wine Midi driver for MacOSX
+ *
+ * Copyright 2006 Emmanuel Maillard
+ *
+ * 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
+ */
+
+#ifndef WINE_COREMIDI_H
+#define WINE_COREMIDI_H
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#ifdef WINE_DEFINITIONS
+/*
+ * Due to CoreMIDI headers conflict redefine some types for Wine
+ */
+typedef void *MIDIClientRef;
+typedef void *MIDIEndpointRef;
+typedef void *MIDIPortRef;
+typedef void *MIDIObjectRef;
+
+typedef struct MIDIPacketList MIDIPacketList;
+
+/*
+ * functions
+ */
+extern OSStatus MIDIClientDispose(MIDIClientRef client);
+extern unsigned MIDIGetNumberOfDestinations(void);
+extern MIDIEndpointRef MIDIGetDestination(unsigned i);
+extern unsigned MIDIGetNumberOfSources(void);
+extern MIDIEndpointRef MIDIGetSource(unsigned i);
+
+extern OSStatus MIDIObjectGetProperties(MIDIObjectRef obj, CFPropertyListRef *outProperties, Boolean deep);
+
+
+/*
+ * Due to AudioUnit headers conflict redefine some types.
+ */
+typedef void *AudioUnit;
+typedef void *AUGraph;
+
+/*
+ * redefine MusicDeviceMIDIEvent and MusicDeviceSysEx
+ * from AudioUnit/MusicDevice.h
+ * ComponentResult MusicDeviceMIDIEvent(MusicDeviceComponent ci, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
+ * ComponentResult MusicDeviceSysEx(MusicDeviceComponent ci, const UInt8 *inData, UInt32 inLength)
+ */
+extern OSStatus MusicDeviceMIDIEvent(AudioUnit au, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame);
+extern OSStatus MusicDeviceSysEx(AudioUnit au, const UInt8 *inData, UInt32 inLength);
+
+#endif /* WINE_DEFINITIONS */
+
+
+/* coremidi.c */
+extern MIDIClientRef CoreMidi_CreateClient(CFStringRef name);
+extern void CoreMidi_GetObjectName(MIDIObjectRef obj, char *name, int size);
+#endif
\ No newline at end of file
diff --git a/dlls/winecoreaudio.drv/midi.c b/dlls/winecoreaudio.drv/midi.c
new file mode 100644
index 0000000..c0ff6ad
--- /dev/null
+++ b/dlls/winecoreaudio.drv/midi.c
@@ -0,0 +1,579 @@
+/*
+ * Sample MIDI Wine Driver for MacOSX (based on OSS midi driver)
+ *
+ * Copyright 1994 Martin Ayotte
+ * Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
+ * Copyright 1998/1999 Eric POUECH :
+ * 98/7 changes for making this MIDI driver work on OSS
+ * current support is limited to MIDI ports of OSS systems
+ * 98/9 rewriting MCI code for MIDI
+ * 98/11 splitted in midi.c and mcimidi.c
+ * Copyright 2006 Emmanuel Maillard
+ *
+ * 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 <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "mmddk.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(midi);
+
+#if defined(HAVE_COREAUDIO_COREAUDIO_H)
+#include <CoreAudio/CoreAudio.h>
+
+#define WINE_DEFINITIONS
+#include "coremidi.h"
+
+static MIDIClientRef wineMidiClient = NULL;
+
+static DWORD MidiOut_NumDevs = 0;
+static DWORD MidiIn_NumDevs = 0;
+
+typedef struct tagMidiDestination {
+ /* graph and synth are only used for Midi Synth */
+ AUGraph graph;
+ AudioUnit synth;
+
+ MIDIPortRef port;
+ MIDIOUTCAPSW caps;
+ MIDIOPENDESC midiDesc;
+ WORD wFlags;
+} MidiDestination;
+
+#define MAX_MIDI_SYNTHS 1
+
+MidiDestination *destinations;
+
+extern int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth);
+extern int SynthUnit_Initialize(AudioUnit synth, AUGraph graph);
+extern int SynthUnit_Close(AUGraph graph);
+extern int AudioUnit_SetVolume(AudioUnit au, float left, float right);
+extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right);
+
+LONG CoreAudio_MidiInit(void)
+{
+ int i;
+ CHAR szPname[MAXPNAMELEN] = {0};
+ OSStatus err = noErr;
+
+ CFStringRef name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineMidiClient.%d"), getpid());
+
+ wineMidiClient = CoreMidi_CreateClient( name );
+ if (wineMidiClient == NULL)
+ {
+ CFRelease(name);
+ ERR("can't create wineMidiClient\n");
+ return 0;
+ }
+ CFRelease(name);
+
+ MidiOut_NumDevs = MAX_MIDI_SYNTHS + MIDIGetNumberOfDestinations();
+
+ TRACE("MidiOut_NumDevs %d\n", MidiOut_NumDevs);
+
+ destinations = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MidiOut_NumDevs * sizeof(MidiDestination));
+
+ /* initialise Midi synths */
+ for (i = 0; i < MAX_MIDI_SYNTHS; i++)
+ {
+ snprintf(szPname, sizeof(szPname), "CoreAudio Midi Synth %d", i);
+ MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR));
+
+ destinations[i].caps.wTechnology = MOD_SYNTH;
+ destinations[i].caps.wChannelMask = 0xFFFF;
+
+ destinations[i].caps.wMid = 0x00FF; /* Manufac ID */
+ destinations[i].caps.wPid = 0x0001; /* Product ID */
+ destinations[i].caps.vDriverVersion = 0x0001;
+ destinations[i].caps.dwSupport = MIDICAPS_VOLUME;
+ destinations[i].caps.wVoices = 16;
+ destinations[i].caps.wNotes = 16;
+ }
+ /* initialise available destinations */
+ for (i = MAX_MIDI_SYNTHS; i < MidiOut_NumDevs; i++)
+ {
+ MIDIEndpointRef endpoint = MIDIGetDestination(i - MAX_MIDI_SYNTHS);
+
+ CoreMidi_GetObjectName(endpoint, szPname, sizeof(szPname));
+ MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR));
+
+ name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineOutputPort.%d.%u"), i, getpid());
+ MIDIOutputPortCreate(wineMidiClient, name, &destinations[i].port);
+ CFRelease(name);
+
+ destinations[i].caps.wTechnology = MOD_MIDIPORT;
+ destinations[i].caps.wChannelMask = 0xFFFF;
+
+ destinations[i].caps.wMid = 0x00FF; /* Manufac ID */
+ destinations[i].caps.wPid = 0x0001;
+ destinations[i].caps.vDriverVersion = 0x0001;
+ destinations[i].caps.dwSupport = 0;
+ destinations[i].caps.wVoices = 16;
+ destinations[i].caps.wNotes = 16;
+ }
+ return 1;
+}
+
+LONG CoreAudio_MidiRelease(void)
+{
+ TRACE("\n");
+
+ if (wineMidiClient) MIDIClientDispose(wineMidiClient); /* MIDIClientDispose will close all ports */
+
+ HeapFree(GetProcessHeap(), 0, destinations);
+ return 1;
+}
+
+
+/**************************************************************************
+ * MIDI_NotifyClient [internal]
+ */
+static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
+{
+ DWORD dwCallBack;
+ UINT uFlags;
+ HANDLE hDev;
+ DWORD dwInstance;
+
+ TRACE("wDevID=%d wMsg=%d dwParm1=%04X dwParam2=%04X\n", wDevID, wMsg, dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case MOM_OPEN:
+ case MOM_CLOSE:
+ case MOM_DONE:
+ case MOM_POSITIONCB:
+ dwCallBack = destinations[wDevID].midiDesc.dwCallback;
+ uFlags = destinations[wDevID].wFlags;
+ hDev = destinations[wDevID].midiDesc.hMidi;
+ dwInstance = destinations[wDevID].midiDesc.dwInstance;
+ break;
+
+ case MIM_OPEN:
+ case MIM_CLOSE:
+ case MIM_DATA:
+ case MIM_LONGDATA:
+ case MIM_ERROR:
+ case MIM_LONGERROR:
+ case MIM_MOREDATA:
+ default:
+ WARN("Unsupported MSW-MIDI message %u\n", wMsg);
+ return MMSYSERR_ERROR;
+ }
+
+ return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
+ MMSYSERR_NOERROR : MMSYSERR_ERROR;
+}
+
+static DWORD MidiOut_Open(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
+{
+ MidiDestination *dest;
+
+ TRACE("wDevID=%d lpDesc=%p dwFlags=%08x\n", wDevID, lpDesc, dwFlags);
+
+ if (lpDesc == NULL) {
+ WARN("Invalid Parameter\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ if (destinations[wDevID].midiDesc.hMidi != 0) {
+ WARN("device already open !\n");
+ return MMSYSERR_ALLOCATED;
+ }
+
+ if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
+ WARN("bad dwFlags\n");
+ return MMSYSERR_INVALFLAG;
+ }
+ dest = &destinations[wDevID];
+
+ if (dest->caps.wTechnology == MOD_SYNTH)
+ {
+ if (!SynthUnit_CreateDefaultSynthUnit(&dest->graph, &dest->synth))
+ {
+ ERR("SynthUnit_CreateDefaultSynthUnit dest=%p failed\n", dest);
+ return MMSYSERR_ERROR;
+ }
+
+ if (!SynthUnit_Initialize(dest->synth, dest->graph))
+ {
+ ERR("SynthUnit_Initialise dest=%p failed\n", dest);
+ return MMSYSERR_ERROR;
+ }
+ }
+ else
+ {
+ FIXME("MOD_MIDIPORT\n");
+ }
+ dest->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+ dest->midiDesc = *lpDesc;
+
+ return MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
+}
+
+static DWORD MidiOut_Close(WORD wDevID)
+{
+ DWORD ret = MMSYSERR_NOERROR;
+
+ TRACE("wDevID=%d\n", wDevID);
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
+ SynthUnit_Close(destinations[wDevID].graph);
+ else
+ FIXME("MOD_MIDIPORT\n");
+
+ destinations[wDevID].graph = 0;
+ destinations[wDevID].synth = 0;
+
+ if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
+ WARN("can't notify client !\n");
+ ret = MMSYSERR_INVALPARAM;
+ }
+ destinations[wDevID].midiDesc.hMidi = 0;
+
+ return ret;
+}
+
+static DWORD MidiOut_Data(WORD wDevID, DWORD dwParam)
+{
+ WORD evt = LOBYTE(LOWORD(dwParam));
+ WORD d1 = HIBYTE(LOWORD(dwParam));
+ WORD d2 = LOBYTE(HIWORD(dwParam));
+ UInt8 chn = (evt & 0x0F);
+ OSStatus err = noErr;
+
+ TRACE("wDevID=%d dwParam=%08X\n", wDevID, dwParam);
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ TRACE("evt=%08x d1=%04x d2=%04x (evt & 0xF0)=%04x chn=%d\n", evt, d1, d2, (evt & 0xF0), chn);
+
+ if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
+ {
+ err = MusicDeviceMIDIEvent(destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0);
+ if (err != noErr)
+ {
+ ERR("MusicDeviceMIDIEvent(%p, %04x, %04x, %04x, %d) return %c%c%c%c\n", destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return MMSYSERR_ERROR;
+ }
+ }
+ else FIXME("MOD_MIDIPORT\n");
+
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD MidiOut_LongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
+{
+ LPBYTE lpData;
+ OSStatus err = noErr;
+
+ TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
+
+ /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
+ * but it seems to be used only for midi input.
+ * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
+ */
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ if (lpMidiHdr == NULL) {
+ WARN("Invalid Parameter\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ lpData = (LPBYTE) lpMidiHdr->lpData;
+
+ if (lpData == NULL)
+ return MIDIERR_UNPREPARED;
+ if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
+ return MIDIERR_UNPREPARED;
+ if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
+ return MIDIERR_STILLPLAYING;
+ lpMidiHdr->dwFlags &= ~MHDR_DONE;
+ lpMidiHdr->dwFlags |= MHDR_INQUEUE;
+
+ /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
+ * data, or can it also contain raw MIDI data, to be split up and sent to
+ * modShortData() ?
+ * If the latest is true, then the following WARNing will fire up
+ */
+ if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
+ WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
+ }
+
+ TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
+ TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
+ lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
+ lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
+
+
+ if (lpData[0] != 0xF0) {
+ /* System Exclusive */
+ ERR("Add missing 0xF0 marker at the beginning of system exclusive byte stream\n");
+ }
+ if (lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
+ /* Send end of System Exclusive */
+ ERR("Add missing 0xF7 marker at the end of system exclusive byte stream\n");
+ }
+ if (destinations[wDevID].caps.wTechnology == MOD_SYNTH) /* FIXME */
+ {
+ err = MusicDeviceSysEx(destinations[wDevID].synth, (const UInt8 *) lpData, lpMidiHdr->dwBufferLength);
+ if (err != noErr)
+ {
+ ERR("MusicDeviceSysEx(%p, %p, %d) return %c%c%c%c\n", destinations[wDevID].synth, lpData, lpMidiHdr->dwBufferLength, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
+ return MMSYSERR_ERROR;
+ }
+ }
+ else
+ {
+ FIXME("MOD_MIDIPORT\n");
+ }
+
+ lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
+ lpMidiHdr->dwFlags |= MHDR_DONE;
+ if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
+ WARN("can't notify client !\n");
+ return MMSYSERR_INVALPARAM;
+ }
+ return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
+ * MidiOut_Prepare [internal]
+ */
+static DWORD MidiOut_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
+{
+ TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
+ * asks to prepare MIDIHDR which dwFlags != 0.
+ * So at least check for the inqueue flag
+ */
+ if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
+ lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
+ lpMidiHdr->dwBufferLength >= 0x10000ul) {
+ WARN("%p %p %08x %lu/%d\n", lpMidiHdr, lpMidiHdr->lpData,
+ lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
+ return MMSYSERR_INVALPARAM;
+ }
+
+ lpMidiHdr->lpNext = 0;
+ lpMidiHdr->dwFlags |= MHDR_PREPARED;
+ lpMidiHdr->dwFlags &= ~MHDR_DONE;
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * MidiOut_Unprepare [internal]
+ */
+static DWORD MidiOut_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
+{
+ TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+ if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
+ return MMSYSERR_INVALPARAM;
+ if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
+ return MIDIERR_STILLPLAYING;
+
+ lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
+
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD MidiOut_GetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
+{
+ TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
+
+ if (lpCaps == NULL) {
+ WARN("Invalid Parameter\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+ memcpy(lpCaps, &destinations[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD MidiOut_GetNumDevs(void)
+{
+ TRACE("\n");
+ return MidiOut_NumDevs;
+}
+
+static DWORD MidiOut_GetVolume(WORD wDevID, DWORD *lpdwVolume)
+{
+ TRACE("%d\n", wDevID);
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+ if (lpdwVolume == NULL) {
+ WARN("Invalid Parameter\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
+ {
+ float left;
+ float right;
+ AudioUnit_GetVolume(destinations[wDevID].synth, &left, &right);
+
+ *lpdwVolume = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
+
+ return MMSYSERR_NOERROR;
+ }
+
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+static DWORD MidiOut_SetVolume(WORD wDevID, DWORD dwVolume)
+{
+ FIXME("%d\n", wDevID);
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+ if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
+ {
+ float left;
+ float right;
+
+ left = LOWORD(dwVolume) / 65535.0f;
+ right = HIWORD(dwVolume) / 65535.0f;
+ AudioUnit_SetVolume(destinations[wDevID].synth, left, right);
+
+ return MMSYSERR_NOERROR;
+ }
+
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+static DWORD MidiOut_Reset(WORD wDevID)
+{
+ unsigned chn;
+
+ TRACE("%d\n", wDevID);
+
+ if (wDevID >= MidiOut_NumDevs) {
+ WARN("bad device ID : %d\n", wDevID);
+ return MMSYSERR_BADDEVICEID;
+ }
+ if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
+ {
+ for (chn = 0; chn < 16; chn++) {
+ /* turn off every note */
+ MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x7B, 0, 0);
+ /* remove sustain on channel */
+ MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x40, 0, 0);
+ }
+ }
+ else FIXME("MOD_MIDIPORT\n");
+
+ /* FIXME: the LongData buffers must also be returned to the app */
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* modMessage
+*/
+DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("%d %s %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case DRVM_INIT:
+ case DRVM_EXIT:
+ case DRVM_ENABLE:
+ case DRVM_DISABLE:
+ return 0;
+ case MODM_OPEN:
+ return MidiOut_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
+ case MODM_CLOSE:
+ return MidiOut_Close(wDevID);
+ case MODM_DATA:
+ return MidiOut_Data(wDevID, dwParam1);
+ case MODM_LONGDATA:
+ return MidiOut_LongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
+ case MODM_PREPARE:
+ return MidiOut_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
+ case MODM_UNPREPARE:
+ return MidiOut_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
+ case MODM_GETDEVCAPS:
+ return MidiOut_GetDevCaps(wDevID, (LPMIDIOUTCAPSW) dwParam1, dwParam2);
+ case MODM_GETNUMDEVS:
+ return MidiOut_GetNumDevs();
+ case MODM_GETVOLUME:
+ return MidiOut_GetVolume(wDevID, (DWORD*)dwParam1);
+ case MODM_SETVOLUME:
+ return MidiOut_SetVolume(wDevID, dwParam1);
+ case MODM_RESET:
+ return MidiOut_Reset(wDevID);
+ default:
+ TRACE("Unsupported message (08%x)\n", wMsg);
+ }
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+#else
+DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
+ return MMSYSERR_NOTENABLED;
+}
+
+#endif
\ No newline at end of file
diff --git a/dlls/winecoreaudio.drv/winecoreaudio.drv.spec b/dlls/winecoreaudio.drv/winecoreaudio.drv.spec
index 49fbfb2..456c8a5 100644
--- a/dlls/winecoreaudio.drv/winecoreaudio.drv.spec
+++ b/dlls/winecoreaudio.drv/winecoreaudio.drv.spec
@@ -1,3 +1,4 @@
@ stdcall -private DriverProc(long long long long long) CoreAudio_DriverProc
+@ stdcall -private modMessage(long long long long long) CoreAudio_modMessage
@ stdcall -private widMessage(long long long long long) CoreAudio_widMessage
@ stdcall -private wodMessage(long long long long long) CoreAudio_wodMessage
--
1.3.0
More information about the wine-patches
mailing list