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