Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
a773b16d
Commit
a773b16d
authored
Sep 23, 2011
by
Andrew Eikum
Committed by
Alexandre Julliard
Sep 26, 2011
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
winecoreaudio.drv: Remove wave, mixer, and dsound driver code.
parent
a83bc10c
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
2 additions
and
3786 deletions
+2
-3786
Makefile.in
dlls/winecoreaudio.drv/Makefile.in
+0
-2
audio.c
dlls/winecoreaudio.drv/audio.c
+0
-2539
audiounit.c
dlls/winecoreaudio.drv/audiounit.c
+0
-208
coreaudio.c
dlls/winecoreaudio.drv/coreaudio.c
+1
-5
mixer.c
dlls/winecoreaudio.drv/mixer.c
+0
-1028
mmdevdrv.c
dlls/winecoreaudio.drv/mmdevdrv.c
+1
-1
winecoreaudio.drv.spec
dlls/winecoreaudio.drv/winecoreaudio.drv.spec
+0
-3
No files found.
dlls/winecoreaudio.drv/Makefile.in
View file @
a773b16d
...
...
@@ -3,12 +3,10 @@ IMPORTS = uuid winmm ole32 user32
EXTRALIBS
=
@COREAUDIO@
C_SRCS
=
\
audio.c
\
audiounit.c
\
coreaudio.c
\
coremidi.c
\
midi.c
\
mixer.c
\
mmdevdrv.c
@MAKE_DLL_RULES@
dlls/winecoreaudio.drv/audio.c
deleted
100644 → 0
View file @
a83bc10c
/*
* Wine Driver for CoreAudio based on Jack Driver
*
* Copyright 1994 Martin Ayotte
* Copyright 1999 Eric Pouech (async playing in waveOut/waveIn)
* Copyright 2000 Eric Pouech (loops in waveOut)
* Copyright 2002 Chris Morgan (jack version of this file)
* 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
* 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 <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <fcntl.h>
#include <assert.h>
#ifdef HAVE_COREAUDIO_COREAUDIO_H
#include <CoreAudio/CoreAudio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <libkern/OSAtomic.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wingdi.h"
#include "winerror.h"
#include "mmddk.h"
#include "mmreg.h"
#include "dsound.h"
#include "dsdriver.h"
#include "ks.h"
#include "coreaudio.h"
#include "wine/unicode.h"
#include "wine/library.h"
#include "wine/debug.h"
#include "wine/list.h"
#include "initguid.h"
#include "ksmedia.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
wave
);
WINE_DECLARE_DEBUG_CHANNEL
(
coreaudio
);
/*
Due to AudioUnit headers conflict define some needed types.
*/
typedef
void
*
AudioUnit
;
/* From AudioUnit/AUComponents.h */
enum
{
kAudioUnitRenderAction_OutputIsSilence
=
(
1
<<
4
),
/* provides hint on return from Render(): if set the buffer contains all zeroes */
};
typedef
UInt32
AudioUnitRenderActionFlags
;
typedef
long
ComponentResult
;
extern
ComponentResult
AudioUnitRender
(
AudioUnit
ci
,
AudioUnitRenderActionFlags
*
ioActionFlags
,
const
AudioTimeStamp
*
inTimeStamp
,
UInt32
inOutputBusNumber
,
UInt32
inNumberFrames
,
AudioBufferList
*
ioData
)
AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
;
/* only allow 10 output devices through this driver, this ought to be adequate */
#define MAX_WAVEOUTDRV (1)
#define MAX_WAVEINDRV (1)
/* state diagram for waveOut writing:
*
* +---------+-------------+---------------+---------------------------------+
* | state | function | event | new state |
* +---------+-------------+---------------+---------------------------------+
* | | open() | | PLAYING |
* | PAUSED | write() | | PAUSED |
* | PLAYING | write() | HEADER | PLAYING |
* | (other) | write() | <error> | |
* | (any) | pause() | PAUSING | PAUSED |
* | PAUSED | restart() | RESTARTING | PLAYING |
* | (any) | reset() | RESETTING | PLAYING |
* | (any) | close() | CLOSING | <deallocated> |
* +---------+-------------+---------------+---------------------------------+
*/
/* states of the playing device */
#define WINE_WS_PLAYING 0
/* for waveOut: lpPlayPtr == NULL -> stopped */
#define WINE_WS_PAUSED 1
#define WINE_WS_STOPPED 2
/* Not used for waveOut */
#define WINE_WS_CLOSED 3
/* Not used for waveOut */
#define WINE_WS_OPENING 4
#define WINE_WS_CLOSING 5
typedef
struct
tagCoreAudio_Device
{
char
dev_name
[
32
];
char
mixer_name
[
32
];
unsigned
open_count
;
char
*
interface_name
;
WAVEOUTCAPSW
out_caps
;
WAVEINCAPSW
in_caps
;
DWORD
in_caps_support
;
int
sample_rate
;
int
stereo
;
int
format
;
unsigned
audio_fragment
;
BOOL
full_duplex
;
BOOL
bTriggerSupport
;
BOOL
bOutputEnabled
;
BOOL
bInputEnabled
;
DSDRIVERDESC
ds_desc
;
DSDRIVERCAPS
ds_caps
;
DSCDRIVERCAPS
dsc_caps
;
GUID
ds_guid
;
GUID
dsc_guid
;
AudioDeviceID
outputDeviceID
;
AudioDeviceID
inputDeviceID
;
AudioStreamBasicDescription
streamDescription
;
}
CoreAudio_Device
;
/* for now use the default device */
static
CoreAudio_Device
CoreAudio_DefaultDevice
;
typedef
struct
{
struct
list
entry
;
volatile
int
state
;
/* one of the WINE_WS_ manifest constants */
WAVEOPENDESC
waveDesc
;
WORD
wFlags
;
PCMWAVEFORMAT
format
;
DWORD
woID
;
AudioUnit
audioUnit
;
AudioStreamBasicDescription
streamDescription
;
LPWAVEHDR
lpQueuePtr
;
/* start of queued WAVEHDRs (waiting to be notified) */
LPWAVEHDR
lpPlayPtr
;
/* start of not yet fully played buffers */
DWORD
dwPartialOffset
;
/* Offset of not yet written bytes in lpPlayPtr */
LPWAVEHDR
lpLoopPtr
;
/* pointer of first buffer in loop, if any */
DWORD
dwLoops
;
/* private copy of loop counter */
DWORD
dwPlayedTotal
;
/* number of bytes actually played since opening */
OSSpinLock
lock
;
/* synchronization stuff */
}
WINE_WAVEOUT_INSTANCE
;
typedef
struct
{
CoreAudio_Device
*
cadev
;
WAVEOUTCAPSW
caps
;
char
interface_name
[
32
];
DWORD
device_volume
;
BOOL
trace_on
;
BOOL
warn_on
;
BOOL
err_on
;
struct
list
instances
;
OSSpinLock
lock
;
/* guards the instances list */
}
WINE_WAVEOUT
;
typedef
struct
{
/* This device's device number */
DWORD
wiID
;
/* Access to the following fields is synchronized across threads. */
volatile
int
state
;
LPWAVEHDR
lpQueuePtr
;
DWORD
dwTotalRecorded
;
/* Synchronization mechanism to protect above fields */
OSSpinLock
lock
;
/* Capabilities description */
WAVEINCAPSW
caps
;
char
interface_name
[
32
];
/* Record the arguments used when opening the device. */
WAVEOPENDESC
waveDesc
;
WORD
wFlags
;
PCMWAVEFORMAT
format
;
AudioUnit
audioUnit
;
AudioBufferList
*
bufferList
;
AudioBufferList
*
bufferListCopy
;
/* Record state of debug channels at open. Used to control fprintf's since
* we can't use Wine debug channel calls in non-Wine AudioUnit threads. */
BOOL
trace_on
;
BOOL
warn_on
;
BOOL
err_on
;
/* These fields aren't used. */
#if 0
CoreAudio_Device *cadev;
AudioStreamBasicDescription streamDescription;
#endif
}
WINE_WAVEIN
;
static
WINE_WAVEOUT
WOutDev
[
MAX_WAVEOUTDRV
];
static
WINE_WAVEIN
WInDev
[
MAX_WAVEINDRV
];
static
HANDLE
hThread
=
NULL
;
/* Track the thread we create so we can clean it up later */
static
CFMessagePortRef
Port_SendToMessageThread
;
static
void
wodHelper_PlayPtrNext
(
WINE_WAVEOUT_INSTANCE
*
wwo
);
static
void
wodHelper_NotifyDoneForList
(
WINE_WAVEOUT_INSTANCE
*
wwo
,
LPWAVEHDR
lpWaveHdr
);
static
void
wodHelper_NotifyCompletions
(
WINE_WAVEOUT_INSTANCE
*
wwo
,
BOOL
force
);
static
void
widHelper_NotifyCompletions
(
WINE_WAVEIN
*
wwi
);
extern
int
AudioUnit_CreateDefaultAudioUnit
(
void
*
wwo
,
AudioUnit
*
au
);
extern
int
AudioUnit_CloseAudioUnit
(
AudioUnit
au
);
extern
int
AudioUnit_InitializeWithStreamDescription
(
AudioUnit
au
,
AudioStreamBasicDescription
*
streamFormat
);
extern
OSStatus
AudioOutputUnitStart
(
AudioUnit
au
);
extern
OSStatus
AudioOutputUnitStop
(
AudioUnit
au
);
extern
OSStatus
AudioUnitUninitialize
(
AudioUnit
au
);
extern
int
AudioUnit_SetVolume
(
AudioUnit
au
,
float
left
,
float
right
);
extern
int
AudioUnit_GetVolume
(
AudioUnit
au
,
float
*
left
,
float
*
right
);
extern
int
AudioUnit_GetInputDeviceSampleRate
(
void
);
extern
int
AudioUnit_CreateInputUnit
(
void
*
wwi
,
AudioUnit
*
out_au
,
WORD
nChannels
,
DWORD
nSamplesPerSec
,
WORD
wBitsPerSample
,
UInt32
*
outFrameCount
);
OSStatus
CoreAudio_woAudioUnitIOProc
(
void
*
inRefCon
,
AudioUnitRenderActionFlags
*
ioActionFlags
,
const
AudioTimeStamp
*
inTimeStamp
,
UInt32
inBusNumber
,
UInt32
inNumberFrames
,
AudioBufferList
*
ioData
);
OSStatus
CoreAudio_wiAudioUnitIOProc
(
void
*
inRefCon
,
AudioUnitRenderActionFlags
*
ioActionFlags
,
const
AudioTimeStamp
*
inTimeStamp
,
UInt32
inBusNumber
,
UInt32
inNumberFrames
,
AudioBufferList
*
ioData
);
/* These strings used only for tracing */
static
const
char
*
getMessage
(
UINT
msg
)
{
#define MSG_TO_STR(x) case x: return #x
switch
(
msg
)
{
MSG_TO_STR
(
DRVM_INIT
);
MSG_TO_STR
(
DRVM_EXIT
);
MSG_TO_STR
(
DRVM_ENABLE
);
MSG_TO_STR
(
DRVM_DISABLE
);
MSG_TO_STR
(
WIDM_OPEN
);
MSG_TO_STR
(
WIDM_CLOSE
);
MSG_TO_STR
(
WIDM_ADDBUFFER
);
MSG_TO_STR
(
WIDM_PREPARE
);
MSG_TO_STR
(
WIDM_UNPREPARE
);
MSG_TO_STR
(
WIDM_GETDEVCAPS
);
MSG_TO_STR
(
WIDM_GETNUMDEVS
);
MSG_TO_STR
(
WIDM_GETPOS
);
MSG_TO_STR
(
WIDM_RESET
);
MSG_TO_STR
(
WIDM_START
);
MSG_TO_STR
(
WIDM_STOP
);
MSG_TO_STR
(
WODM_OPEN
);
MSG_TO_STR
(
WODM_CLOSE
);
MSG_TO_STR
(
WODM_WRITE
);
MSG_TO_STR
(
WODM_PAUSE
);
MSG_TO_STR
(
WODM_GETPOS
);
MSG_TO_STR
(
WODM_BREAKLOOP
);
MSG_TO_STR
(
WODM_PREPARE
);
MSG_TO_STR
(
WODM_UNPREPARE
);
MSG_TO_STR
(
WODM_GETDEVCAPS
);
MSG_TO_STR
(
WODM_GETNUMDEVS
);
MSG_TO_STR
(
WODM_GETPITCH
);
MSG_TO_STR
(
WODM_SETPITCH
);
MSG_TO_STR
(
WODM_GETPLAYBACKRATE
);
MSG_TO_STR
(
WODM_SETPLAYBACKRATE
);
MSG_TO_STR
(
WODM_GETVOLUME
);
MSG_TO_STR
(
WODM_SETVOLUME
);
MSG_TO_STR
(
WODM_RESTART
);
MSG_TO_STR
(
WODM_RESET
);
MSG_TO_STR
(
DRV_QUERYDEVICEINTERFACESIZE
);
MSG_TO_STR
(
DRV_QUERYDEVICEINTERFACE
);
MSG_TO_STR
(
DRV_QUERYDSOUNDIFACE
);
MSG_TO_STR
(
DRV_QUERYDSOUNDDESC
);
}
#undef MSG_TO_STR
return
wine_dbg_sprintf
(
"UNKNOWN(0x%04x)"
,
msg
);
}
#define kStopLoopMessage 0
#define kWaveOutNotifyCompletionsMessage 1
#define kWaveInNotifyCompletionsMessage 2
/* Mach Message Handling */
static
CFDataRef
wodMessageHandler
(
CFMessagePortRef
port_ReceiveInMessageThread
,
SInt32
msgid
,
CFDataRef
data
,
void
*
info
)
{
UInt32
*
buffer
=
NULL
;
switch
(
msgid
)
{
case
kWaveOutNotifyCompletionsMessage
:
wodHelper_NotifyCompletions
(
*
(
WINE_WAVEOUT_INSTANCE
**
)
CFDataGetBytePtr
(
data
),
FALSE
);
break
;
case
kWaveInNotifyCompletionsMessage
:
buffer
=
(
UInt32
*
)
CFDataGetBytePtr
(
data
);
widHelper_NotifyCompletions
(
&
WInDev
[
buffer
[
0
]]);
break
;
default:
CFRunLoopStop
(
CFRunLoopGetCurrent
());
break
;
}
return
NULL
;
}
static
DWORD
WINAPI
messageThread
(
LPVOID
p
)
{
CFMessagePortRef
port_ReceiveInMessageThread
=
(
CFMessagePortRef
)
p
;
CFRunLoopSourceRef
source
;
source
=
CFMessagePortCreateRunLoopSource
(
kCFAllocatorDefault
,
port_ReceiveInMessageThread
,
0
);
CFRunLoopAddSource
(
CFRunLoopGetCurrent
(),
source
,
kCFRunLoopDefaultMode
);
CFRunLoopRun
();
CFRunLoopSourceInvalidate
(
source
);
CFRelease
(
source
);
CFRelease
(
port_ReceiveInMessageThread
);
return
0
;
}
/**************************************************************************
* wodSendNotifyCompletionsMessage [internal]
* Call from AudioUnit IO thread can't use Wine debug channels.
*/
static
void
wodSendNotifyCompletionsMessage
(
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
CFDataRef
data
;
if
(
!
Port_SendToMessageThread
)
return
;
data
=
CFDataCreate
(
kCFAllocatorDefault
,
(
UInt8
*
)
&
wwo
,
sizeof
(
wwo
));
if
(
!
data
)
return
;
CFMessagePortSendRequest
(
Port_SendToMessageThread
,
kWaveOutNotifyCompletionsMessage
,
data
,
0
.
0
,
0
.
0
,
NULL
,
NULL
);
CFRelease
(
data
);
}
/**************************************************************************
* wodSendNotifyInputCompletionsMessage [internal]
* Call from AudioUnit IO thread can't use Wine debug channels.
*/
static
void
wodSendNotifyInputCompletionsMessage
(
WINE_WAVEIN
*
wwi
)
{
CFDataRef
data
;
UInt32
buffer
;
if
(
!
Port_SendToMessageThread
)
return
;
buffer
=
(
UInt32
)
wwi
->
wiID
;
data
=
CFDataCreate
(
kCFAllocatorDefault
,
(
UInt8
*
)
&
buffer
,
sizeof
(
buffer
));
if
(
!
data
)
return
;
CFMessagePortSendRequest
(
Port_SendToMessageThread
,
kWaveInNotifyCompletionsMessage
,
data
,
0
.
0
,
0
.
0
,
NULL
,
NULL
);
CFRelease
(
data
);
}
static
DWORD
bytes_to_mmtime
(
LPMMTIME
lpTime
,
DWORD
position
,
PCMWAVEFORMAT
*
format
)
{
TRACE
(
"wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u
\n
"
,
lpTime
->
wType
,
format
->
wBitsPerSample
,
format
->
wf
.
nSamplesPerSec
,
format
->
wf
.
nChannels
,
format
->
wf
.
nAvgBytesPerSec
);
TRACE
(
"Position in bytes=%u
\n
"
,
position
);
switch
(
lpTime
->
wType
)
{
case
TIME_SAMPLES
:
lpTime
->
u
.
sample
=
position
/
(
format
->
wBitsPerSample
/
8
*
format
->
wf
.
nChannels
);
TRACE
(
"TIME_SAMPLES=%u
\n
"
,
lpTime
->
u
.
sample
);
break
;
case
TIME_MS
:
lpTime
->
u
.
ms
=
1000
.
0
*
position
/
(
format
->
wBitsPerSample
/
8
*
format
->
wf
.
nChannels
*
format
->
wf
.
nSamplesPerSec
);
TRACE
(
"TIME_MS=%u
\n
"
,
lpTime
->
u
.
ms
);
break
;
case
TIME_SMPTE
:
lpTime
->
u
.
smpte
.
fps
=
30
;
position
=
position
/
(
format
->
wBitsPerSample
/
8
*
format
->
wf
.
nChannels
);
position
+=
(
format
->
wf
.
nSamplesPerSec
/
lpTime
->
u
.
smpte
.
fps
)
-
1
;
/* round up */
lpTime
->
u
.
smpte
.
sec
=
position
/
format
->
wf
.
nSamplesPerSec
;
position
-=
lpTime
->
u
.
smpte
.
sec
*
format
->
wf
.
nSamplesPerSec
;
lpTime
->
u
.
smpte
.
min
=
lpTime
->
u
.
smpte
.
sec
/
60
;
lpTime
->
u
.
smpte
.
sec
-=
60
*
lpTime
->
u
.
smpte
.
min
;
lpTime
->
u
.
smpte
.
hour
=
lpTime
->
u
.
smpte
.
min
/
60
;
lpTime
->
u
.
smpte
.
min
-=
60
*
lpTime
->
u
.
smpte
.
hour
;
lpTime
->
u
.
smpte
.
fps
=
30
;
lpTime
->
u
.
smpte
.
frame
=
position
*
lpTime
->
u
.
smpte
.
fps
/
format
->
wf
.
nSamplesPerSec
;
TRACE
(
"TIME_SMPTE=%02u:%02u:%02u:%02u
\n
"
,
lpTime
->
u
.
smpte
.
hour
,
lpTime
->
u
.
smpte
.
min
,
lpTime
->
u
.
smpte
.
sec
,
lpTime
->
u
.
smpte
.
frame
);
break
;
default:
WARN
(
"Format %d not supported, using TIME_BYTES !
\n
"
,
lpTime
->
wType
);
lpTime
->
wType
=
TIME_BYTES
;
/* fall through */
case
TIME_BYTES
:
lpTime
->
u
.
cb
=
position
;
TRACE
(
"TIME_BYTES=%u
\n
"
,
lpTime
->
u
.
cb
);
break
;
}
return
MMSYSERR_NOERROR
;
}
static
BOOL
supportedFormat
(
LPWAVEFORMATEX
wf
)
{
if
(
wf
->
nSamplesPerSec
<
DSBFREQUENCY_MIN
||
wf
->
nSamplesPerSec
>
DSBFREQUENCY_MAX
)
return
FALSE
;
if
(
wf
->
wFormatTag
==
WAVE_FORMAT_PCM
)
{
if
(
wf
->
nChannels
>=
1
&&
wf
->
nChannels
<=
2
)
{
if
(
wf
->
wBitsPerSample
==
8
||
wf
->
wBitsPerSample
==
16
)
return
TRUE
;
}
}
else
if
(
wf
->
wFormatTag
==
WAVE_FORMAT_EXTENSIBLE
)
{
WAVEFORMATEXTENSIBLE
*
wfex
=
(
WAVEFORMATEXTENSIBLE
*
)
wf
;
if
(
wf
->
cbSize
==
22
&&
IsEqualGUID
(
&
wfex
->
SubFormat
,
&
KSDATAFORMAT_SUBTYPE_PCM
))
{
if
(
wf
->
nChannels
>=
1
&&
wf
->
nChannels
<=
8
)
{
if
(
wf
->
wBitsPerSample
==
wfex
->
Samples
.
wValidBitsPerSample
)
{
if
(
wf
->
wBitsPerSample
==
8
||
wf
->
wBitsPerSample
==
16
)
return
TRUE
;
}
else
WARN
(
"wBitsPerSample != wValidBitsPerSample not supported yet
\n
"
);
}
}
else
WARN
(
"only KSDATAFORMAT_SUBTYPE_PCM supported
\n
"
);
}
else
WARN
(
"only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported
\n
"
);
return
FALSE
;
}
void
copyFormat
(
LPWAVEFORMATEX
wf1
,
LPPCMWAVEFORMAT
wf2
)
{
memcpy
(
wf2
,
wf1
,
sizeof
(
PCMWAVEFORMAT
));
/* Downgrade WAVE_FORMAT_EXTENSIBLE KSDATAFORMAT_SUBTYPE_PCM
* to smaller yet compatible WAVE_FORMAT_PCM structure */
if
(
wf2
->
wf
.
wFormatTag
==
WAVE_FORMAT_EXTENSIBLE
)
wf2
->
wf
.
wFormatTag
=
WAVE_FORMAT_PCM
;
}
/**************************************************************************
* CoreAudio_GetDevCaps [internal]
*/
BOOL
CoreAudio_GetDevCaps
(
void
)
{
OSStatus
status
;
UInt32
propertySize
;
AudioDeviceID
devId
=
CoreAudio_DefaultDevice
.
outputDeviceID
;
AudioObjectPropertyAddress
propertyAddress
;
CFStringRef
name
;
CFRange
range
;
propertySize
=
sizeof
(
name
);
propertyAddress
.
mSelector
=
kAudioObjectPropertyName
;
propertyAddress
.
mScope
=
kAudioDevicePropertyScopeOutput
;
propertyAddress
.
mElement
=
kAudioObjectPropertyElementMaster
;
status
=
AudioObjectGetPropertyData
(
devId
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
,
&
name
);
if
(
status
)
{
ERR
(
"AudioObjectGetPropertyData for kAudioObjectPropertyName return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
return
FALSE
;
}
CFStringGetCString
(
name
,
CoreAudio_DefaultDevice
.
ds_desc
.
szDesc
,
sizeof
(
CoreAudio_DefaultDevice
.
ds_desc
.
szDesc
),
kCFStringEncodingUTF8
);
strcpy
(
CoreAudio_DefaultDevice
.
ds_desc
.
szDrvname
,
"winecoreaudio.drv"
);
range
=
CFRangeMake
(
0
,
min
(
sizeof
(
CoreAudio_DefaultDevice
.
out_caps
.
szPname
)
/
sizeof
(
WCHAR
)
-
1
,
CFStringGetLength
(
name
)));
CFStringGetCharacters
(
name
,
range
,
CoreAudio_DefaultDevice
.
out_caps
.
szPname
);
CoreAudio_DefaultDevice
.
out_caps
.
szPname
[
range
.
length
]
=
0
;
CFStringGetCString
(
name
,
CoreAudio_DefaultDevice
.
dev_name
,
32
,
kCFStringEncodingUTF8
);
CFRelease
(
name
);
propertySize
=
sizeof
(
CoreAudio_DefaultDevice
.
streamDescription
);
/* FIXME: kAudioDevicePropertyStreamFormat is deprecated. We're
* "supposed" to get an AudioStream object from the AudioDevice,
* then query it for the format with kAudioStreamPropertyVirtualFormat.
* Apple says that this is for our own good, because this property
* "has been shown to lead to programming mistakes by clients when
* working with devices with multiple streams." Only one problem:
* which stream? For now, just query the device.
*/
propertyAddress
.
mSelector
=
kAudioDevicePropertyStreamFormat
;
status
=
AudioObjectGetPropertyData
(
devId
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
,
&
CoreAudio_DefaultDevice
.
streamDescription
);
if
(
status
!=
noErr
)
{
ERR
(
"AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
return
FALSE
;
}
TRACE
(
"Device Stream Description mSampleRate : %f
\n
mFormatID : %s
\n
"
"mFormatFlags : %lX
\n
mBytesPerPacket : %lu
\n
mFramesPerPacket : %lu
\n
"
"mBytesPerFrame : %lu
\n
mChannelsPerFrame : %lu
\n
mBitsPerChannel : %lu
\n
"
,
CoreAudio_DefaultDevice
.
streamDescription
.
mSampleRate
,
wine_dbgstr_fourcc
(
CoreAudio_DefaultDevice
.
streamDescription
.
mFormatID
),
CoreAudio_DefaultDevice
.
streamDescription
.
mFormatFlags
,
CoreAudio_DefaultDevice
.
streamDescription
.
mBytesPerPacket
,
CoreAudio_DefaultDevice
.
streamDescription
.
mFramesPerPacket
,
CoreAudio_DefaultDevice
.
streamDescription
.
mBytesPerFrame
,
CoreAudio_DefaultDevice
.
streamDescription
.
mChannelsPerFrame
,
CoreAudio_DefaultDevice
.
streamDescription
.
mBitsPerChannel
);
CoreAudio_DefaultDevice
.
out_caps
.
wMid
=
0xcafe
;
CoreAudio_DefaultDevice
.
out_caps
.
wPid
=
0x0001
;
CoreAudio_DefaultDevice
.
out_caps
.
vDriverVersion
=
0x0001
;
CoreAudio_DefaultDevice
.
out_caps
.
dwFormats
=
0x00000000
;
CoreAudio_DefaultDevice
.
out_caps
.
wReserved1
=
0
;
CoreAudio_DefaultDevice
.
out_caps
.
dwSupport
=
WAVECAPS_VOLUME
;
CoreAudio_DefaultDevice
.
out_caps
.
dwSupport
|=
WAVECAPS_LRVOLUME
;
CoreAudio_DefaultDevice
.
out_caps
.
wChannels
=
2
;
CoreAudio_DefaultDevice
.
out_caps
.
dwFormats
|=
WAVE_FORMAT_4S16
;
TRACE_
(
coreaudio
)(
"out dwFormats = %08x, dwSupport = %08x
\n
"
,
CoreAudio_DefaultDevice
.
out_caps
.
dwFormats
,
CoreAudio_DefaultDevice
.
out_caps
.
dwSupport
);
return
TRUE
;
}
/******************************************************************
* CoreAudio_WaveInit
*
* Initialize CoreAudio_DefaultDevice
*/
LONG
CoreAudio_WaveInit
(
void
)
{
OSStatus
status
;
UInt32
propertySize
;
AudioObjectPropertyAddress
propertyAddress
;
int
i
;
CFStringRef
messageThreadPortName
;
CFMessagePortRef
port_ReceiveInMessageThread
;
int
inputSampleRate
;
TRACE
(
"()
\n
"
);
/* number of sound cards */
propertyAddress
.
mSelector
=
kAudioHardwarePropertyDevices
;
propertyAddress
.
mScope
=
kAudioObjectPropertyScopeGlobal
;
propertyAddress
.
mElement
=
kAudioObjectPropertyElementMaster
;
AudioObjectGetPropertyDataSize
(
kAudioObjectSystemObject
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
);
propertySize
/=
sizeof
(
AudioDeviceID
);
TRACE
(
"sound cards : %lu
\n
"
,
propertySize
);
/* Get the output device */
propertySize
=
sizeof
(
CoreAudio_DefaultDevice
.
outputDeviceID
);
propertyAddress
.
mSelector
=
kAudioHardwarePropertyDefaultOutputDevice
;
status
=
AudioObjectGetPropertyData
(
kAudioObjectSystemObject
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
,
&
CoreAudio_DefaultDevice
.
outputDeviceID
);
if
(
status
)
{
ERR
(
"AudioObjectGetPropertyData return %s for kAudioHardwarePropertyDefaultOutputDevice
\n
"
,
wine_dbgstr_fourcc
(
status
));
return
DRV_FAILURE
;
}
if
(
CoreAudio_DefaultDevice
.
outputDeviceID
==
kAudioDeviceUnknown
)
{
ERR
(
"AudioObjectGetPropertyData: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown
\n
"
);
return
DRV_FAILURE
;
}
if
(
!
CoreAudio_GetDevCaps
()
)
return
DRV_FAILURE
;
CoreAudio_DefaultDevice
.
interface_name
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
CoreAudio_DefaultDevice
.
dev_name
)
+
1
);
strcpy
(
CoreAudio_DefaultDevice
.
interface_name
,
CoreAudio_DefaultDevice
.
dev_name
);
for
(
i
=
0
;
i
<
MAX_WAVEOUTDRV
;
++
i
)
{
static
const
WCHAR
wszWaveOutFormat
[]
=
{
'C'
,
'o'
,
'r'
,
'e'
,
'A'
,
'u'
,
'd'
,
'i'
,
'o'
,
' '
,
'W'
,
'a'
,
'v'
,
'e'
,
'O'
,
'u'
,
't'
,
' '
,
'%'
,
'd'
,
0
};
list_init
(
&
WOutDev
[
i
].
instances
);
WOutDev
[
i
].
cadev
=
&
CoreAudio_DefaultDevice
;
memset
(
&
WOutDev
[
i
].
caps
,
0
,
sizeof
(
WOutDev
[
i
].
caps
));
WOutDev
[
i
].
caps
.
wMid
=
0xcafe
;
/* Manufac ID */
WOutDev
[
i
].
caps
.
wPid
=
0x0001
;
/* Product ID */
snprintfW
(
WOutDev
[
i
].
caps
.
szPname
,
sizeof
(
WOutDev
[
i
].
caps
.
szPname
)
/
sizeof
(
WCHAR
),
wszWaveOutFormat
,
i
);
snprintf
(
WOutDev
[
i
].
interface_name
,
sizeof
(
WOutDev
[
i
].
interface_name
),
"winecoreaudio: %d"
,
i
);
WOutDev
[
i
].
caps
.
vDriverVersion
=
0x0001
;
WOutDev
[
i
].
caps
.
dwFormats
=
0x00000000
;
WOutDev
[
i
].
caps
.
dwSupport
=
WAVECAPS_VOLUME
;
WOutDev
[
i
].
caps
.
wChannels
=
2
;
/* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */
/* FIXME */
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96M08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96S08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96M16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96S16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48M08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48S08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48M16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48S16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4M08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4S08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4S16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4M16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2M08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2S08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2M16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2S16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1M08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1S08
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1M16
;
WOutDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1S16
;
WOutDev
[
i
].
device_volume
=
0xffffffff
;
WOutDev
[
i
].
lock
=
0
;
/* initialize the mutex */
}
/* FIXME: implement sample rate conversion on input */
inputSampleRate
=
AudioUnit_GetInputDeviceSampleRate
();
for
(
i
=
0
;
i
<
MAX_WAVEINDRV
;
++
i
)
{
static
const
WCHAR
wszWaveInFormat
[]
=
{
'C'
,
'o'
,
'r'
,
'e'
,
'A'
,
'u'
,
'd'
,
'i'
,
'o'
,
' '
,
'W'
,
'a'
,
'v'
,
'e'
,
'I'
,
'n'
,
' '
,
'%'
,
'd'
,
0
};
memset
(
&
WInDev
[
i
],
0
,
sizeof
(
WInDev
[
i
]));
WInDev
[
i
].
wiID
=
i
;
/* Establish preconditions for widOpen */
WInDev
[
i
].
state
=
WINE_WS_CLOSED
;
WInDev
[
i
].
lock
=
0
;
/* initialize the mutex */
/* Fill in capabilities. widGetDevCaps can be called at any time. */
WInDev
[
i
].
caps
.
wMid
=
0xcafe
;
/* Manufac ID */
WInDev
[
i
].
caps
.
wPid
=
0x0001
;
/* Product ID */
WInDev
[
i
].
caps
.
vDriverVersion
=
0x0001
;
snprintfW
(
WInDev
[
i
].
caps
.
szPname
,
sizeof
(
WInDev
[
i
].
caps
.
szPname
)
/
sizeof
(
WCHAR
),
wszWaveInFormat
,
i
);
snprintf
(
WInDev
[
i
].
interface_name
,
sizeof
(
WInDev
[
i
].
interface_name
),
"winecoreaudio in: %d"
,
i
);
if
(
inputSampleRate
==
96000
)
{
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96M08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96S08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96M16
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_96S16
;
}
if
(
inputSampleRate
==
48000
)
{
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48M08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48S08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48M16
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_48S16
;
}
if
(
inputSampleRate
==
44100
)
{
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4M08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4S08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4M16
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_4S16
;
}
if
(
inputSampleRate
==
22050
)
{
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2M08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2S08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2M16
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_2S16
;
}
if
(
inputSampleRate
==
11025
)
{
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1M08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1S08
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1M16
;
WInDev
[
i
].
caps
.
dwFormats
|=
WAVE_FORMAT_1S16
;
}
WInDev
[
i
].
caps
.
wChannels
=
2
;
}
/* create mach messages handler */
srandomdev
();
messageThreadPortName
=
CFStringCreateWithFormat
(
kCFAllocatorDefault
,
NULL
,
CFSTR
(
"WaveMessagePort.%d.%lu"
),
getpid
(),
(
unsigned
long
)
random
());
if
(
!
messageThreadPortName
)
{
ERR
(
"Can't create message thread port name
\n
"
);
return
DRV_FAILURE
;
}
port_ReceiveInMessageThread
=
CFMessagePortCreateLocal
(
kCFAllocatorDefault
,
messageThreadPortName
,
&
wodMessageHandler
,
NULL
,
NULL
);
if
(
!
port_ReceiveInMessageThread
)
{
ERR
(
"Can't create message thread local port
\n
"
);
CFRelease
(
messageThreadPortName
);
return
DRV_FAILURE
;
}
Port_SendToMessageThread
=
CFMessagePortCreateRemote
(
kCFAllocatorDefault
,
messageThreadPortName
);
CFRelease
(
messageThreadPortName
);
if
(
!
Port_SendToMessageThread
)
{
ERR
(
"Can't create port for sending to message thread
\n
"
);
CFRelease
(
port_ReceiveInMessageThread
);
return
DRV_FAILURE
;
}
/* Cannot WAIT for any events because we are called from the loader (which has a lock on loading stuff) */
/* We might want to wait for this thread to be created -- but we cannot -- not here at least */
/* Instead track the thread so we can clean it up later */
if
(
hThread
)
{
ERR
(
"Message thread already started -- expect problems
\n
"
);
}
hThread
=
CreateThread
(
NULL
,
0
,
messageThread
,
(
LPVOID
)
port_ReceiveInMessageThread
,
0
,
NULL
);
if
(
!
hThread
)
{
ERR
(
"Can't create message thread
\n
"
);
CFRelease
(
port_ReceiveInMessageThread
);
CFRelease
(
Port_SendToMessageThread
);
Port_SendToMessageThread
=
NULL
;
return
DRV_FAILURE
;
}
/* The message thread is responsible for releasing port_ReceiveInMessageThread. */
return
DRV_SUCCESS
;
}
void
CoreAudio_WaveRelease
(
void
)
{
/* Stop CFRunLoop in messageThread */
TRACE
(
"()
\n
"
);
if
(
!
Port_SendToMessageThread
)
return
;
CFMessagePortSendRequest
(
Port_SendToMessageThread
,
kStopLoopMessage
,
NULL
,
0
.
0
,
0
.
0
,
NULL
,
NULL
);
CFRelease
(
Port_SendToMessageThread
);
Port_SendToMessageThread
=
NULL
;
/* Wait for the thread to finish and clean it up */
/* This rids us of any quick start/shutdown driver crashes */
WaitForSingleObject
(
hThread
,
INFINITE
);
CloseHandle
(
hThread
);
hThread
=
NULL
;
}
/*======================================================================*
* Low level WAVE OUT implementation *
*======================================================================*/
/**************************************************************************
* wodNotifyClient [internal]
*/
static
void
wodNotifyClient
(
WINE_WAVEOUT_INSTANCE
*
wwo
,
WORD
wMsg
,
DWORD_PTR
dwParam1
,
DWORD_PTR
dwParam2
)
{
TRACE
(
"wMsg = 0x%04x dwParm1 = %04lx dwParam2 = %04lx
\n
"
,
wMsg
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
case
WOM_OPEN
:
case
WOM_CLOSE
:
case
WOM_DONE
:
DriverCallback
(
wwo
->
waveDesc
.
dwCallback
,
wwo
->
wFlags
,
(
HDRVR
)
wwo
->
waveDesc
.
hWave
,
wMsg
,
wwo
->
waveDesc
.
dwInstance
,
dwParam1
,
dwParam2
);
break
;
default:
FIXME
(
"Unknown callback message %u
\n
"
,
wMsg
);
}
}
/**************************************************************************
* wodGetDevCaps [internal]
*/
static
DWORD
wodGetDevCaps
(
WORD
wDevID
,
LPWAVEOUTCAPSW
lpCaps
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %u);
\n
"
,
wDevID
,
lpCaps
,
dwSize
);
if
(
lpCaps
==
NULL
)
return
MMSYSERR_NOTENABLED
;
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
TRACE
(
"MAX_WAVOUTDRV reached !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
TRACE
(
"dwSupport=(0x%x), dwFormats=(0x%x)
\n
"
,
WOutDev
[
wDevID
].
caps
.
dwSupport
,
WOutDev
[
wDevID
].
caps
.
dwFormats
);
memcpy
(
lpCaps
,
&
WOutDev
[
wDevID
].
caps
,
min
(
dwSize
,
sizeof
(
*
lpCaps
)));
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodOpen [internal]
*/
static
DWORD
wodOpen
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
**
pInstance
,
LPWAVEOPENDESC
lpDesc
,
DWORD
dwFlags
)
{
WINE_WAVEOUT_INSTANCE
*
wwo
;
DWORD
ret
;
AudioStreamBasicDescription
streamFormat
;
AudioUnit
audioUnit
=
NULL
;
BOOL
auInited
=
FALSE
;
TRACE
(
"(%u, %p, %p, %08x);
\n
"
,
wDevID
,
pInstance
,
lpDesc
,
dwFlags
);
if
(
lpDesc
==
NULL
)
{
WARN
(
"Invalid Parameter !
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
TRACE
(
"MAX_WAVOUTDRV reached !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
TRACE
(
"Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
,
lpDesc
->
lpFormat
->
wBitsPerSample
);
if
(
!
supportedFormat
(
lpDesc
->
lpFormat
))
{
WARN
(
"Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
,
lpDesc
->
lpFormat
->
wBitsPerSample
);
return
WAVERR_BADFORMAT
;
}
if
(
dwFlags
&
WAVE_FORMAT_QUERY
)
{
TRACE
(
"Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
);
return
MMSYSERR_NOERROR
;
}
/* nBlockAlign and nAvgBytesPerSec are output variables for dsound */
if
(
lpDesc
->
lpFormat
->
nBlockAlign
!=
lpDesc
->
lpFormat
->
nChannels
*
lpDesc
->
lpFormat
->
wBitsPerSample
/
8
)
{
lpDesc
->
lpFormat
->
nBlockAlign
=
lpDesc
->
lpFormat
->
nChannels
*
lpDesc
->
lpFormat
->
wBitsPerSample
/
8
;
WARN
(
"Fixing nBlockAlign
\n
"
);
}
if
(
lpDesc
->
lpFormat
->
nAvgBytesPerSec
!=
lpDesc
->
lpFormat
->
nSamplesPerSec
*
lpDesc
->
lpFormat
->
nBlockAlign
)
{
lpDesc
->
lpFormat
->
nAvgBytesPerSec
=
lpDesc
->
lpFormat
->
nSamplesPerSec
*
lpDesc
->
lpFormat
->
nBlockAlign
;
WARN
(
"Fixing nAvgBytesPerSec
\n
"
);
}
/* We proceed in three phases:
* o Allocate the device instance, marking it as opening
* o Create, configure, and start the Audio Unit. To avoid deadlock,
* this has to be done without holding wwo->lock.
* o If that was successful, finish setting up our instance and add it
* to the device's list.
* Otherwise, clean up and deallocate the instance.
*/
wwo
=
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
sizeof
(
*
wwo
));
if
(
!
wwo
)
return
MMSYSERR_NOMEM
;
wwo
->
woID
=
wDevID
;
wwo
->
state
=
WINE_WS_OPENING
;
if
(
!
AudioUnit_CreateDefaultAudioUnit
((
void
*
)
wwo
,
&
audioUnit
))
{
ERR
(
"CoreAudio_CreateDefaultAudioUnit(0x%04x) failed
\n
"
,
wDevID
);
ret
=
MMSYSERR_ERROR
;
goto
error
;
}
streamFormat
.
mFormatID
=
kAudioFormatLinearPCM
;
streamFormat
.
mFormatFlags
=
kLinearPCMFormatFlagIsPacked
;
/* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
if
(
lpDesc
->
lpFormat
->
wBitsPerSample
!=
8
)
streamFormat
.
mFormatFlags
|=
kLinearPCMFormatFlagIsSignedInteger
;
# ifdef WORDS_BIGENDIAN
streamFormat
.
mFormatFlags
|=
kLinearPCMFormatFlagIsBigEndian
;
/* FIXME Wave format is little endian */
# endif
streamFormat
.
mSampleRate
=
lpDesc
->
lpFormat
->
nSamplesPerSec
;
streamFormat
.
mChannelsPerFrame
=
lpDesc
->
lpFormat
->
nChannels
;
streamFormat
.
mFramesPerPacket
=
1
;
streamFormat
.
mBitsPerChannel
=
lpDesc
->
lpFormat
->
wBitsPerSample
;
streamFormat
.
mBytesPerFrame
=
streamFormat
.
mBitsPerChannel
*
streamFormat
.
mChannelsPerFrame
/
8
;
streamFormat
.
mBytesPerPacket
=
streamFormat
.
mBytesPerFrame
*
streamFormat
.
mFramesPerPacket
;
ret
=
AudioUnit_InitializeWithStreamDescription
(
audioUnit
,
&
streamFormat
);
if
(
!
ret
)
{
ret
=
WAVERR_BADFORMAT
;
/* FIXME return an error based on the OSStatus */
goto
error
;
}
auInited
=
TRUE
;
AudioUnit_SetVolume
(
audioUnit
,
LOWORD
(
WOutDev
[
wDevID
].
device_volume
)
/
65535
.
0
f
,
HIWORD
(
WOutDev
[
wDevID
].
device_volume
)
/
65535
.
0
f
);
/* Our render callback CoreAudio_woAudioUnitIOProc may be called before
* AudioOutputUnitStart returns. Core Audio will grab its own internal
* lock before calling it and the callback grabs wwo->lock. This would
* deadlock if we were holding wwo->lock.
* Also, the callback has to safely do nothing in that case, because
* wwo hasn't been completely filled out, yet. This is assured by state
* being WINE_WS_OPENING. */
ret
=
AudioOutputUnitStart
(
audioUnit
);
if
(
ret
)
{
ERR
(
"AudioOutputUnitStart failed: %08x
\n
"
,
ret
);
ret
=
MMSYSERR_ERROR
;
/* FIXME return an error based on the OSStatus */
goto
error
;
}
OSSpinLockLock
(
&
wwo
->
lock
);
assert
(
wwo
->
state
==
WINE_WS_OPENING
);
wwo
->
audioUnit
=
audioUnit
;
wwo
->
streamDescription
=
streamFormat
;
wwo
->
state
=
WINE_WS_PLAYING
;
wwo
->
wFlags
=
HIWORD
(
dwFlags
&
CALLBACK_TYPEMASK
);
wwo
->
waveDesc
=
*
lpDesc
;
copyFormat
(
lpDesc
->
lpFormat
,
&
wwo
->
format
);
WOutDev
[
wDevID
].
trace_on
=
TRACE_ON
(
wave
);
WOutDev
[
wDevID
].
warn_on
=
WARN_ON
(
wave
);
WOutDev
[
wDevID
].
err_on
=
ERR_ON
(
wave
);
OSSpinLockUnlock
(
&
wwo
->
lock
);
OSSpinLockLock
(
&
WOutDev
[
wDevID
].
lock
);
list_add_head
(
&
WOutDev
[
wDevID
].
instances
,
&
wwo
->
entry
);
OSSpinLockUnlock
(
&
WOutDev
[
wDevID
].
lock
);
*
pInstance
=
wwo
;
TRACE
(
"opened instance %p
\n
"
,
wwo
);
wodNotifyClient
(
wwo
,
WOM_OPEN
,
0L
,
0L
);
return
MMSYSERR_NOERROR
;
error:
if
(
audioUnit
)
{
if
(
auInited
)
AudioUnitUninitialize
(
audioUnit
);
AudioUnit_CloseAudioUnit
(
audioUnit
);
}
OSSpinLockLock
(
&
wwo
->
lock
);
assert
(
wwo
->
state
==
WINE_WS_OPENING
);
/* OSSpinLockUnlock(&wwo->lock); *//* No need, about to free */
HeapFree
(
GetProcessHeap
(),
0
,
wwo
);
return
ret
;
}
/**************************************************************************
* wodClose [internal]
*/
static
DWORD
wodClose
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
DWORD
ret
=
MMSYSERR_NOERROR
;
TRACE
(
"(%u, %p);
\n
"
,
wDevID
,
wwo
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
OSSpinLockLock
(
&
wwo
->
lock
);
if
(
wwo
->
lpQueuePtr
)
{
OSSpinLockUnlock
(
&
wwo
->
lock
);
WARN
(
"buffers still playing !
\n
"
);
return
WAVERR_STILLPLAYING
;
}
else
{
OSStatus
err
;
AudioUnit
audioUnit
=
wwo
->
audioUnit
;
/* sanity check: this should not happen since the device must have been reset before */
if
(
wwo
->
lpQueuePtr
||
wwo
->
lpPlayPtr
)
ERR
(
"out of sync
\n
"
);
wwo
->
state
=
WINE_WS_CLOSING
;
/* mark the device as closing */
wwo
->
audioUnit
=
NULL
;
OSSpinLockUnlock
(
&
wwo
->
lock
);
err
=
AudioUnitUninitialize
(
audioUnit
);
if
(
err
)
{
ERR
(
"AudioUnitUninitialize return %s
\n
"
,
wine_dbgstr_fourcc
(
err
));
return
MMSYSERR_ERROR
;
/* FIXME return an error based on the OSStatus */
}
if
(
!
AudioUnit_CloseAudioUnit
(
audioUnit
)
)
{
ERR
(
"Can't close AudioUnit
\n
"
);
return
MMSYSERR_ERROR
;
/* FIXME return an error based on the OSStatus */
}
OSSpinLockLock
(
&
WOutDev
[
wDevID
].
lock
);
list_remove
(
&
wwo
->
entry
);
OSSpinLockUnlock
(
&
WOutDev
[
wDevID
].
lock
);
wodNotifyClient
(
wwo
,
WOM_CLOSE
,
0L
,
0L
);
HeapFree
(
GetProcessHeap
(),
0
,
wwo
);
}
return
ret
;
}
/**************************************************************************
* wodPrepare [internal]
*/
static
DWORD
wodPrepare
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
,
LPWAVEHDR
lpWaveHdr
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %p, %08x);
\n
"
,
wDevID
,
wwo
,
lpWaveHdr
,
dwSize
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
lpWaveHdr
->
dwFlags
&
WHDR_INQUEUE
)
return
WAVERR_STILLPLAYING
;
lpWaveHdr
->
dwFlags
|=
WHDR_PREPARED
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_DONE
;
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodUnprepare [internal]
*/
static
DWORD
wodUnprepare
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
,
LPWAVEHDR
lpWaveHdr
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %p, %08x);
\n
"
,
wDevID
,
wwo
,
lpWaveHdr
,
dwSize
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
lpWaveHdr
->
dwFlags
&
WHDR_INQUEUE
)
return
WAVERR_STILLPLAYING
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_PREPARED
;
lpWaveHdr
->
dwFlags
|=
WHDR_DONE
;
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodHelper_CheckForLoopBegin [internal]
*
* Check if the new waveheader is the beginning of a loop, and set up
* state if so.
* This is called with the WAVEOUT_INSTANCE lock held.
* Call from AudioUnit IO thread can't use Wine debug channels.
*/
static
void
wodHelper_CheckForLoopBegin
(
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
LPWAVEHDR
lpWaveHdr
=
wwo
->
lpPlayPtr
;
if
(
lpWaveHdr
->
dwFlags
&
WHDR_BEGINLOOP
)
{
if
(
wwo
->
lpLoopPtr
)
{
if
(
WOutDev
[
wwo
->
woID
].
warn_on
)
fprintf
(
stderr
,
"warn:winecoreaudio:wodHelper_CheckForLoopBegin Already in a loop. Discarding loop on this header (%p)
\n
"
,
lpWaveHdr
);
}
else
{
if
(
WOutDev
[
wwo
->
woID
].
trace_on
)
fprintf
(
stderr
,
"trace:winecoreaudio:wodHelper_CheckForLoopBegin Starting loop (%dx) with %p
\n
"
,
lpWaveHdr
->
dwLoops
,
lpWaveHdr
);
wwo
->
lpLoopPtr
=
lpWaveHdr
;
/* Windows does not touch WAVEHDR.dwLoops,
* so we need to make an internal copy */
wwo
->
dwLoops
=
lpWaveHdr
->
dwLoops
;
}
}
}
/**************************************************************************
* wodHelper_PlayPtrNext [internal]
*
* Advance the play pointer to the next waveheader, looping if required.
* This is called with the WAVEOUT_INSTANCE lock held.
* Call from AudioUnit IO thread can't use Wine debug channels.
*/
static
void
wodHelper_PlayPtrNext
(
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
BOOL
didLoopBack
=
FALSE
;
wwo
->
dwPartialOffset
=
0
;
if
((
wwo
->
lpPlayPtr
->
dwFlags
&
WHDR_ENDLOOP
)
&&
wwo
->
lpLoopPtr
)
{
/* We're at the end of a loop, loop if required */
if
(
wwo
->
dwLoops
>
1
)
{
wwo
->
dwLoops
--
;
wwo
->
lpPlayPtr
=
wwo
->
lpLoopPtr
;
didLoopBack
=
TRUE
;
}
else
{
wwo
->
lpLoopPtr
=
NULL
;
}
}
if
(
!
didLoopBack
)
{
/* We didn't loop back. Advance to the next wave header */
wwo
->
lpPlayPtr
=
wwo
->
lpPlayPtr
->
lpNext
;
if
(
wwo
->
lpPlayPtr
)
wodHelper_CheckForLoopBegin
(
wwo
);
}
}
/* Send the "done" notification for each WAVEHDR in a list. The list must be
* free-standing. It should not be part of a device instance's queue.
* This function must be called with the WAVEOUT_INSTANCE lock *not* held.
* Furthermore, it does not lock it, itself. That's because the callback to the
* application may prompt the application to operate on the device, and we don't
* want to deadlock.
*/
static
void
wodHelper_NotifyDoneForList
(
WINE_WAVEOUT_INSTANCE
*
wwo
,
LPWAVEHDR
lpWaveHdr
)
{
while
(
lpWaveHdr
)
{
LPWAVEHDR
lpNext
=
lpWaveHdr
->
lpNext
;
lpWaveHdr
->
lpNext
=
NULL
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_INQUEUE
;
lpWaveHdr
->
dwFlags
|=
WHDR_DONE
;
wodNotifyClient
(
wwo
,
WOM_DONE
,
(
DWORD_PTR
)
lpWaveHdr
,
0
);
lpWaveHdr
=
lpNext
;
}
}
/* if force is TRUE then notify the client that all the headers were completed
*/
static
void
wodHelper_NotifyCompletions
(
WINE_WAVEOUT_INSTANCE
*
wwo
,
BOOL
force
)
{
LPWAVEHDR
lpFirstDoneWaveHdr
=
NULL
;
OSSpinLockLock
(
&
wwo
->
lock
);
/* First, excise all of the done headers from the queue into
* a free-standing list. */
if
(
force
)
{
lpFirstDoneWaveHdr
=
wwo
->
lpQueuePtr
;
wwo
->
lpQueuePtr
=
NULL
;
}
else
{
LPWAVEHDR
lpWaveHdr
;
LPWAVEHDR
lpLastDoneWaveHdr
=
NULL
;
/* Start from lpQueuePtr and keep notifying until:
* - we hit an unwritten wavehdr
* - we hit the beginning of a running loop
* - we hit a wavehdr which hasn't finished playing
*/
for
(
lpWaveHdr
=
wwo
->
lpQueuePtr
;
lpWaveHdr
&&
lpWaveHdr
!=
wwo
->
lpPlayPtr
&&
lpWaveHdr
!=
wwo
->
lpLoopPtr
;
lpWaveHdr
=
lpWaveHdr
->
lpNext
)
{
if
(
!
lpFirstDoneWaveHdr
)
lpFirstDoneWaveHdr
=
lpWaveHdr
;
lpLastDoneWaveHdr
=
lpWaveHdr
;
}
if
(
lpLastDoneWaveHdr
)
{
wwo
->
lpQueuePtr
=
lpLastDoneWaveHdr
->
lpNext
;
lpLastDoneWaveHdr
->
lpNext
=
NULL
;
}
}
OSSpinLockUnlock
(
&
wwo
->
lock
);
/* Now, send the "done" notification for each header in our list. */
wodHelper_NotifyDoneForList
(
wwo
,
lpFirstDoneWaveHdr
);
}
/**************************************************************************
* wodWrite [internal]
*
*/
static
DWORD
wodWrite
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
,
LPWAVEHDR
lpWaveHdr
,
DWORD
dwSize
)
{
LPWAVEHDR
*
wh
;
TRACE
(
"(%u, %p, %p, %lu, %08X);
\n
"
,
wDevID
,
wwo
,
lpWaveHdr
,
(
unsigned
long
)
lpWaveHdr
->
dwBufferLength
,
dwSize
);
/* first, do the sanity checks... */
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad dev ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
lpWaveHdr
->
lpData
==
NULL
||
!
(
lpWaveHdr
->
dwFlags
&
WHDR_PREPARED
))
{
TRACE
(
"unprepared
\n
"
);
return
WAVERR_UNPREPARED
;
}
if
(
lpWaveHdr
->
dwFlags
&
WHDR_INQUEUE
)
{
TRACE
(
"still playing
\n
"
);
return
WAVERR_STILLPLAYING
;
}
lpWaveHdr
->
dwFlags
&=
~
WHDR_DONE
;
lpWaveHdr
->
dwFlags
|=
WHDR_INQUEUE
;
lpWaveHdr
->
lpNext
=
0
;
OSSpinLockLock
(
&
wwo
->
lock
);
/* insert buffer at the end of queue */
for
(
wh
=
&
(
wwo
->
lpQueuePtr
);
*
wh
;
wh
=
&
((
*
wh
)
->
lpNext
))
/* Do nothing */
;
*
wh
=
lpWaveHdr
;
if
(
!
wwo
->
lpPlayPtr
)
{
wwo
->
lpPlayPtr
=
lpWaveHdr
;
wodHelper_CheckForLoopBegin
(
wwo
);
wwo
->
dwPartialOffset
=
0
;
}
OSSpinLockUnlock
(
&
wwo
->
lock
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodPause [internal]
*/
static
DWORD
wodPause
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
OSStatus
status
;
TRACE
(
"(%u, %p);!
\n
"
,
wDevID
,
wwo
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
/* The order of the following operations is important since we can't hold
* the mutex while we make an Audio Unit call. Stop the Audio Unit before
* setting the PAUSED state. In wodRestart, the order is reversed. This
* guarantees that we can't get into a situation where the state is
* PLAYING but the Audio Unit isn't running. Although we can be in PAUSED
* state with the Audio Unit still running, that's harmless because the
* render callback will just produce silence.
*/
status
=
AudioOutputUnitStop
(
wwo
->
audioUnit
);
if
(
status
)
WARN
(
"AudioOutputUnitStop return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
OSSpinLockLock
(
&
wwo
->
lock
);
if
(
wwo
->
state
==
WINE_WS_PLAYING
)
wwo
->
state
=
WINE_WS_PAUSED
;
OSSpinLockUnlock
(
&
wwo
->
lock
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodRestart [internal]
*/
static
DWORD
wodRestart
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
OSStatus
status
;
TRACE
(
"(%u, %p);
\n
"
,
wDevID
,
wwo
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
/* The order of the following operations is important since we can't hold
* the mutex while we make an Audio Unit call. Set the PLAYING
* state before starting the Audio Unit. In wodPause, the order is
* reversed. This guarantees that we can't get into a situation where
* the state is PLAYING but the Audio Unit isn't running.
* Although we can be in PAUSED state with the Audio Unit still running,
* that's harmless because the render callback will just produce silence.
*/
OSSpinLockLock
(
&
wwo
->
lock
);
if
(
wwo
->
state
==
WINE_WS_PAUSED
)
wwo
->
state
=
WINE_WS_PLAYING
;
OSSpinLockUnlock
(
&
wwo
->
lock
);
status
=
AudioOutputUnitStart
(
wwo
->
audioUnit
);
if
(
status
)
{
ERR
(
"AudioOutputUnitStart return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
return
MMSYSERR_ERROR
;
/* FIXME return an error based on the OSStatus */
}
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodReset [internal]
*/
static
DWORD
wodReset
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
OSStatus
status
;
LPWAVEHDR
lpSavedQueuePtr
;
TRACE
(
"(%u, %p);
\n
"
,
wDevID
,
wwo
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
OSSpinLockLock
(
&
wwo
->
lock
);
if
(
wwo
->
state
==
WINE_WS_CLOSING
||
wwo
->
state
==
WINE_WS_OPENING
)
{
OSSpinLockUnlock
(
&
wwo
->
lock
);
WARN
(
"resetting a closed device
\n
"
);
return
MMSYSERR_INVALHANDLE
;
}
lpSavedQueuePtr
=
wwo
->
lpQueuePtr
;
wwo
->
lpPlayPtr
=
wwo
->
lpQueuePtr
=
wwo
->
lpLoopPtr
=
NULL
;
wwo
->
state
=
WINE_WS_PLAYING
;
wwo
->
dwPlayedTotal
=
0
;
wwo
->
dwPartialOffset
=
0
;
/* Clear partial wavehdr */
OSSpinLockUnlock
(
&
wwo
->
lock
);
status
=
AudioOutputUnitStart
(
wwo
->
audioUnit
);
if
(
status
)
{
ERR
(
"AudioOutputUnitStart return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
return
MMSYSERR_ERROR
;
/* FIXME return an error based on the OSStatus */
}
/* Now, send the "done" notification for each header in our list. */
/* Do this last so the reset operation is effectively complete before the
* app does whatever it's going to do in response to these notifications. */
wodHelper_NotifyDoneForList
(
wwo
,
lpSavedQueuePtr
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodBreakLoop [internal]
*/
static
DWORD
wodBreakLoop
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
)
{
TRACE
(
"(%u, %p);
\n
"
,
wDevID
,
wwo
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
OSSpinLockLock
(
&
wwo
->
lock
);
if
(
wwo
->
lpLoopPtr
!=
NULL
)
{
/* ensure exit at end of current loop */
wwo
->
dwLoops
=
1
;
}
OSSpinLockUnlock
(
&
wwo
->
lock
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodGetPosition [internal]
*/
static
DWORD
wodGetPosition
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
,
LPMMTIME
lpTime
,
DWORD
uSize
)
{
DWORD
val
;
TRACE
(
"(%u, %p, %p, %u);
\n
"
,
wDevID
,
wwo
,
lpTime
,
uSize
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
/* if null pointer to time structure return error */
if
(
lpTime
==
NULL
)
return
MMSYSERR_INVALPARAM
;
OSSpinLockLock
(
&
wwo
->
lock
);
val
=
wwo
->
dwPlayedTotal
;
OSSpinLockUnlock
(
&
wwo
->
lock
);
return
bytes_to_mmtime
(
lpTime
,
val
,
&
wwo
->
format
);
}
/**************************************************************************
* wodGetVolume [internal]
*/
static
DWORD
wodGetVolume
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
,
LPDWORD
lpdwVol
)
{
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
TRACE
(
"(%u, %p, %p);
\n
"
,
wDevID
,
wwo
,
lpdwVol
);
if
(
wwo
)
{
float
left
;
float
right
;
AudioUnit_GetVolume
(
wwo
->
audioUnit
,
&
left
,
&
right
);
*
lpdwVol
=
(
WORD
)(
left
*
0xFFFFl
)
+
((
WORD
)(
right
*
0xFFFFl
)
<<
16
);
}
else
*
lpdwVol
=
WOutDev
[
wDevID
].
device_volume
;
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodSetVolume [internal]
*/
static
DWORD
wodSetVolume
(
WORD
wDevID
,
WINE_WAVEOUT_INSTANCE
*
wwo
,
DWORD
dwParam
)
{
float
left
;
float
right
;
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
left
=
LOWORD
(
dwParam
)
/
65535
.
0
f
;
right
=
HIWORD
(
dwParam
)
/
65535
.
0
f
;
TRACE
(
"(%u, %p, %08x);
\n
"
,
wDevID
,
wwo
,
dwParam
);
if
(
wwo
)
AudioUnit_SetVolume
(
wwo
->
audioUnit
,
left
,
right
);
else
{
OSSpinLockLock
(
&
WOutDev
[
wDevID
].
lock
);
LIST_FOR_EACH_ENTRY
(
wwo
,
&
WOutDev
[
wDevID
].
instances
,
WINE_WAVEOUT_INSTANCE
,
entry
)
AudioUnit_SetVolume
(
wwo
->
audioUnit
,
left
,
right
);
OSSpinLockUnlock
(
&
WOutDev
[
wDevID
].
lock
);
WOutDev
[
wDevID
].
device_volume
=
dwParam
;
}
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodGetNumDevs [internal]
*/
static
DWORD
wodGetNumDevs
(
void
)
{
TRACE
(
"
\n
"
);
return
MAX_WAVEOUTDRV
;
}
/**************************************************************************
* wodDevInterfaceSize [internal]
*/
static
DWORD
wodDevInterfaceSize
(
UINT
wDevID
,
LPDWORD
dwParam1
)
{
TRACE
(
"(%u, %p)
\n
"
,
wDevID
,
dwParam1
);
*
dwParam1
=
MultiByteToWideChar
(
CP_UNIXCP
,
0
,
WOutDev
[
wDevID
].
cadev
->
interface_name
,
-
1
,
NULL
,
0
)
*
sizeof
(
WCHAR
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodDevInterface [internal]
*/
static
DWORD
wodDevInterface
(
UINT
wDevID
,
PWCHAR
dwParam1
,
DWORD
dwParam2
)
{
TRACE
(
"
\n
"
);
if
(
dwParam2
>=
MultiByteToWideChar
(
CP_UNIXCP
,
0
,
WOutDev
[
wDevID
].
cadev
->
interface_name
,
-
1
,
NULL
,
0
)
*
sizeof
(
WCHAR
))
{
MultiByteToWideChar
(
CP_UNIXCP
,
0
,
WOutDev
[
wDevID
].
cadev
->
interface_name
,
-
1
,
dwParam1
,
dwParam2
/
sizeof
(
WCHAR
));
return
MMSYSERR_NOERROR
;
}
return
MMSYSERR_INVALPARAM
;
}
/**************************************************************************
* widDsCreate [internal]
*/
static
DWORD
wodDsCreate
(
UINT
wDevID
,
PIDSDRIVER
*
drv
)
{
TRACE
(
"(%d,%p)
\n
"
,
wDevID
,
drv
);
FIXME
(
"DirectSound not implemented
\n
"
);
FIXME
(
"The (slower) DirectSound HEL mode will be used instead.
\n
"
);
return
MMSYSERR_NOTSUPPORTED
;
}
/**************************************************************************
* wodDsDesc [internal]
*/
static
DWORD
wodDsDesc
(
UINT
wDevID
,
PDSDRIVERDESC
desc
)
{
/* The DirectSound HEL will automatically wrap a non-DirectSound-capable
* driver in a DirectSound adaptor, thus allowing the driver to be used by
* DirectSound clients. However, it only does this if we respond
* successfully to the DRV_QUERYDSOUNDDESC message. It's enough to fill in
* the driver and device names of the description output parameter. */
*
desc
=
WOutDev
[
wDevID
].
cadev
->
ds_desc
;
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodMessage (WINECOREAUDIO.7)
*/
DWORD
WINAPI
CoreAudio_wodMessage
(
UINT
wDevID
,
UINT
wMsg
,
DWORD_PTR
dwUser
,
DWORD_PTR
dwParam1
,
DWORD_PTR
dwParam2
)
{
WINE_WAVEOUT_INSTANCE
*
wwo
=
(
WINE_WAVEOUT_INSTANCE
*
)
dwUser
;
TRACE
(
"(%u, %s, %p, %p, %p);
\n
"
,
wDevID
,
getMessage
(
wMsg
),
(
void
*
)
dwUser
,
(
void
*
)
dwParam1
,
(
void
*
)
dwParam2
);
switch
(
wMsg
)
{
case
DRVM_INIT
:
case
DRVM_EXIT
:
case
DRVM_ENABLE
:
case
DRVM_DISABLE
:
/* FIXME: Pretend this is supported */
return
0
;
case
WODM_OPEN
:
return
wodOpen
(
wDevID
,
(
WINE_WAVEOUT_INSTANCE
**
)
dwUser
,
(
LPWAVEOPENDESC
)
dwParam1
,
dwParam2
);
case
WODM_CLOSE
:
return
wodClose
(
wDevID
,
wwo
);
case
WODM_WRITE
:
return
wodWrite
(
wDevID
,
wwo
,
(
LPWAVEHDR
)
dwParam1
,
dwParam2
);
case
WODM_PAUSE
:
return
wodPause
(
wDevID
,
wwo
);
case
WODM_GETPOS
:
return
wodGetPosition
(
wDevID
,
wwo
,
(
LPMMTIME
)
dwParam1
,
dwParam2
);
case
WODM_BREAKLOOP
:
return
wodBreakLoop
(
wDevID
,
wwo
);
case
WODM_PREPARE
:
return
wodPrepare
(
wDevID
,
wwo
,
(
LPWAVEHDR
)
dwParam1
,
dwParam2
);
case
WODM_UNPREPARE
:
return
wodUnprepare
(
wDevID
,
wwo
,
(
LPWAVEHDR
)
dwParam1
,
dwParam2
);
case
WODM_GETDEVCAPS
:
return
wodGetDevCaps
(
wDevID
,
(
LPWAVEOUTCAPSW
)
dwParam1
,
dwParam2
);
case
WODM_GETNUMDEVS
:
return
wodGetNumDevs
();
case
WODM_GETPITCH
:
case
WODM_SETPITCH
:
case
WODM_GETPLAYBACKRATE
:
case
WODM_SETPLAYBACKRATE
:
return
MMSYSERR_NOTSUPPORTED
;
case
WODM_GETVOLUME
:
return
wodGetVolume
(
wDevID
,
wwo
,
(
LPDWORD
)
dwParam1
);
case
WODM_SETVOLUME
:
return
wodSetVolume
(
wDevID
,
wwo
,
dwParam1
);
case
WODM_RESTART
:
return
wodRestart
(
wDevID
,
wwo
);
case
WODM_RESET
:
return
wodReset
(
wDevID
,
wwo
);
case
DRV_QUERYDEVICEINTERFACESIZE
:
return
wodDevInterfaceSize
(
wDevID
,
(
LPDWORD
)
dwParam1
);
case
DRV_QUERYDEVICEINTERFACE
:
return
wodDevInterface
(
wDevID
,
(
PWCHAR
)
dwParam1
,
dwParam2
);
case
DRV_QUERYDSOUNDIFACE
:
return
wodDsCreate
(
wDevID
,
(
PIDSDRIVER
*
)
dwParam1
);
case
DRV_QUERYDSOUNDDESC
:
return
wodDsDesc
(
wDevID
,
(
PDSDRIVERDESC
)
dwParam1
);
default:
FIXME
(
"unknown message %d!
\n
"
,
wMsg
);
}
return
MMSYSERR_NOTSUPPORTED
;
}
/*======================================================================*
* Low level DSOUND implementation *
*======================================================================*/
typedef
struct
IDsDriverImpl
IDsDriverImpl
;
typedef
struct
IDsDriverBufferImpl
IDsDriverBufferImpl
;
struct
IDsDriverImpl
{
/* IUnknown fields */
const
IDsDriverVtbl
*
lpVtbl
;
DWORD
ref
;
/* IDsDriverImpl fields */
UINT
wDevID
;
IDsDriverBufferImpl
*
primary
;
};
struct
IDsDriverBufferImpl
{
/* IUnknown fields */
const
IDsDriverBufferVtbl
*
lpVtbl
;
DWORD
ref
;
/* IDsDriverBufferImpl fields */
IDsDriverImpl
*
drv
;
DWORD
buflen
;
};
/*
CoreAudio IO threaded callback,
we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
*/
OSStatus
CoreAudio_woAudioUnitIOProc
(
void
*
inRefCon
,
AudioUnitRenderActionFlags
*
ioActionFlags
,
const
AudioTimeStamp
*
inTimeStamp
,
UInt32
inBusNumber
,
UInt32
inNumberFrames
,
AudioBufferList
*
ioData
)
{
UInt32
buffer
;
WINE_WAVEOUT_INSTANCE
*
wwo
=
(
WINE_WAVEOUT_INSTANCE
*
)
inRefCon
;
int
needNotify
=
0
;
unsigned
int
dataNeeded
=
ioData
->
mBuffers
[
0
].
mDataByteSize
;
unsigned
int
dataProvided
=
0
;
OSSpinLockLock
(
&
wwo
->
lock
);
/* We might have been called before wwo has been completely filled out by
* wodOpen, or while it's being closed in wodClose. We have to do nothing
* in that case. The check of wwo->state below ensures that. */
while
(
dataNeeded
>
0
&&
wwo
->
state
==
WINE_WS_PLAYING
&&
wwo
->
lpPlayPtr
)
{
unsigned
int
available
=
wwo
->
lpPlayPtr
->
dwBufferLength
-
wwo
->
dwPartialOffset
;
unsigned
int
toCopy
;
if
(
available
>=
dataNeeded
)
toCopy
=
dataNeeded
;
else
toCopy
=
available
;
if
(
toCopy
>
0
)
{
memcpy
((
char
*
)
ioData
->
mBuffers
[
0
].
mData
+
dataProvided
,
wwo
->
lpPlayPtr
->
lpData
+
wwo
->
dwPartialOffset
,
toCopy
);
wwo
->
dwPartialOffset
+=
toCopy
;
wwo
->
dwPlayedTotal
+=
toCopy
;
dataProvided
+=
toCopy
;
dataNeeded
-=
toCopy
;
available
-=
toCopy
;
}
if
(
available
==
0
)
{
wodHelper_PlayPtrNext
(
wwo
);
needNotify
=
1
;
}
}
ioData
->
mBuffers
[
0
].
mDataByteSize
=
dataProvided
;
OSSpinLockUnlock
(
&
wwo
->
lock
);
/* We can't provide any more wave data. Fill the rest with silence. */
if
(
dataNeeded
>
0
)
{
if
(
!
dataProvided
)
*
ioActionFlags
|=
kAudioUnitRenderAction_OutputIsSilence
;
memset
((
char
*
)
ioData
->
mBuffers
[
0
].
mData
+
dataProvided
,
0
,
dataNeeded
);
dataProvided
+=
dataNeeded
;
dataNeeded
=
0
;
}
/* We only fill buffer 0. Set any others that might be requested to 0. */
for
(
buffer
=
1
;
buffer
<
ioData
->
mNumberBuffers
;
buffer
++
)
{
memset
(
ioData
->
mBuffers
[
buffer
].
mData
,
0
,
ioData
->
mBuffers
[
buffer
].
mDataByteSize
);
}
if
(
needNotify
)
wodSendNotifyCompletionsMessage
(
wwo
);
return
noErr
;
}
/*======================================================================*
* Low level WAVE IN implementation *
*======================================================================*/
/**************************************************************************
* widNotifyClient [internal]
*/
static
void
widNotifyClient
(
WINE_WAVEIN
*
wwi
,
WORD
wMsg
,
DWORD_PTR
dwParam1
,
DWORD_PTR
dwParam2
)
{
TRACE
(
"wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX
\n
"
,
wMsg
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
case
WIM_OPEN
:
case
WIM_CLOSE
:
case
WIM_DATA
:
DriverCallback
(
wwi
->
waveDesc
.
dwCallback
,
wwi
->
wFlags
,
(
HDRVR
)
wwi
->
waveDesc
.
hWave
,
wMsg
,
wwi
->
waveDesc
.
dwInstance
,
dwParam1
,
dwParam2
);
break
;
default:
FIXME
(
"Unknown callback message %u
\n
"
,
wMsg
);
}
}
/**************************************************************************
* widHelper_NotifyCompletions [internal]
*/
static
void
widHelper_NotifyCompletions
(
WINE_WAVEIN
*
wwi
)
{
LPWAVEHDR
lpWaveHdr
;
LPWAVEHDR
lpFirstDoneWaveHdr
=
NULL
;
LPWAVEHDR
lpLastDoneWaveHdr
=
NULL
;
OSSpinLockLock
(
&
wwi
->
lock
);
/* First, excise all of the done headers from the queue into
* a free-standing list. */
/* Start from lpQueuePtr and keep notifying until:
* - we hit an unfilled wavehdr
* - we hit the end of the list
*/
for
(
lpWaveHdr
=
wwi
->
lpQueuePtr
;
lpWaveHdr
&&
lpWaveHdr
->
dwBytesRecorded
>=
lpWaveHdr
->
dwBufferLength
;
lpWaveHdr
=
lpWaveHdr
->
lpNext
)
{
if
(
!
lpFirstDoneWaveHdr
)
lpFirstDoneWaveHdr
=
lpWaveHdr
;
lpLastDoneWaveHdr
=
lpWaveHdr
;
}
if
(
lpLastDoneWaveHdr
)
{
wwi
->
lpQueuePtr
=
lpLastDoneWaveHdr
->
lpNext
;
lpLastDoneWaveHdr
->
lpNext
=
NULL
;
}
OSSpinLockUnlock
(
&
wwi
->
lock
);
/* Now, send the "done" notification for each header in our list. */
lpWaveHdr
=
lpFirstDoneWaveHdr
;
while
(
lpWaveHdr
)
{
LPWAVEHDR
lpNext
=
lpWaveHdr
->
lpNext
;
lpWaveHdr
->
lpNext
=
NULL
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_INQUEUE
;
lpWaveHdr
->
dwFlags
|=
WHDR_DONE
;
widNotifyClient
(
wwi
,
WIM_DATA
,
(
DWORD_PTR
)
lpWaveHdr
,
0
);
lpWaveHdr
=
lpNext
;
}
}
/**************************************************************************
* widGetDevCaps [internal]
*/
static
DWORD
widGetDevCaps
(
WORD
wDevID
,
LPWAVEINCAPSW
lpCaps
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %u);
\n
"
,
wDevID
,
lpCaps
,
dwSize
);
if
(
lpCaps
==
NULL
)
return
MMSYSERR_NOTENABLED
;
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
TRACE
(
"MAX_WAVEINDRV reached !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
memcpy
(
lpCaps
,
&
WInDev
[
wDevID
].
caps
,
min
(
dwSize
,
sizeof
(
*
lpCaps
)));
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* widHelper_DestroyAudioBufferList [internal]
* Convenience function to dispose of our audio buffers
*/
static
void
widHelper_DestroyAudioBufferList
(
AudioBufferList
*
list
)
{
if
(
list
)
{
UInt32
i
;
for
(
i
=
0
;
i
<
list
->
mNumberBuffers
;
i
++
)
{
if
(
list
->
mBuffers
[
i
].
mData
)
HeapFree
(
GetProcessHeap
(),
0
,
list
->
mBuffers
[
i
].
mData
);
}
HeapFree
(
GetProcessHeap
(),
0
,
list
);
}
}
#define AUDIOBUFFERLISTSIZE(numBuffers) (offsetof(AudioBufferList, mBuffers) + (numBuffers) * sizeof(AudioBuffer))
/**************************************************************************
* widHelper_AllocateAudioBufferList [internal]
* Convenience function to allocate our audio buffers
*/
static
AudioBufferList
*
widHelper_AllocateAudioBufferList
(
UInt32
numChannels
,
UInt32
bitsPerChannel
,
UInt32
bufferFrames
,
BOOL
interleaved
)
{
UInt32
numBuffers
;
UInt32
channelsPerFrame
;
UInt32
bytesPerFrame
;
UInt32
bytesPerBuffer
;
AudioBufferList
*
list
;
UInt32
i
;
if
(
interleaved
)
{
/* For interleaved audio, we allocate one buffer for all channels. */
numBuffers
=
1
;
channelsPerFrame
=
numChannels
;
}
else
{
numBuffers
=
numChannels
;
channelsPerFrame
=
1
;
}
bytesPerFrame
=
bitsPerChannel
*
channelsPerFrame
/
8
;
bytesPerBuffer
=
bytesPerFrame
*
bufferFrames
;
list
=
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
AUDIOBUFFERLISTSIZE
(
numBuffers
));
if
(
list
==
NULL
)
return
NULL
;
list
->
mNumberBuffers
=
numBuffers
;
for
(
i
=
0
;
i
<
numBuffers
;
++
i
)
{
list
->
mBuffers
[
i
].
mNumberChannels
=
channelsPerFrame
;
list
->
mBuffers
[
i
].
mDataByteSize
=
bytesPerBuffer
;
list
->
mBuffers
[
i
].
mData
=
HeapAlloc
(
GetProcessHeap
(),
0
,
bytesPerBuffer
);
if
(
list
->
mBuffers
[
i
].
mData
==
NULL
)
{
widHelper_DestroyAudioBufferList
(
list
);
return
NULL
;
}
}
return
list
;
}
/**************************************************************************
* widOpen [internal]
*/
static
DWORD
widOpen
(
WORD
wDevID
,
LPWAVEOPENDESC
lpDesc
,
DWORD
dwFlags
)
{
WINE_WAVEIN
*
wwi
;
UInt32
frameCount
;
TRACE
(
"(%u, %p, %08X);
\n
"
,
wDevID
,
lpDesc
,
dwFlags
);
if
(
lpDesc
==
NULL
)
{
WARN
(
"Invalid Parameter !
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
TRACE
(
"MAX_WAVEINDRV reached !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
TRACE
(
"Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
,
lpDesc
->
lpFormat
->
wBitsPerSample
);
if
(
!
supportedFormat
(
lpDesc
->
lpFormat
)
||
lpDesc
->
lpFormat
->
nSamplesPerSec
!=
AudioUnit_GetInputDeviceSampleRate
()
)
{
WARN
(
"Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
,
lpDesc
->
lpFormat
->
wBitsPerSample
);
return
WAVERR_BADFORMAT
;
}
if
(
dwFlags
&
WAVE_FORMAT_QUERY
)
{
TRACE
(
"Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
);
return
MMSYSERR_NOERROR
;
}
/* nBlockAlign and nAvgBytesPerSec are output variables for dsound */
if
(
lpDesc
->
lpFormat
->
nBlockAlign
!=
lpDesc
->
lpFormat
->
nChannels
*
lpDesc
->
lpFormat
->
wBitsPerSample
/
8
)
{
lpDesc
->
lpFormat
->
nBlockAlign
=
lpDesc
->
lpFormat
->
nChannels
*
lpDesc
->
lpFormat
->
wBitsPerSample
/
8
;
WARN
(
"Fixing nBlockAlign
\n
"
);
}
if
(
lpDesc
->
lpFormat
->
nAvgBytesPerSec
!=
lpDesc
->
lpFormat
->
nSamplesPerSec
*
lpDesc
->
lpFormat
->
nBlockAlign
)
{
lpDesc
->
lpFormat
->
nAvgBytesPerSec
=
lpDesc
->
lpFormat
->
nSamplesPerSec
*
lpDesc
->
lpFormat
->
nBlockAlign
;
WARN
(
"Fixing nAvgBytesPerSec
\n
"
);
}
wwi
=
&
WInDev
[
wDevID
];
if
(
!
OSSpinLockTry
(
&
wwi
->
lock
))
return
MMSYSERR_ALLOCATED
;
if
(
wwi
->
state
!=
WINE_WS_CLOSED
)
{
OSSpinLockUnlock
(
&
wwi
->
lock
);
return
MMSYSERR_ALLOCATED
;
}
wwi
->
state
=
WINE_WS_STOPPED
;
wwi
->
wFlags
=
HIWORD
(
dwFlags
&
CALLBACK_TYPEMASK
);
wwi
->
waveDesc
=
*
lpDesc
;
copyFormat
(
lpDesc
->
lpFormat
,
&
wwi
->
format
);
wwi
->
dwTotalRecorded
=
0
;
wwi
->
trace_on
=
TRACE_ON
(
wave
);
wwi
->
warn_on
=
WARN_ON
(
wave
);
wwi
->
err_on
=
ERR_ON
(
wave
);
if
(
!
AudioUnit_CreateInputUnit
(
wwi
,
&
wwi
->
audioUnit
,
wwi
->
format
.
wf
.
nChannels
,
wwi
->
format
.
wf
.
nSamplesPerSec
,
wwi
->
format
.
wBitsPerSample
,
&
frameCount
))
{
OSSpinLockUnlock
(
&
wwi
->
lock
);
ERR
(
"AudioUnit_CreateInputUnit failed
\n
"
);
return
MMSYSERR_ERROR
;
}
/* Allocate our audio buffers */
wwi
->
bufferList
=
widHelper_AllocateAudioBufferList
(
wwi
->
format
.
wf
.
nChannels
,
wwi
->
format
.
wBitsPerSample
,
frameCount
,
TRUE
);
if
(
wwi
->
bufferList
==
NULL
)
{
AudioUnitUninitialize
(
wwi
->
audioUnit
);
AudioUnit_CloseAudioUnit
(
wwi
->
audioUnit
);
OSSpinLockUnlock
(
&
wwi
->
lock
);
ERR
(
"Failed to allocate buffer list
\n
"
);
return
MMSYSERR_NOMEM
;
}
/* Keep a copy of the buffer list structure (but not the buffers themselves)
* in case AudioUnitRender clobbers the original, as it tends to do. */
wwi
->
bufferListCopy
=
HeapAlloc
(
GetProcessHeap
(),
0
,
AUDIOBUFFERLISTSIZE
(
wwi
->
bufferList
->
mNumberBuffers
));
if
(
wwi
->
bufferListCopy
==
NULL
)
{
widHelper_DestroyAudioBufferList
(
wwi
->
bufferList
);
AudioUnitUninitialize
(
wwi
->
audioUnit
);
AudioUnit_CloseAudioUnit
(
wwi
->
audioUnit
);
OSSpinLockUnlock
(
&
wwi
->
lock
);
ERR
(
"Failed to allocate buffer list copy
\n
"
);
return
MMSYSERR_NOMEM
;
}
memcpy
(
wwi
->
bufferListCopy
,
wwi
->
bufferList
,
AUDIOBUFFERLISTSIZE
(
wwi
->
bufferList
->
mNumberBuffers
));
OSSpinLockUnlock
(
&
wwi
->
lock
);
widNotifyClient
(
wwi
,
WIM_OPEN
,
0L
,
0L
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* widClose [internal]
*/
static
DWORD
widClose
(
WORD
wDevID
)
{
DWORD
ret
=
MMSYSERR_NOERROR
;
WINE_WAVEIN
*
wwi
;
OSStatus
err
;
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
wwi
=
&
WInDev
[
wDevID
];
OSSpinLockLock
(
&
wwi
->
lock
);
if
(
wwi
->
state
==
WINE_WS_CLOSED
||
wwi
->
state
==
WINE_WS_CLOSING
)
{
WARN
(
"Device already closed.
\n
"
);
ret
=
MMSYSERR_INVALHANDLE
;
}
else
if
(
wwi
->
lpQueuePtr
)
{
WARN
(
"Buffers in queue.
\n
"
);
ret
=
WAVERR_STILLPLAYING
;
}
else
{
wwi
->
state
=
WINE_WS_CLOSING
;
}
OSSpinLockUnlock
(
&
wwi
->
lock
);
if
(
ret
!=
MMSYSERR_NOERROR
)
return
ret
;
/* Clean up and close the audio unit. This has to be done without
* wwi->lock being held to avoid deadlock. AudioUnitUninitialize will
* grab an internal Core Audio lock while waiting for the device work
* thread to exit. Meanwhile the device work thread may be holding
* that lock and trying to grab the wwi->lock in the callback. */
err
=
AudioUnitUninitialize
(
wwi
->
audioUnit
);
if
(
err
)
ERR
(
"AudioUnitUninitialize return %s
\n
"
,
wine_dbgstr_fourcc
(
err
));
if
(
!
AudioUnit_CloseAudioUnit
(
wwi
->
audioUnit
))
ERR
(
"Can't close AudioUnit
\n
"
);
OSSpinLockLock
(
&
wwi
->
lock
);
assert
(
wwi
->
state
==
WINE_WS_CLOSING
);
/* Dellocate our audio buffers */
widHelper_DestroyAudioBufferList
(
wwi
->
bufferList
);
wwi
->
bufferList
=
NULL
;
HeapFree
(
GetProcessHeap
(),
0
,
wwi
->
bufferListCopy
);
wwi
->
bufferListCopy
=
NULL
;
wwi
->
audioUnit
=
NULL
;
wwi
->
state
=
WINE_WS_CLOSED
;
OSSpinLockUnlock
(
&
wwi
->
lock
);
widNotifyClient
(
wwi
,
WIM_CLOSE
,
0L
,
0L
);
return
ret
;
}
/**************************************************************************
* widAddBuffer [internal]
*/
static
DWORD
widAddBuffer
(
WORD
wDevID
,
LPWAVEHDR
lpWaveHdr
,
DWORD
dwSize
)
{
DWORD
ret
=
MMSYSERR_NOERROR
;
WINE_WAVEIN
*
wwi
;
TRACE
(
"(%u, %p, %08X);
\n
"
,
wDevID
,
lpWaveHdr
,
dwSize
);
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
WARN
(
"invalid device ID
\n
"
);
return
MMSYSERR_INVALHANDLE
;
}
if
(
!
(
lpWaveHdr
->
dwFlags
&
WHDR_PREPARED
))
{
TRACE
(
"never been prepared !
\n
"
);
return
WAVERR_UNPREPARED
;
}
if
(
lpWaveHdr
->
dwFlags
&
WHDR_INQUEUE
)
{
TRACE
(
"header already in use !
\n
"
);
return
WAVERR_STILLPLAYING
;
}
wwi
=
&
WInDev
[
wDevID
];
OSSpinLockLock
(
&
wwi
->
lock
);
if
(
wwi
->
state
==
WINE_WS_CLOSED
||
wwi
->
state
==
WINE_WS_CLOSING
)
{
WARN
(
"Trying to add buffer to closed device.
\n
"
);
ret
=
MMSYSERR_INVALHANDLE
;
}
else
{
LPWAVEHDR
*
wh
;
lpWaveHdr
->
dwFlags
|=
WHDR_INQUEUE
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_DONE
;
lpWaveHdr
->
dwBytesRecorded
=
0
;
lpWaveHdr
->
lpNext
=
NULL
;
/* insert buffer at end of queue */
for
(
wh
=
&
(
wwi
->
lpQueuePtr
);
*
wh
;
wh
=
&
((
*
wh
)
->
lpNext
))
/* Do nothing */
;
*
wh
=
lpWaveHdr
;
}
OSSpinLockUnlock
(
&
wwi
->
lock
);
return
ret
;
}
/**************************************************************************
* widStart [internal]
*/
static
DWORD
widStart
(
WORD
wDevID
)
{
DWORD
ret
=
MMSYSERR_NOERROR
;
WINE_WAVEIN
*
wwi
;
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
WARN
(
"invalid device ID
\n
"
);
return
MMSYSERR_INVALHANDLE
;
}
/* The order of the following operations is important since we can't hold
* the mutex while we make an Audio Unit call. Set the PLAYING state
* before starting the Audio Unit. In widStop, the order is reversed.
* This guarantees that we can't get into a situation where the state is
* PLAYING but the Audio Unit isn't running. Although we can be in STOPPED
* state with the Audio Unit still running, that's harmless because the
* input callback will just throw away the sound data.
*/
wwi
=
&
WInDev
[
wDevID
];
OSSpinLockLock
(
&
wwi
->
lock
);
if
(
wwi
->
state
==
WINE_WS_CLOSED
||
wwi
->
state
==
WINE_WS_CLOSING
)
{
WARN
(
"Trying to start closed device.
\n
"
);
ret
=
MMSYSERR_INVALHANDLE
;
}
else
wwi
->
state
=
WINE_WS_PLAYING
;
OSSpinLockUnlock
(
&
wwi
->
lock
);
if
(
ret
==
MMSYSERR_NOERROR
)
{
/* Start pulling for audio data */
OSStatus
err
=
AudioOutputUnitStart
(
wwi
->
audioUnit
);
if
(
err
!=
noErr
)
ERR
(
"Failed to start AU: %08lx
\n
"
,
err
);
TRACE
(
"Recording started...
\n
"
);
}
return
ret
;
}
/**************************************************************************
* widStop [internal]
*/
static
DWORD
widStop
(
WORD
wDevID
)
{
DWORD
ret
=
MMSYSERR_NOERROR
;
WINE_WAVEIN
*
wwi
;
WAVEHDR
*
lpWaveHdr
=
NULL
;
OSStatus
err
;
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
WARN
(
"invalid device ID
\n
"
);
return
MMSYSERR_INVALHANDLE
;
}
wwi
=
&
WInDev
[
wDevID
];
/* The order of the following operations is important since we can't hold
* the mutex while we make an Audio Unit call. Stop the Audio Unit before
* setting the STOPPED state. In widStart, the order is reversed. This
* guarantees that we can't get into a situation where the state is
* PLAYING but the Audio Unit isn't running. Although we can be in STOPPED
* state with the Audio Unit still running, that's harmless because the
* input callback will just throw away the sound data.
*/
err
=
AudioOutputUnitStop
(
wwi
->
audioUnit
);
if
(
err
!=
noErr
)
WARN
(
"Failed to stop AU: %08lx
\n
"
,
err
);
TRACE
(
"Recording stopped.
\n
"
);
OSSpinLockLock
(
&
wwi
->
lock
);
if
(
wwi
->
state
==
WINE_WS_CLOSED
||
wwi
->
state
==
WINE_WS_CLOSING
)
{
WARN
(
"Trying to stop closed device.
\n
"
);
ret
=
MMSYSERR_INVALHANDLE
;
}
else
if
(
wwi
->
state
!=
WINE_WS_STOPPED
)
{
wwi
->
state
=
WINE_WS_STOPPED
;
/* If there's a buffer in progress, it's done. Remove it from the
* queue so that we can return it to the app, below. */
if
(
wwi
->
lpQueuePtr
)
{
lpWaveHdr
=
wwi
->
lpQueuePtr
;
wwi
->
lpQueuePtr
=
lpWaveHdr
->
lpNext
;
}
}
OSSpinLockUnlock
(
&
wwi
->
lock
);
if
(
lpWaveHdr
)
{
lpWaveHdr
->
lpNext
=
NULL
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_INQUEUE
;
lpWaveHdr
->
dwFlags
|=
WHDR_DONE
;
widNotifyClient
(
wwi
,
WIM_DATA
,
(
DWORD_PTR
)
lpWaveHdr
,
0
);
}
return
ret
;
}
/**************************************************************************
* widGetPos [internal]
*/
static
DWORD
widGetPos
(
WORD
wDevID
,
LPMMTIME
lpTime
,
UINT
size
)
{
DWORD
val
;
WINE_WAVEIN
*
wwi
;
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
WARN
(
"invalid device ID
\n
"
);
return
MMSYSERR_INVALHANDLE
;
}
wwi
=
&
WInDev
[
wDevID
];
OSSpinLockLock
(
&
WInDev
[
wDevID
].
lock
);
val
=
wwi
->
dwTotalRecorded
;
OSSpinLockUnlock
(
&
WInDev
[
wDevID
].
lock
);
return
bytes_to_mmtime
(
lpTime
,
val
,
&
wwi
->
format
);
}
/**************************************************************************
* widReset [internal]
*/
static
DWORD
widReset
(
WORD
wDevID
)
{
DWORD
ret
=
MMSYSERR_NOERROR
;
WINE_WAVEIN
*
wwi
;
WAVEHDR
*
lpWaveHdr
=
NULL
;
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEINDRV
)
{
WARN
(
"invalid device ID
\n
"
);
return
MMSYSERR_INVALHANDLE
;
}
wwi
=
&
WInDev
[
wDevID
];
OSSpinLockLock
(
&
wwi
->
lock
);
if
(
wwi
->
state
==
WINE_WS_CLOSED
||
wwi
->
state
==
WINE_WS_CLOSING
)
{
WARN
(
"Trying to reset a closed device.
\n
"
);
ret
=
MMSYSERR_INVALHANDLE
;
}
else
{
lpWaveHdr
=
wwi
->
lpQueuePtr
;
wwi
->
lpQueuePtr
=
NULL
;
wwi
->
state
=
WINE_WS_STOPPED
;
wwi
->
dwTotalRecorded
=
0
;
}
OSSpinLockUnlock
(
&
wwi
->
lock
);
if
(
ret
==
MMSYSERR_NOERROR
)
{
OSStatus
err
=
AudioOutputUnitStop
(
wwi
->
audioUnit
);
if
(
err
!=
noErr
)
WARN
(
"Failed to stop AU: %08lx
\n
"
,
err
);
TRACE
(
"Recording stopped.
\n
"
);
}
while
(
lpWaveHdr
)
{
WAVEHDR
*
lpNext
=
lpWaveHdr
->
lpNext
;
lpWaveHdr
->
lpNext
=
NULL
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_INQUEUE
;
lpWaveHdr
->
dwFlags
|=
WHDR_DONE
;
widNotifyClient
(
wwi
,
WIM_DATA
,
(
DWORD_PTR
)
lpWaveHdr
,
0
);
lpWaveHdr
=
lpNext
;
}
return
ret
;
}
/**************************************************************************
* widGetNumDevs [internal]
*/
static
DWORD
widGetNumDevs
(
void
)
{
return
MAX_WAVEINDRV
;
}
/**************************************************************************
* widDevInterfaceSize [internal]
*/
static
DWORD
widDevInterfaceSize
(
UINT
wDevID
,
LPDWORD
dwParam1
)
{
TRACE
(
"(%u, %p)
\n
"
,
wDevID
,
dwParam1
);
*
dwParam1
=
MultiByteToWideChar
(
CP_UNIXCP
,
0
,
WInDev
[
wDevID
].
interface_name
,
-
1
,
NULL
,
0
)
*
sizeof
(
WCHAR
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* widDevInterface [internal]
*/
static
DWORD
widDevInterface
(
UINT
wDevID
,
PWCHAR
dwParam1
,
DWORD
dwParam2
)
{
if
(
dwParam2
>=
MultiByteToWideChar
(
CP_UNIXCP
,
0
,
WInDev
[
wDevID
].
interface_name
,
-
1
,
NULL
,
0
)
*
sizeof
(
WCHAR
))
{
MultiByteToWideChar
(
CP_UNIXCP
,
0
,
WInDev
[
wDevID
].
interface_name
,
-
1
,
dwParam1
,
dwParam2
/
sizeof
(
WCHAR
));
return
MMSYSERR_NOERROR
;
}
return
MMSYSERR_INVALPARAM
;
}
/**************************************************************************
* widDsCreate [internal]
*/
static
DWORD
widDsCreate
(
UINT
wDevID
,
PIDSCDRIVER
*
drv
)
{
TRACE
(
"(%d,%p)
\n
"
,
wDevID
,
drv
);
FIXME
(
"DirectSoundCapture not implemented
\n
"
);
FIXME
(
"The (slower) DirectSound HEL mode will be used instead.
\n
"
);
return
MMSYSERR_NOTSUPPORTED
;
}
/**************************************************************************
* widDsDesc [internal]
*/
static
DWORD
widDsDesc
(
UINT
wDevID
,
PDSDRIVERDESC
desc
)
{
/* The DirectSound HEL will automatically wrap a non-DirectSound-capable
* driver in a DirectSound adaptor, thus allowing the driver to be used by
* DirectSound clients. However, it only does this if we respond
* successfully to the DRV_QUERYDSOUNDDESC message. It's enough to fill in
* the driver and device names of the description output parameter. */
memset
(
desc
,
0
,
sizeof
(
*
desc
));
lstrcpynA
(
desc
->
szDrvname
,
"winecoreaudio.drv"
,
sizeof
(
desc
->
szDrvname
)
-
1
);
lstrcpynA
(
desc
->
szDesc
,
WInDev
[
wDevID
].
interface_name
,
sizeof
(
desc
->
szDesc
)
-
1
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* widMessage (WINECOREAUDIO.6)
*/
DWORD
WINAPI
CoreAudio_widMessage
(
WORD
wDevID
,
WORD
wMsg
,
DWORD
dwUser
,
DWORD
dwParam1
,
DWORD
dwParam2
)
{
TRACE
(
"(%u, %04X, %08X, %08X, %08X);
\n
"
,
wDevID
,
wMsg
,
dwUser
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
case
DRVM_INIT
:
case
DRVM_EXIT
:
case
DRVM_ENABLE
:
case
DRVM_DISABLE
:
/* FIXME: Pretend this is supported */
return
0
;
case
WIDM_OPEN
:
return
widOpen
(
wDevID
,
(
LPWAVEOPENDESC
)
dwParam1
,
dwParam2
);
case
WIDM_CLOSE
:
return
widClose
(
wDevID
);
case
WIDM_ADDBUFFER
:
return
widAddBuffer
(
wDevID
,
(
LPWAVEHDR
)
dwParam1
,
dwParam2
);
case
WIDM_PREPARE
:
return
MMSYSERR_NOTSUPPORTED
;
case
WIDM_UNPREPARE
:
return
MMSYSERR_NOTSUPPORTED
;
case
WIDM_GETDEVCAPS
:
return
widGetDevCaps
(
wDevID
,
(
LPWAVEINCAPSW
)
dwParam1
,
dwParam2
);
case
WIDM_GETNUMDEVS
:
return
widGetNumDevs
();
case
WIDM_RESET
:
return
widReset
(
wDevID
);
case
WIDM_START
:
return
widStart
(
wDevID
);
case
WIDM_STOP
:
return
widStop
(
wDevID
);
case
WIDM_GETPOS
:
return
widGetPos
(
wDevID
,
(
LPMMTIME
)
dwParam1
,
(
UINT
)
dwParam2
);
case
DRV_QUERYDEVICEINTERFACESIZE
:
return
widDevInterfaceSize
(
wDevID
,
(
LPDWORD
)
dwParam1
);
case
DRV_QUERYDEVICEINTERFACE
:
return
widDevInterface
(
wDevID
,
(
PWCHAR
)
dwParam1
,
dwParam2
);
case
DRV_QUERYDSOUNDIFACE
:
return
widDsCreate
(
wDevID
,
(
PIDSCDRIVER
*
)
dwParam1
);
case
DRV_QUERYDSOUNDDESC
:
return
widDsDesc
(
wDevID
,
(
PDSDRIVERDESC
)
dwParam1
);
default:
FIXME
(
"unknown message %d!
\n
"
,
wMsg
);
}
return
MMSYSERR_NOTSUPPORTED
;
}
OSStatus
CoreAudio_wiAudioUnitIOProc
(
void
*
inRefCon
,
AudioUnitRenderActionFlags
*
ioActionFlags
,
const
AudioTimeStamp
*
inTimeStamp
,
UInt32
inBusNumber
,
UInt32
inNumberFrames
,
AudioBufferList
*
ioData
)
{
WINE_WAVEIN
*
wwi
=
(
WINE_WAVEIN
*
)
inRefCon
;
OSStatus
err
=
noErr
;
BOOL
needNotify
=
FALSE
;
WAVEHDR
*
lpStorePtr
;
unsigned
int
dataToStore
;
unsigned
int
dataStored
=
0
;
if
(
wwi
->
trace_on
)
fprintf
(
stderr
,
"trace:wave:CoreAudio_wiAudioUnitIOProc (ioActionFlags = %08lx, "
"inTimeStamp = { %f, %x%08x, %f, %x%08x, %08lx }, inBusNumber = %lu, inNumberFrames = %lu)
\n
"
,
*
ioActionFlags
,
inTimeStamp
->
mSampleTime
,
(
DWORD
)(
inTimeStamp
->
mHostTime
>>
32
),
(
DWORD
)
inTimeStamp
->
mHostTime
,
inTimeStamp
->
mRateScalar
,
(
DWORD
)(
inTimeStamp
->
mWordClockTime
>>
32
),
(
DWORD
)
inTimeStamp
->
mWordClockTime
,
inTimeStamp
->
mFlags
,
inBusNumber
,
inNumberFrames
);
/* Render into audio buffer */
/* FIXME: implement sample rate conversion on input. This will require
* a different render strategy. We'll need to buffer the sound data
* received here and pass it off to an AUConverter in another thread. */
err
=
AudioUnitRender
(
wwi
->
audioUnit
,
ioActionFlags
,
inTimeStamp
,
inBusNumber
,
inNumberFrames
,
wwi
->
bufferList
);
if
(
err
)
{
if
(
wwi
->
err_on
)
fprintf
(
stderr
,
"err:wave:CoreAudio_wiAudioUnitIOProc AudioUnitRender failed with error %li
\n
"
,
err
);
return
err
;
}
/* Copy from audio buffer to the wavehdrs */
dataToStore
=
wwi
->
bufferList
->
mBuffers
[
0
].
mDataByteSize
;
OSSpinLockLock
(
&
wwi
->
lock
);
lpStorePtr
=
wwi
->
lpQueuePtr
;
/* We might have been called while the waveIn device is being closed in
* widClose. We have to do nothing in that case. The check of wwi->state
* below ensures that. */
while
(
dataToStore
>
0
&&
wwi
->
state
==
WINE_WS_PLAYING
&&
lpStorePtr
)
{
unsigned
int
room
=
lpStorePtr
->
dwBufferLength
-
lpStorePtr
->
dwBytesRecorded
;
unsigned
int
toCopy
;
if
(
wwi
->
trace_on
)
fprintf
(
stderr
,
"trace:wave:CoreAudio_wiAudioUnitIOProc Looking to store %u bytes to wavehdr %p, which has room for %u
\n
"
,
dataToStore
,
lpStorePtr
,
room
);
if
(
room
>=
dataToStore
)
toCopy
=
dataToStore
;
else
toCopy
=
room
;
if
(
toCopy
>
0
)
{
memcpy
(
lpStorePtr
->
lpData
+
lpStorePtr
->
dwBytesRecorded
,
(
char
*
)
wwi
->
bufferList
->
mBuffers
[
0
].
mData
+
dataStored
,
toCopy
);
lpStorePtr
->
dwBytesRecorded
+=
toCopy
;
wwi
->
dwTotalRecorded
+=
toCopy
;
dataStored
+=
toCopy
;
dataToStore
-=
toCopy
;
room
-=
toCopy
;
}
if
(
room
==
0
)
{
lpStorePtr
=
lpStorePtr
->
lpNext
;
needNotify
=
TRUE
;
}
}
OSSpinLockUnlock
(
&
wwi
->
lock
);
/* Restore the audio buffer list structure from backup, in case
* AudioUnitRender clobbered it. (It modifies mDataByteSize and may even
* give us a different mData buffer to avoid a copy.) */
memcpy
(
wwi
->
bufferList
,
wwi
->
bufferListCopy
,
AUDIOBUFFERLISTSIZE
(
wwi
->
bufferList
->
mNumberBuffers
));
if
(
needNotify
)
wodSendNotifyInputCompletionsMessage
(
wwi
);
return
err
;
}
dlls/winecoreaudio.drv/audiounit.c
View file @
a773b16d
...
...
@@ -85,55 +85,6 @@ static const char *streamDescription(const AudioStreamBasicDescription* stream)
stream
->
mBitsPerChannel
);
}
extern
OSStatus
CoreAudio_woAudioUnitIOProc
(
void
*
inRefCon
,
AudioUnitRenderActionFlags
*
ioActionFlags
,
const
AudioTimeStamp
*
inTimeStamp
,
UInt32
inBusNumber
,
UInt32
inNumberFrames
,
AudioBufferList
*
ioData
);
extern
OSStatus
CoreAudio_wiAudioUnitIOProc
(
void
*
inRefCon
,
AudioUnitRenderActionFlags
*
ioActionFlags
,
const
AudioTimeStamp
*
inTimeStamp
,
UInt32
inBusNumber
,
UInt32
inNumberFrames
,
AudioBufferList
*
ioData
);
int
AudioUnit_CreateDefaultAudioUnit
(
void
*
wwo
,
AudioUnit
*
au
)
{
OSStatus
err
;
AudioComponent
comp
;
AudioComponentDescription
desc
;
AURenderCallbackStruct
callbackStruct
;
TRACE
(
"
\n
"
);
desc
.
componentType
=
kAudioUnitType_Output
;
desc
.
componentSubType
=
kAudioUnitSubType_DefaultOutput
;
desc
.
componentManufacturer
=
kAudioUnitManufacturer_Apple
;
desc
.
componentFlags
=
0
;
desc
.
componentFlagsMask
=
0
;
comp
=
AudioComponentFindNext
(
NULL
,
&
desc
);
if
(
comp
==
NULL
)
return
0
;
err
=
AudioComponentInstanceNew
(
comp
,
au
);
if
(
err
!=
noErr
||
*
au
==
NULL
)
return
0
;
callbackStruct
.
inputProc
=
CoreAudio_woAudioUnitIOProc
;
callbackStruct
.
inputProcRefCon
=
wwo
;
err
=
AudioUnitSetProperty
(
*
au
,
kAudioUnitProperty_SetRenderCallback
,
kAudioUnitScope_Input
,
0
,
&
callbackStruct
,
sizeof
(
callbackStruct
));
return
(
err
==
noErr
);
}
int
AudioUnit_CloseAudioUnit
(
AudioUnit
au
)
{
OSStatus
err
=
AudioComponentInstanceDispose
(
au
);
...
...
@@ -232,165 +183,6 @@ int AudioUnit_GetInputDeviceSampleRate(void)
return
sampleRate
;
}
int
AudioUnit_CreateInputUnit
(
void
*
wwi
,
AudioUnit
*
out_au
,
WORD
nChannels
,
DWORD
nSamplesPerSec
,
WORD
wBitsPerSample
,
UInt32
*
outFrameCount
)
{
OSStatus
err
=
noErr
;
AudioComponentDescription
description
;
AudioComponent
component
;
AudioUnit
au
;
UInt32
param
;
AudioObjectPropertyAddress
propertyAddress
;
AURenderCallbackStruct
callback
;
AudioDeviceID
defaultInputDevice
;
AudioStreamBasicDescription
desiredFormat
;
if
(
!
outFrameCount
)
{
ERR
(
"Invalid parameter
\n
"
);
return
0
;
}
/* Open the AudioOutputUnit */
description
.
componentType
=
kAudioUnitType_Output
;
description
.
componentSubType
=
kAudioUnitSubType_HALOutput
;
description
.
componentManufacturer
=
kAudioUnitManufacturer_Apple
;
description
.
componentFlags
=
0
;
description
.
componentFlagsMask
=
0
;
component
=
AudioComponentFindNext
(
NULL
,
&
description
);
if
(
!
component
)
{
ERR
(
"AudioComponentFindNext(kAudioUnitSubType_HALOutput) failed
\n
"
);
return
0
;
}
err
=
AudioComponentInstanceNew
(
component
,
&
au
);
if
(
err
!=
noErr
||
au
==
NULL
)
{
ERR
(
"AudioComponentInstanceNew failed: %08lx
\n
"
,
err
);
return
0
;
}
/* Configure the AudioOutputUnit */
/* The AUHAL has two buses (AKA elements). Bus 0 is output from the app
* to the device. Bus 1 is input from the device to the app. Each bus
* has two ends (AKA scopes). Data goes from the input scope to the
* output scope. The terminology is somewhat confusing because the terms
* "input" and "output" have two meanings. Here's a summary:
*
* Bus 0, input scope: refers to the source of data to be output as sound
* Bus 0, output scope: refers to the actual sound output device
* Bus 1, input scope: refers to the actual sound input device
* Bus 1, output scope: refers to the destination of data received by the input device
*/
/* Enable input on the AUHAL */
param
=
1
;
err
=
AudioUnitSetProperty
(
au
,
kAudioOutputUnitProperty_EnableIO
,
kAudioUnitScope_Input
,
1
,
&
param
,
sizeof
(
param
));
if
(
err
!=
noErr
)
{
ERR
(
"Couldn't enable input on AUHAL: %08lx
\n
"
,
err
);
goto
error
;
}
/* Disable Output on the AUHAL */
param
=
0
;
err
=
AudioUnitSetProperty
(
au
,
kAudioOutputUnitProperty_EnableIO
,
kAudioUnitScope_Output
,
0
,
&
param
,
sizeof
(
param
));
if
(
err
!=
noErr
)
{
ERR
(
"Couldn't disable output on AUHAL: %08lx
\n
"
,
err
);
goto
error
;
}
/* Find the default input device */
param
=
sizeof
(
defaultInputDevice
);
propertyAddress
.
mSelector
=
kAudioHardwarePropertyDefaultInputDevice
;
propertyAddress
.
mScope
=
kAudioObjectPropertyScopeGlobal
;
propertyAddress
.
mElement
=
kAudioObjectPropertyElementMaster
;
err
=
AudioObjectGetPropertyData
(
kAudioObjectSystemObject
,
&
propertyAddress
,
0
,
NULL
,
&
param
,
&
defaultInputDevice
);
if
(
err
!=
noErr
||
defaultInputDevice
==
kAudioDeviceUnknown
)
{
ERR
(
"Couldn't get the default audio device ID: %08lx
\n
"
,
err
);
goto
error
;
}
/* Set the current device to the default input device. */
err
=
AudioUnitSetProperty
(
au
,
kAudioOutputUnitProperty_CurrentDevice
,
kAudioUnitScope_Global
,
0
,
&
defaultInputDevice
,
sizeof
(
defaultInputDevice
));
if
(
err
!=
noErr
)
{
ERR
(
"Couldn't set current device of AUHAL to default input device: %08lx
\n
"
,
err
);
goto
error
;
}
/* Setup render callback */
/* This will be called when the AUHAL has input data. However, it won't
* be passed the data itself. The callback will have to all AudioUnitRender. */
callback
.
inputProc
=
CoreAudio_wiAudioUnitIOProc
;
callback
.
inputProcRefCon
=
wwi
;
err
=
AudioUnitSetProperty
(
au
,
kAudioOutputUnitProperty_SetInputCallback
,
kAudioUnitScope_Global
,
0
,
&
callback
,
sizeof
(
callback
));
if
(
err
!=
noErr
)
{
ERR
(
"Couldn't set input callback of AUHAL: %08lx
\n
"
,
err
);
goto
error
;
}
/* Setup the desired data format. */
/* FIXME: implement sample rate conversion on input. We shouldn't set
* the mSampleRate of this to the desired sample rate. We need to query
* the input device and use that. If they don't match, we need to set up
* an AUConverter to do the sample rate conversion on a separate thread. */
desiredFormat
.
mFormatID
=
kAudioFormatLinearPCM
;
desiredFormat
.
mFormatFlags
=
kLinearPCMFormatFlagIsPacked
;
if
(
wBitsPerSample
!=
8
)
desiredFormat
.
mFormatFlags
|=
kLinearPCMFormatFlagIsSignedInteger
;
desiredFormat
.
mSampleRate
=
nSamplesPerSec
;
desiredFormat
.
mChannelsPerFrame
=
nChannels
;
desiredFormat
.
mFramesPerPacket
=
1
;
desiredFormat
.
mBitsPerChannel
=
wBitsPerSample
;
desiredFormat
.
mBytesPerFrame
=
desiredFormat
.
mBitsPerChannel
*
desiredFormat
.
mChannelsPerFrame
/
8
;
desiredFormat
.
mBytesPerPacket
=
desiredFormat
.
mBytesPerFrame
*
desiredFormat
.
mFramesPerPacket
;
/* Set the AudioOutputUnit output data format */
err
=
AudioUnitSetProperty
(
au
,
kAudioUnitProperty_StreamFormat
,
kAudioUnitScope_Output
,
1
,
&
desiredFormat
,
sizeof
(
desiredFormat
));
if
(
err
!=
noErr
)
{
ERR
(
"Couldn't set desired input format of AUHAL: %08lx
\n
"
,
err
);
goto
error
;
}
/* Get the number of frames in the IO buffer(s) */
param
=
sizeof
(
*
outFrameCount
);
err
=
AudioUnitGetProperty
(
au
,
kAudioDevicePropertyBufferFrameSize
,
kAudioUnitScope_Global
,
0
,
outFrameCount
,
&
param
);
if
(
err
!=
noErr
)
{
ERR
(
"Failed to get audio sample size: %08lx
\n
"
,
err
);
goto
error
;
}
TRACE
(
"Frame count: %lu
\n
"
,
*
outFrameCount
);
/* Initialize the AU */
err
=
AudioUnitInitialize
(
au
);
if
(
err
!=
noErr
)
{
ERR
(
"Failed to initialize AU: %08lx
\n
"
,
err
);
goto
error
;
}
*
out_au
=
au
;
return
1
;
error:
if
(
au
)
AudioComponentInstanceDispose
(
au
);
return
0
;
}
/*
* MIDI Synth Unit
*/
...
...
dlls/winecoreaudio.drv/coreaudio.c
View file @
a773b16d
...
...
@@ -42,9 +42,7 @@ static LRESULT CoreAudio_drvLoad(void)
{
TRACE
(
"()
\n
"
);
if
(
CoreAudio_WaveInit
()
!=
DRV_SUCCESS
||
CoreAudio_MIDIInit
()
!=
DRV_SUCCESS
||
CoreAudio_MixerInit
()
!=
DRV_SUCCESS
)
if
(
CoreAudio_MIDIInit
()
!=
DRV_SUCCESS
)
return
DRV_FAILURE
;
return
DRV_SUCCESS
;
...
...
@@ -56,9 +54,7 @@ static LRESULT CoreAudio_drvLoad(void)
static
LRESULT
CoreAudio_drvFree
(
void
)
{
TRACE
(
"()
\n
"
);
CoreAudio_WaveRelease
();
CoreAudio_MIDIRelease
();
CoreAudio_MixerRelease
();
return
DRV_SUCCESS
;
}
...
...
dlls/winecoreaudio.drv/mixer.c
deleted
100644 → 0
View file @
a83bc10c
/*
* Sample MIXER Wine Driver for Mac OS X (based on OSS mixer)
*
* Copyright 1997 Marcus Meissner
* 1999,2001 Eric Pouech
* 2006,2007 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 <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "mmddk.h"
#include "coreaudio.h"
#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
mixer
);
#include <CoreAudio/CoreAudio.h>
#include <CoreFoundation/CoreFoundation.h>
#define WINE_MIXER_NAME "CoreAudio Mixer"
#define InputDevice (1 << 0)
#define OutputDevice (1 << 1)
#define IsInput(dir) ((dir) & InputDevice)
#define IsOutput(dir) ((dir) & OutputDevice)
#define ControlsPerLine 2
/* number of control per line : volume & (mute | onoff) */
#define IDControlVolume 0
#define IDControlMute 1
typedef
struct
tagMixerLine
{
char
*
name
;
int
direction
;
int
numChannels
;
int
componentType
;
AudioDeviceID
deviceID
;
}
MixerLine
;
typedef
struct
tagMixerCtrl
{
DWORD
dwLineID
;
MIXERCONTROLW
ctrl
;
}
MixerCtrl
;
typedef
struct
tagCoreAudio_Mixer
{
MIXERCAPSW
caps
;
MixerCtrl
*
mixerCtrls
;
MixerLine
*
lines
;
DWORD
numCtrl
;
}
CoreAudio_Mixer
;
static
CoreAudio_Mixer
mixer
;
static
int
numMixers
=
1
;
/**************************************************************************
*/
static
const
char
*
getMessage
(
UINT
uMsg
)
{
#define MSG_TO_STR(x) case x: return #x;
switch
(
uMsg
)
{
MSG_TO_STR
(
DRVM_INIT
);
MSG_TO_STR
(
DRVM_EXIT
);
MSG_TO_STR
(
DRVM_ENABLE
);
MSG_TO_STR
(
DRVM_DISABLE
);
MSG_TO_STR
(
MXDM_GETDEVCAPS
);
MSG_TO_STR
(
MXDM_GETLINEINFO
);
MSG_TO_STR
(
MXDM_GETNUMDEVS
);
MSG_TO_STR
(
MXDM_OPEN
);
MSG_TO_STR
(
MXDM_CLOSE
);
MSG_TO_STR
(
MXDM_GETLINECONTROLS
);
MSG_TO_STR
(
MXDM_GETCONTROLDETAILS
);
MSG_TO_STR
(
MXDM_SETCONTROLDETAILS
);
}
#undef MSG_TO_STR
return
wine_dbg_sprintf
(
"UNKNOWN(%08x)"
,
uMsg
);
}
static
const
char
*
getControlType
(
DWORD
dwControlType
)
{
#define TYPE_TO_STR(x) case x: return #x;
switch
(
dwControlType
)
{
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_CUSTOM
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_BOOLEANMETER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_SIGNEDMETER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_PEAKMETER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_BOOLEAN
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_ONOFF
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_MUTE
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_MONO
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_LOUDNESS
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_STEREOENH
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_BASS_BOOST
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_BUTTON
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_DECIBELS
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_SIGNED
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_UNSIGNED
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_PERCENT
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_SLIDER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_PAN
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_QSOUNDPAN
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_FADER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_VOLUME
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_BASS
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_TREBLE
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_EQUALIZER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_SINGLESELECT
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_MUX
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_MIXER
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_MICROTIME
);
TYPE_TO_STR
(
MIXERCONTROL_CONTROLTYPE_MILLITIME
);
}
#undef TYPE_TO_STR
return
wine_dbg_sprintf
(
"UNKNOWN(%08x)"
,
dwControlType
);
}
static
const
char
*
getComponentType
(
DWORD
dwComponentType
)
{
#define TYPE_TO_STR(x) case x: return #x;
switch
(
dwComponentType
)
{
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_UNDEFINED
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_DIGITAL
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_LINE
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_MONITOR
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_TELEPHONE
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_WAVEIN
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_DST_VOICEIN
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_DIGITAL
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_LINE
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
);
TYPE_TO_STR
(
MIXERLINE_COMPONENTTYPE_SRC_ANALOG
);
}
#undef TYPE_TO_STR
return
wine_dbg_sprintf
(
"UNKNOWN(%08x)"
,
dwComponentType
);
}
static
const
char
*
getTargetType
(
DWORD
dwType
)
{
#define TYPE_TO_STR(x) case x: return #x;
switch
(
dwType
)
{
TYPE_TO_STR
(
MIXERLINE_TARGETTYPE_UNDEFINED
);
TYPE_TO_STR
(
MIXERLINE_TARGETTYPE_WAVEOUT
);
TYPE_TO_STR
(
MIXERLINE_TARGETTYPE_WAVEIN
);
TYPE_TO_STR
(
MIXERLINE_TARGETTYPE_MIDIOUT
);
TYPE_TO_STR
(
MIXERLINE_TARGETTYPE_MIDIIN
);
TYPE_TO_STR
(
MIXERLINE_TARGETTYPE_AUX
);
}
#undef TYPE_TO_STR
return
wine_dbg_sprintf
(
"UNKNOWN(%08x)"
,
dwType
);
}
/* FIXME is there a better way ? */
static
DWORD
DeviceComponentType
(
char
*
name
)
{
if
(
strcmp
(
name
,
"Built-in Microphone"
)
==
0
)
return
MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
;
if
(
strcmp
(
name
,
"Built-in Line Input"
)
==
0
)
return
MIXERLINE_COMPONENTTYPE_SRC_LINE
;
if
(
strcmp
(
name
,
"Built-in Output"
)
==
0
)
return
MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
return
MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
;
}
static
BOOL
DeviceHasMute
(
AudioDeviceID
deviceID
,
Boolean
isInput
)
{
Boolean
writable
=
false
;
OSStatus
err
=
noErr
;
AudioObjectPropertyAddress
propertyAddress
;
propertyAddress
.
mSelector
=
kAudioDevicePropertyMute
;
propertyAddress
.
mScope
=
isInput
?
kAudioDevicePropertyScopeInput
:
kAudioDevicePropertyScopeOutput
;
propertyAddress
.
mElement
=
0
;
if
(
AudioObjectHasProperty
(
deviceID
,
&
propertyAddress
))
{
/* check if we can set it */
err
=
AudioObjectIsPropertySettable
(
deviceID
,
&
propertyAddress
,
&
writable
);
if
(
err
==
noErr
)
return
writable
;
}
return
FALSE
;
}
/*
* Getters
*/
static
BOOL
MIX_LineGetVolume
(
DWORD
lineID
,
DWORD
channels
,
Float32
*
left
,
Float32
*
right
)
{
MixerLine
*
line
=
&
mixer
.
lines
[
lineID
];
UInt32
size
=
sizeof
(
Float32
);
OSStatus
err
=
noErr
;
AudioObjectPropertyAddress
address
;
*
left
=
*
right
=
0
.
0
;
address
.
mSelector
=
kAudioDevicePropertyVolumeScalar
;
address
.
mScope
=
IsInput
(
line
->
direction
)
?
kAudioDevicePropertyScopeInput
:
kAudioDevicePropertyScopeOutput
;
address
.
mElement
=
1
;
err
=
AudioObjectGetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
NULL
,
&
size
,
left
);
if
(
err
!=
noErr
)
return
FALSE
;
if
(
channels
==
2
)
{
size
=
sizeof
(
Float32
);
address
.
mElement
=
2
;
err
=
AudioObjectGetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
NULL
,
&
size
,
right
);
if
(
err
!=
noErr
)
return
FALSE
;
}
TRACE
(
"lineID %d channels %d return left %f right %f
\n
"
,
lineID
,
channels
,
*
left
,
*
right
);
return
(
err
==
noErr
);
}
static
BOOL
MIX_LineGetMute
(
DWORD
lineID
,
BOOL
*
muted
)
{
MixerLine
*
line
=
&
mixer
.
lines
[
lineID
];
UInt32
size
=
sizeof
(
UInt32
);
UInt32
val
=
0
;
OSStatus
err
=
noErr
;
AudioObjectPropertyAddress
address
;
address
.
mSelector
=
kAudioDevicePropertyMute
;
address
.
mScope
=
IsInput
(
line
->
direction
)
?
kAudioDevicePropertyScopeInput
:
kAudioDevicePropertyScopeOutput
;
address
.
mElement
=
0
;
err
=
AudioObjectGetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
NULL
,
&
size
,
&
val
);
*
muted
=
val
;
return
(
err
==
noErr
);
}
/*
* Setters
*/
static
BOOL
MIX_LineSetVolume
(
DWORD
lineID
,
DWORD
channels
,
Float32
left
,
Float32
right
)
{
MixerLine
*
line
=
&
mixer
.
lines
[
lineID
];
UInt32
size
=
sizeof
(
Float32
);
AudioObjectPropertyAddress
address
;
OSStatus
err
=
noErr
;
TRACE
(
"lineID %d channels %d left %f right %f
\n
"
,
lineID
,
channels
,
left
,
right
);
address
.
mSelector
=
kAudioDevicePropertyVolumeScalar
;
address
.
mScope
=
IsInput
(
line
->
direction
)
?
kAudioDevicePropertyScopeInput
:
kAudioDevicePropertyScopeOutput
;
if
(
channels
==
2
)
{
address
.
mElement
=
1
;
err
=
AudioObjectSetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
NULL
,
size
,
&
left
);
if
(
err
!=
noErr
)
return
FALSE
;
address
.
mElement
=
2
;
err
=
AudioObjectSetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
NULL
,
size
,
&
right
);
}
else
{
/*
FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
address.mElement = 0;
err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
*/
right
=
left
;
address
.
mElement
=
1
;
err
=
AudioObjectSetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
NULL
,
size
,
&
left
);
if
(
err
!=
noErr
)
return
FALSE
;
address
.
mElement
=
2
;
err
=
AudioObjectSetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
NULL
,
size
,
&
right
);
}
return
(
err
==
noErr
);
}
static
BOOL
MIX_LineSetMute
(
DWORD
lineID
,
BOOL
mute
)
{
MixerLine
*
line
=
&
mixer
.
lines
[
lineID
];
UInt32
val
=
mute
;
UInt32
size
=
sizeof
(
UInt32
);
AudioObjectPropertyAddress
address
;
OSStatus
err
=
noErr
;
address
.
mSelector
=
kAudioDevicePropertyMute
;
address
.
mScope
=
IsInput
(
line
->
direction
)
?
kAudioDevicePropertyScopeInput
:
kAudioDevicePropertyScopeOutput
;
address
.
mElement
=
0
;
err
=
AudioObjectSetPropertyData
(
line
->
deviceID
,
&
address
,
0
,
0
,
size
,
&
val
);
return
(
err
==
noErr
);
}
static
void
MIX_FillControls
(
void
)
{
int
i
;
int
ctrl
=
0
;
MixerLine
*
line
;
for
(
i
=
0
;
i
<
mixer
.
caps
.
cDestinations
;
i
++
)
{
line
=
&
mixer
.
lines
[
i
];
mixer
.
mixerCtrls
[
ctrl
].
dwLineID
=
i
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
cbStruct
=
sizeof
(
MIXERCONTROLW
);
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
dwControlType
=
MIXERCONTROL_CONTROLTYPE_VOLUME
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
dwControlID
=
ctrl
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
Bounds
.
s1
.
dwMinimum
=
0
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
Bounds
.
s1
.
dwMaximum
=
65535
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
Metrics
.
cSteps
=
656
;
ctrl
++
;
mixer
.
mixerCtrls
[
ctrl
].
dwLineID
=
i
;
if
(
!
DeviceHasMute
(
line
->
deviceID
,
IsInput
(
line
->
direction
))
)
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
fdwControl
|=
MIXERCONTROL_CONTROLF_DISABLED
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
cbStruct
=
sizeof
(
MIXERCONTROLW
);
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
dwControlType
=
MIXERCONTROL_CONTROLTYPE_MUTE
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
dwControlID
=
ctrl
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
Bounds
.
s1
.
dwMinimum
=
0
;
mixer
.
mixerCtrls
[
ctrl
].
ctrl
.
Bounds
.
s1
.
dwMaximum
=
1
;
ctrl
++
;
}
assert
(
ctrl
==
mixer
.
numCtrl
);
}
/**************************************************************************
* CoreAudio_MixerInit
*/
LONG
CoreAudio_MixerInit
(
void
)
{
OSStatus
status
;
UInt32
propertySize
;
AudioObjectPropertyAddress
propertyAddress
;
AudioDeviceID
*
deviceArray
=
NULL
;
CFStringRef
name
;
int
i
;
int
numLines
;
AudioStreamBasicDescription
streamDescription
;
/* Find number of lines */
propertyAddress
.
mSelector
=
kAudioHardwarePropertyDevices
;
propertyAddress
.
mScope
=
kAudioObjectPropertyScopeGlobal
;
propertyAddress
.
mElement
=
kAudioObjectPropertyElementMaster
;
status
=
AudioObjectGetPropertyDataSize
(
kAudioObjectSystemObject
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
);
if
(
status
)
{
ERR
(
"AudioObjectGetPropertyDataSize for kAudioHardwarePropertyDevices return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
return
DRV_FAILURE
;
}
numLines
=
propertySize
/
sizeof
(
AudioDeviceID
);
mixer
.
mixerCtrls
=
NULL
;
mixer
.
lines
=
NULL
;
mixer
.
numCtrl
=
0
;
mixer
.
caps
.
cDestinations
=
numLines
;
mixer
.
caps
.
wMid
=
0xAA
;
mixer
.
caps
.
wPid
=
0x55
;
mixer
.
caps
.
vDriverVersion
=
0x0100
;
MultiByteToWideChar
(
CP_ACP
,
0
,
WINE_MIXER_NAME
,
-
1
,
mixer
.
caps
.
szPname
,
sizeof
(
mixer
.
caps
.
szPname
)
/
sizeof
(
WCHAR
));
mixer
.
caps
.
fdwSupport
=
0
;
/* No bits defined yet */
mixer
.
lines
=
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
sizeof
(
MixerLine
)
*
numLines
);
if
(
!
mixer
.
lines
)
goto
error
;
deviceArray
=
HeapAlloc
(
GetProcessHeap
(),
0
,
sizeof
(
AudioDeviceID
)
*
numLines
);
propertySize
=
sizeof
(
AudioDeviceID
)
*
numLines
;
status
=
AudioObjectGetPropertyData
(
kAudioObjectSystemObject
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
,
deviceArray
);
if
(
status
)
{
ERR
(
"AudioObjectGetPropertyData for kAudioHardwarePropertyDevices return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
goto
error
;
}
for
(
i
=
0
;
i
<
numLines
;
i
++
)
{
MixerLine
*
line
=
&
mixer
.
lines
[
i
];
line
->
deviceID
=
deviceArray
[
i
];
propertySize
=
sizeof
(
CFStringRef
);
propertyAddress
.
mSelector
=
kAudioObjectPropertyName
;
propertyAddress
.
mScope
=
kAudioObjectPropertyScopeGlobal
;
propertyAddress
.
mElement
=
kAudioObjectPropertyElementMaster
;
status
=
AudioObjectGetPropertyData
(
line
->
deviceID
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
,
&
name
);
if
(
status
)
{
ERR
(
"AudioObjectGetPropertyData for kAudioObjectPropertyName return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
goto
error
;
}
line
->
name
=
HeapAlloc
(
GetProcessHeap
(),
0
,
CFStringGetLength
(
name
)
+
1
);
if
(
!
line
->
name
)
goto
error
;
CFStringGetCString
(
name
,
line
->
name
,
CFStringGetLength
(
name
)
+
1
,
kCFStringEncodingUTF8
);
line
->
componentType
=
DeviceComponentType
(
line
->
name
);
/* check for directions */
/* Output ? */
propertySize
=
sizeof
(
UInt32
);
propertyAddress
.
mSelector
=
kAudioDevicePropertyStreams
;
propertyAddress
.
mScope
=
kAudioDevicePropertyScopeOutput
;
status
=
AudioObjectGetPropertyDataSize
(
line
->
deviceID
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
);
if
(
status
)
{
ERR
(
"AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
goto
error
;
}
if
(
(
propertySize
/
sizeof
(
AudioStreamID
))
!=
0
)
{
line
->
direction
|=
OutputDevice
;
/* Check the number of channel for the stream */
propertySize
=
sizeof
(
streamDescription
);
propertyAddress
.
mSelector
=
kAudioDevicePropertyStreamFormat
;
status
=
AudioObjectGetPropertyData
(
line
->
deviceID
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
,
&
streamDescription
);
if
(
status
!=
noErr
)
{
ERR
(
"AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
goto
error
;
}
line
->
numChannels
=
streamDescription
.
mChannelsPerFrame
;
}
else
{
/* Input ? */
propertySize
=
sizeof
(
UInt32
);
propertyAddress
.
mScope
=
kAudioDevicePropertyScopeInput
;
status
=
AudioObjectGetPropertyDataSize
(
line
->
deviceID
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
);
if
(
status
)
{
ERR
(
"AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
goto
error
;
}
if
(
(
propertySize
/
sizeof
(
AudioStreamID
))
!=
0
)
{
line
->
direction
|=
InputDevice
;
/* Check the number of channel for the stream */
propertySize
=
sizeof
(
streamDescription
);
propertyAddress
.
mSelector
=
kAudioDevicePropertyStreamFormat
;
status
=
AudioObjectGetPropertyData
(
line
->
deviceID
,
&
propertyAddress
,
0
,
NULL
,
&
propertySize
,
&
streamDescription
);
if
(
status
!=
noErr
)
{
ERR
(
"AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s
\n
"
,
wine_dbgstr_fourcc
(
status
));
goto
error
;
}
line
->
numChannels
=
streamDescription
.
mChannelsPerFrame
;
}
}
mixer
.
numCtrl
+=
ControlsPerLine
;
/* volume & (mute | onoff) */
}
mixer
.
mixerCtrls
=
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
sizeof
(
MixerCtrl
)
*
mixer
.
numCtrl
);
if
(
!
mixer
.
mixerCtrls
)
goto
error
;
MIX_FillControls
();
HeapFree
(
GetProcessHeap
(),
0
,
deviceArray
);
return
DRV_SUCCESS
;
error:
if
(
mixer
.
lines
)
{
int
i
;
for
(
i
=
0
;
i
<
mixer
.
caps
.
cDestinations
;
i
++
)
{
HeapFree
(
GetProcessHeap
(),
0
,
mixer
.
lines
[
i
].
name
);
}
HeapFree
(
GetProcessHeap
(),
0
,
mixer
.
lines
);
}
HeapFree
(
GetProcessHeap
(),
0
,
deviceArray
);
if
(
mixer
.
mixerCtrls
)
HeapFree
(
GetProcessHeap
(),
0
,
mixer
.
mixerCtrls
);
return
DRV_FAILURE
;
}
/**************************************************************************
* CoreAudio_MixerRelease
*/
void
CoreAudio_MixerRelease
(
void
)
{
TRACE
(
"()
\n
"
);
if
(
mixer
.
lines
)
{
int
i
;
for
(
i
=
0
;
i
<
mixer
.
caps
.
cDestinations
;
i
++
)
{
HeapFree
(
GetProcessHeap
(),
0
,
mixer
.
lines
[
i
].
name
);
}
HeapFree
(
GetProcessHeap
(),
0
,
mixer
.
lines
);
}
if
(
mixer
.
mixerCtrls
)
HeapFree
(
GetProcessHeap
(),
0
,
mixer
.
mixerCtrls
);
}
/**************************************************************************
* MIX_Open [internal]
*/
static
DWORD
MIX_Open
(
WORD
wDevID
,
LPMIXEROPENDESC
lpMod
,
DWORD_PTR
flags
)
{
TRACE
(
"wDevID=%d lpMod=%p dwSize=%08lx
\n
"
,
wDevID
,
lpMod
,
flags
);
if
(
lpMod
==
NULL
)
{
WARN
(
"invalid parameter: lpMod == NULL
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
numMixers
)
{
WARN
(
"bad device ID: %04X
\n
"
,
wDevID
);
return
MMSYSERR_BADDEVICEID
;
}
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* MIX_GetNumDevs [internal]
*/
static
DWORD
MIX_GetNumDevs
(
void
)
{
TRACE
(
"()
\n
"
);
return
numMixers
;
}
static
DWORD
MIX_GetDevCaps
(
WORD
wDevID
,
LPMIXERCAPSW
lpCaps
,
DWORD_PTR
dwSize
)
{
TRACE
(
"wDevID=%d lpCaps=%p
\n
"
,
wDevID
,
lpCaps
);
if
(
lpCaps
==
NULL
)
{
WARN
(
"Invalid Parameter
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
numMixers
)
{
WARN
(
"bad device ID : %d
\n
"
,
wDevID
);
return
MMSYSERR_BADDEVICEID
;
}
memcpy
(
lpCaps
,
&
mixer
.
caps
,
min
(
dwSize
,
sizeof
(
*
lpCaps
)));
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* MIX_GetLineInfo [internal]
*/
static
DWORD
MIX_GetLineInfo
(
WORD
wDevID
,
LPMIXERLINEW
lpMl
,
DWORD_PTR
fdwInfo
)
{
int
i
;
DWORD
ret
=
MMSYSERR_ERROR
;
MixerLine
*
line
=
NULL
;
TRACE
(
"%04X, %p, %08lx
\n
"
,
wDevID
,
lpMl
,
fdwInfo
);
if
(
lpMl
==
NULL
)
{
WARN
(
"invalid parameter: lpMl = NULL
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
lpMl
->
cbStruct
!=
sizeof
(
*
lpMl
))
{
WARN
(
"invalid parameter: lpMl->cbStruct
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
numMixers
)
{
WARN
(
"bad device ID: %04X
\n
"
,
wDevID
);
return
MMSYSERR_BADDEVICEID
;
}
/* FIXME: set all the variables correctly... the lines below
* are very wrong...
*/
lpMl
->
dwUser
=
0
;
switch
(
fdwInfo
&
MIXER_GETLINEINFOF_QUERYMASK
)
{
case
MIXER_GETLINEINFOF_DESTINATION
:
TRACE
(
"MIXER_GETLINEINFOF_DESTINATION %d
\n
"
,
lpMl
->
dwDestination
);
if
(
(
lpMl
->
dwDestination
>=
0
)
&&
(
lpMl
->
dwDestination
<
mixer
.
caps
.
cDestinations
)
)
{
lpMl
->
dwLineID
=
lpMl
->
dwDestination
;
line
=
&
mixer
.
lines
[
lpMl
->
dwDestination
];
}
else
ret
=
MIXERR_INVALLINE
;
break
;
case
MIXER_GETLINEINFOF_COMPONENTTYPE
:
TRACE
(
"MIXER_GETLINEINFOF_COMPONENTTYPE %s
\n
"
,
getComponentType
(
lpMl
->
dwComponentType
));
for
(
i
=
0
;
i
<
mixer
.
caps
.
cDestinations
;
i
++
)
{
if
(
mixer
.
lines
[
i
].
componentType
==
lpMl
->
dwComponentType
)
{
lpMl
->
dwDestination
=
lpMl
->
dwLineID
=
i
;
line
=
&
mixer
.
lines
[
i
];
break
;
}
}
if
(
line
==
NULL
)
{
WARN
(
"can't find component type %s
\n
"
,
getComponentType
(
lpMl
->
dwComponentType
));
ret
=
MIXERR_INVALVALUE
;
}
break
;
case
MIXER_GETLINEINFOF_SOURCE
:
FIXME
(
"MIXER_GETLINEINFOF_SOURCE %d dst=%d
\n
"
,
lpMl
->
dwSource
,
lpMl
->
dwDestination
);
break
;
case
MIXER_GETLINEINFOF_LINEID
:
TRACE
(
"MIXER_GETLINEINFOF_LINEID %d
\n
"
,
lpMl
->
dwLineID
);
if
(
(
lpMl
->
dwLineID
>=
0
)
&&
(
lpMl
->
dwLineID
<
mixer
.
caps
.
cDestinations
)
)
{
lpMl
->
dwDestination
=
lpMl
->
dwLineID
;
line
=
&
mixer
.
lines
[
lpMl
->
dwLineID
];
}
else
ret
=
MIXERR_INVALLINE
;
break
;
case
MIXER_GETLINEINFOF_TARGETTYPE
:
FIXME
(
"MIXER_GETLINEINFOF_TARGETTYPE (%s)
\n
"
,
getTargetType
(
lpMl
->
Target
.
dwType
));
switch
(
lpMl
->
Target
.
dwType
)
{
case
MIXERLINE_TARGETTYPE_UNDEFINED
:
case
MIXERLINE_TARGETTYPE_WAVEOUT
:
case
MIXERLINE_TARGETTYPE_WAVEIN
:
case
MIXERLINE_TARGETTYPE_MIDIOUT
:
case
MIXERLINE_TARGETTYPE_MIDIIN
:
case
MIXERLINE_TARGETTYPE_AUX
:
default:
FIXME
(
"Unhandled target type (%s)
\n
"
,
getTargetType
(
lpMl
->
Target
.
dwType
));
return
MMSYSERR_INVALPARAM
;
}
break
;
default:
WARN
(
"Unknown flag (%08lx)
\n
"
,
fdwInfo
&
MIXER_GETLINEINFOF_QUERYMASK
);
break
;
}
if
(
line
)
{
lpMl
->
dwComponentType
=
line
->
componentType
;
lpMl
->
cChannels
=
line
->
numChannels
;
lpMl
->
cControls
=
ControlsPerLine
;
/* FIXME check there with CoreAudio */
lpMl
->
cConnections
=
1
;
lpMl
->
fdwLine
=
MIXERLINE_LINEF_ACTIVE
;
MultiByteToWideChar
(
CP_ACP
,
0
,
line
->
name
,
-
1
,
lpMl
->
szShortName
,
sizeof
(
lpMl
->
szShortName
)
/
sizeof
(
WCHAR
));
MultiByteToWideChar
(
CP_ACP
,
0
,
line
->
name
,
-
1
,
lpMl
->
szName
,
sizeof
(
lpMl
->
szName
)
/
sizeof
(
WCHAR
));
if
(
IsInput
(
line
->
direction
)
)
lpMl
->
Target
.
dwType
=
MIXERLINE_TARGETTYPE_WAVEIN
;
else
lpMl
->
Target
.
dwType
=
MIXERLINE_TARGETTYPE_WAVEOUT
;
lpMl
->
Target
.
dwDeviceID
=
line
->
deviceID
;
lpMl
->
Target
.
wMid
=
mixer
.
caps
.
wMid
;
lpMl
->
Target
.
wPid
=
mixer
.
caps
.
wPid
;
lpMl
->
Target
.
vDriverVersion
=
mixer
.
caps
.
vDriverVersion
;
MultiByteToWideChar
(
CP_ACP
,
0
,
WINE_MIXER_NAME
,
-
1
,
lpMl
->
Target
.
szPname
,
sizeof
(
lpMl
->
Target
.
szPname
)
/
sizeof
(
WCHAR
));
ret
=
MMSYSERR_NOERROR
;
}
return
ret
;
}
/**************************************************************************
* MIX_GetLineControls [internal]
*/
static
DWORD
MIX_GetLineControls
(
WORD
wDevID
,
LPMIXERLINECONTROLSW
lpMlc
,
DWORD_PTR
flags
)
{
DWORD
ret
=
MMSYSERR_NOTENABLED
;
int
ctrl
=
0
;
TRACE
(
"%04X, %p, %08lX
\n
"
,
wDevID
,
lpMlc
,
flags
);
if
(
lpMlc
==
NULL
)
{
WARN
(
"invalid parameter: lpMlc == NULL
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
lpMlc
->
cbStruct
<
sizeof
(
*
lpMlc
))
{
WARN
(
"invalid parameter: lpMlc->cbStruct = %d
\n
"
,
lpMlc
->
cbStruct
);
return
MMSYSERR_INVALPARAM
;
}
if
(
lpMlc
->
cbmxctrl
<
sizeof
(
MIXERCONTROLW
))
{
WARN
(
"invalid parameter: lpMlc->cbmxctrl = %d
\n
"
,
lpMlc
->
cbmxctrl
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
numMixers
)
{
WARN
(
"bad device ID: %04X
\n
"
,
wDevID
);
return
MMSYSERR_BADDEVICEID
;
}
switch
(
flags
&
MIXER_GETLINECONTROLSF_QUERYMASK
)
{
case
MIXER_GETLINECONTROLSF_ALL
:
FIXME
(
"dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)
\n
"
,
lpMlc
->
dwLineID
,
lpMlc
->
cControls
);
if
(
lpMlc
->
cControls
!=
ControlsPerLine
)
{
WARN
(
"invalid parameter lpMlc->cControls %d
\n
"
,
lpMlc
->
cControls
);
ret
=
MMSYSERR_INVALPARAM
;
}
else
{
if
(
(
lpMlc
->
dwLineID
>=
0
)
&&
(
lpMlc
->
dwLineID
<
mixer
.
caps
.
cDestinations
)
)
{
int
i
;
for
(
i
=
0
;
i
<
lpMlc
->
cControls
;
i
++
)
{
lpMlc
->
pamxctrl
[
i
]
=
mixer
.
mixerCtrls
[
lpMlc
->
dwLineID
*
i
].
ctrl
;
}
ret
=
MMSYSERR_NOERROR
;
}
else
ret
=
MIXERR_INVALLINE
;
}
break
;
case
MIXER_GETLINECONTROLSF_ONEBYID
:
TRACE
(
"dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)
\n
"
,
lpMlc
->
dwLineID
,
lpMlc
->
u
.
dwControlID
);
if
(
lpMlc
->
u
.
dwControlID
>=
0
&&
lpMlc
->
u
.
dwControlID
<
mixer
.
numCtrl
)
{
lpMlc
->
pamxctrl
[
0
]
=
mixer
.
mixerCtrls
[
lpMlc
->
u
.
dwControlID
].
ctrl
;
ret
=
MMSYSERR_NOERROR
;
}
else
ret
=
MIXERR_INVALVALUE
;
break
;
case
MIXER_GETLINECONTROLSF_ONEBYTYPE
:
TRACE
(
"dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)
\n
"
,
lpMlc
->
dwLineID
,
getControlType
(
lpMlc
->
u
.
dwControlType
));
if
(
(
lpMlc
->
dwLineID
<
0
)
||
(
lpMlc
->
dwLineID
>=
mixer
.
caps
.
cDestinations
)
)
{
ret
=
MIXERR_INVALLINE
;
break
;
}
if
(
lpMlc
->
u
.
dwControlType
==
MIXERCONTROL_CONTROLTYPE_VOLUME
)
{
ctrl
=
(
lpMlc
->
dwLineID
*
ControlsPerLine
)
+
IDControlVolume
;
lpMlc
->
pamxctrl
[
0
]
=
mixer
.
mixerCtrls
[
ctrl
].
ctrl
;
ret
=
MMSYSERR_NOERROR
;
}
else
if
(
lpMlc
->
u
.
dwControlType
==
MIXERCONTROL_CONTROLTYPE_MUTE
)
{
ctrl
=
(
lpMlc
->
dwLineID
*
ControlsPerLine
)
+
IDControlMute
;
lpMlc
->
pamxctrl
[
0
]
=
mixer
.
mixerCtrls
[
ctrl
].
ctrl
;
ret
=
MMSYSERR_NOERROR
;
}
break
;
default:
ERR
(
"Unknown flag %08lx
\n
"
,
flags
&
MIXER_GETLINECONTROLSF_QUERYMASK
);
ret
=
MMSYSERR_INVALPARAM
;
}
return
ret
;
}
/**************************************************************************
* MIX_GetControlDetails [internal]
*/
static
DWORD
MIX_GetControlDetails
(
WORD
wDevID
,
LPMIXERCONTROLDETAILS
lpmcd
,
DWORD_PTR
fdwDetails
)
{
DWORD
ret
=
MMSYSERR_NOTSUPPORTED
;
DWORD
dwControlType
;
TRACE
(
"%04X, %p, %08lx
\n
"
,
wDevID
,
lpmcd
,
fdwDetails
);
if
(
lpmcd
==
NULL
)
{
TRACE
(
"invalid parameter: lpmcd == NULL
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
numMixers
)
{
WARN
(
"bad device ID: %04X
\n
"
,
wDevID
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
(
fdwDetails
&
MIXER_GETCONTROLDETAILSF_QUERYMASK
)
!=
MIXER_GETCONTROLDETAILSF_VALUE
)
{
WARN
(
"Unknown/unimplement GetControlDetails flag (%08lx)
\n
"
,
fdwDetails
&
MIXER_GETCONTROLDETAILSF_QUERYMASK
);
return
MMSYSERR_NOTSUPPORTED
;
}
if
(
lpmcd
->
dwControlID
<
0
||
lpmcd
->
dwControlID
>=
mixer
.
numCtrl
)
{
WARN
(
"bad control ID: %d
\n
"
,
lpmcd
->
dwControlID
);
return
MIXERR_INVALVALUE
;
}
TRACE
(
"MIXER_GETCONTROLDETAILSF_VALUE %d
\n
"
,
lpmcd
->
dwControlID
);
dwControlType
=
mixer
.
mixerCtrls
[
lpmcd
->
dwControlID
].
ctrl
.
dwControlType
;
switch
(
dwControlType
)
{
case
MIXERCONTROL_CONTROLTYPE_VOLUME
:
FIXME
(
"controlType : %s channels %d
\n
"
,
getControlType
(
dwControlType
),
lpmcd
->
cChannels
);
{
LPMIXERCONTROLDETAILS_UNSIGNED
mcdu
;
Float32
left
,
right
;
if
(
lpmcd
->
cbDetails
!=
sizeof
(
MIXERCONTROLDETAILS_UNSIGNED
))
{
WARN
(
"invalid parameter: lpmcd->cbDetails == %d
\n
"
,
lpmcd
->
cbDetails
);
return
MMSYSERR_INVALPARAM
;
}
if
(
MIX_LineGetVolume
(
mixer
.
mixerCtrls
[
lpmcd
->
dwControlID
].
dwLineID
,
lpmcd
->
cChannels
,
&
left
,
&
right
)
)
{
mcdu
=
(
LPMIXERCONTROLDETAILS_UNSIGNED
)
lpmcd
->
paDetails
;
switch
(
lpmcd
->
cChannels
)
{
case
1
:
/* mono... so R = L */
mcdu
->
dwValue
=
left
*
65535
;
TRACE
(
"Reading RL = %d
\n
"
,
mcdu
->
dwValue
);
break
;
case
2
:
/* stereo, left is paDetails[0] */
mcdu
->
dwValue
=
left
*
65535
;
TRACE
(
"Reading L = %d
\n
"
,
mcdu
->
dwValue
);
mcdu
++
;
mcdu
->
dwValue
=
right
*
65535
;
TRACE
(
"Reading R = %d
\n
"
,
mcdu
->
dwValue
);
break
;
default:
WARN
(
"Unsupported cChannels (%d)
\n
"
,
lpmcd
->
cChannels
);
return
MMSYSERR_INVALPARAM
;
}
TRACE
(
"=> %08x
\n
"
,
mcdu
->
dwValue
);
ret
=
MMSYSERR_NOERROR
;
}
}
break
;
case
MIXERCONTROL_CONTROLTYPE_MUTE
:
case
MIXERCONTROL_CONTROLTYPE_ONOFF
:
FIXME
(
"%s MIXERCONTROLDETAILS_BOOLEAN[%u]
\n
"
,
getControlType
(
dwControlType
),
lpmcd
->
cChannels
);
{
LPMIXERCONTROLDETAILS_BOOLEAN
mcdb
;
BOOL
muted
;
if
(
lpmcd
->
cbDetails
!=
sizeof
(
MIXERCONTROLDETAILS_BOOLEAN
))
{
WARN
(
"invalid parameter: lpmcd->cbDetails = %d
\n
"
,
lpmcd
->
cbDetails
);
return
MMSYSERR_INVALPARAM
;
}
mcdb
=
(
LPMIXERCONTROLDETAILS_BOOLEAN
)
lpmcd
->
paDetails
;
if
(
MIX_LineGetMute
(
mixer
.
mixerCtrls
[
lpmcd
->
dwControlID
].
dwLineID
,
&
muted
)
)
{
mcdb
->
fValue
=
muted
;
TRACE
(
"=> %s
\n
"
,
mcdb
->
fValue
?
"on"
:
"off"
);
ret
=
MMSYSERR_NOERROR
;
}
}
break
;
case
MIXERCONTROL_CONTROLTYPE_MIXER
:
case
MIXERCONTROL_CONTROLTYPE_MUX
:
default:
FIXME
(
"controlType : %s
\n
"
,
getControlType
(
dwControlType
));
break
;
}
return
ret
;
}
/**************************************************************************
* MIX_SetControlDetails [internal]
*/
static
DWORD
MIX_SetControlDetails
(
WORD
wDevID
,
LPMIXERCONTROLDETAILS
lpmcd
,
DWORD_PTR
fdwDetails
)
{
DWORD
ret
=
MMSYSERR_NOTSUPPORTED
;
DWORD
dwControlType
;
TRACE
(
"%04X, %p, %08lx
\n
"
,
wDevID
,
lpmcd
,
fdwDetails
);
if
(
lpmcd
==
NULL
)
{
TRACE
(
"invalid parameter: lpmcd == NULL
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
numMixers
)
{
WARN
(
"bad device ID: %04X
\n
"
,
wDevID
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
(
fdwDetails
&
MIXER_SETCONTROLDETAILSF_QUERYMASK
)
!=
MIXER_GETCONTROLDETAILSF_VALUE
)
{
WARN
(
"Unknown SetControlDetails flag (%08lx)
\n
"
,
fdwDetails
&
MIXER_SETCONTROLDETAILSF_QUERYMASK
);
return
MMSYSERR_NOTSUPPORTED
;
}
TRACE
(
"MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d
\n
"
,
lpmcd
->
dwControlID
);
dwControlType
=
mixer
.
mixerCtrls
[
lpmcd
->
dwControlID
].
ctrl
.
dwControlType
;
switch
(
dwControlType
)
{
case
MIXERCONTROL_CONTROLTYPE_VOLUME
:
FIXME
(
"controlType : %s
\n
"
,
getControlType
(
dwControlType
));
{
LPMIXERCONTROLDETAILS_UNSIGNED
mcdu
;
Float32
left
,
right
=
0
;
if
(
lpmcd
->
cbDetails
!=
sizeof
(
MIXERCONTROLDETAILS_UNSIGNED
))
{
WARN
(
"invalid parameter: lpmcd->cbDetails == %d
\n
"
,
lpmcd
->
cbDetails
);
return
MMSYSERR_INVALPARAM
;
}
mcdu
=
(
LPMIXERCONTROLDETAILS_UNSIGNED
)
lpmcd
->
paDetails
;
switch
(
lpmcd
->
cChannels
)
{
case
1
:
/* mono... so R = L */
TRACE
(
"Setting RL to %d
\n
"
,
mcdu
->
dwValue
);
left
=
(
Float32
)
mcdu
->
dwValue
/
65535
.
0
;
break
;
case
2
:
/* stereo, left is paDetails[0] */
TRACE
(
"Setting L to %d
\n
"
,
mcdu
->
dwValue
);
left
=
(
Float32
)
mcdu
->
dwValue
/
65535
.
0
;
mcdu
++
;
TRACE
(
"Setting R to %d
\n
"
,
mcdu
->
dwValue
);
right
=
(
Float32
)
mcdu
->
dwValue
/
65535
.
0
;
break
;
default:
WARN
(
"Unsupported cChannels (%d)
\n
"
,
lpmcd
->
cChannels
);
return
MMSYSERR_INVALPARAM
;
}
if
(
MIX_LineSetVolume
(
mixer
.
mixerCtrls
[
lpmcd
->
dwControlID
].
dwLineID
,
lpmcd
->
cChannels
,
left
,
right
)
)
ret
=
MMSYSERR_NOERROR
;
}
break
;
case
MIXERCONTROL_CONTROLTYPE_MUTE
:
case
MIXERCONTROL_CONTROLTYPE_ONOFF
:
TRACE
(
"%s MIXERCONTROLDETAILS_BOOLEAN[%u]
\n
"
,
getControlType
(
dwControlType
),
lpmcd
->
cChannels
);
{
LPMIXERCONTROLDETAILS_BOOLEAN
mcdb
;
if
(
lpmcd
->
cbDetails
!=
sizeof
(
MIXERCONTROLDETAILS_BOOLEAN
))
{
WARN
(
"invalid parameter: cbDetails
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
mcdb
=
(
LPMIXERCONTROLDETAILS_BOOLEAN
)
lpmcd
->
paDetails
;
if
(
MIX_LineSetMute
(
mixer
.
mixerCtrls
[
lpmcd
->
dwControlID
].
dwLineID
,
mcdb
->
fValue
)
)
ret
=
MMSYSERR_NOERROR
;
}
break
;
case
MIXERCONTROL_CONTROLTYPE_MIXER
:
case
MIXERCONTROL_CONTROLTYPE_MUX
:
default:
FIXME
(
"controlType : %s
\n
"
,
getControlType
(
dwControlType
));
ret
=
MMSYSERR_NOTSUPPORTED
;
break
;
}
return
ret
;
}
/**************************************************************************
* mxdMessage
*/
DWORD
WINAPI
CoreAudio_mxdMessage
(
UINT
wDevID
,
UINT
wMsg
,
DWORD_PTR
dwUser
,
DWORD_PTR
dwParam1
,
DWORD_PTR
dwParam2
)
{
TRACE
(
"(%04X, %s, %08lX, %08lX, %08lX);
\n
"
,
wDevID
,
getMessage
(
wMsg
),
dwUser
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
case
DRVM_INIT
:
case
DRVM_EXIT
:
case
DRVM_ENABLE
:
case
DRVM_DISABLE
:
/* FIXME: Pretend this is supported */
return
0
;
case
MXDM_OPEN
:
return
MIX_Open
(
wDevID
,
(
LPMIXEROPENDESC
)
dwParam1
,
dwParam2
);
case
MXDM_CLOSE
:
return
MMSYSERR_NOERROR
;
case
MXDM_GETNUMDEVS
:
return
MIX_GetNumDevs
();
case
MXDM_GETDEVCAPS
:
return
MIX_GetDevCaps
(
wDevID
,
(
LPMIXERCAPSW
)
dwParam1
,
dwParam2
);
case
MXDM_GETLINEINFO
:
return
MIX_GetLineInfo
(
wDevID
,
(
LPMIXERLINEW
)
dwParam1
,
dwParam2
);
case
MXDM_GETLINECONTROLS
:
return
MIX_GetLineControls
(
wDevID
,
(
LPMIXERLINECONTROLSW
)
dwParam1
,
dwParam2
);
case
MXDM_GETCONTROLDETAILS
:
return
MIX_GetControlDetails
(
wDevID
,
(
LPMIXERCONTROLDETAILS
)
dwParam1
,
dwParam2
);
case
MXDM_SETCONTROLDETAILS
:
return
MIX_SetControlDetails
(
wDevID
,
(
LPMIXERCONTROLDETAILS
)
dwParam1
,
dwParam2
);
default:
WARN
(
"unknown message %d!
\n
"
,
wMsg
);
return
MMSYSERR_NOTSUPPORTED
;
}
}
dlls/winecoreaudio.drv/mmdevdrv.c
View file @
a773b16d
...
...
@@ -35,9 +35,9 @@
#include "devpkey.h"
#include "dshow.h"
#include "dsound.h"
#include "endpointvolume.h"
#include "initguid.h"
#include "endpointvolume.h"
#include "audioclient.h"
#include "audiopolicy.h"
...
...
dlls/winecoreaudio.drv/winecoreaudio.drv.spec
View file @
a773b16d
# WinMM driver functions
@ stdcall -private DriverProc(long long long long long) CoreAudio_DriverProc
@ stdcall -private widMessage(long long long long long) CoreAudio_widMessage
@ stdcall -private wodMessage(long long long long long) CoreAudio_wodMessage
@ stdcall -private midMessage(long long long long long) CoreAudio_midMessage
@ stdcall -private modMessage(long long long long long) CoreAudio_modMessage
@ stdcall -private mxdMessage(long long long long long) CoreAudio_mxdMessage
# MMDevAPI driver functions
@ stdcall -private GetPriority() AUDDRV_GetPriority
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment