winealsa: implement opening/closing of device and device caps, try 4
Maarten Lankhorst
m.b.lankhorst at gmail.com
Fri Apr 20 13:33:43 CDT 2007
As the second patch is moving initialisation to the code itself, I
thought it would be appropiate to do the same.
Deadlocks during normal operation are none existant any more. If winmm
shuts down untidily there's still a small chance though. Continuing more
about this on wine-devel.
-------------- next part --------------
>From dba06f149a549075ee10179bf426f6b81ceb0e3e Mon Sep 17 00:00:00 2001
From: maarten lankhorst <m.b.lankhorst at gmail.com>
Date: Fri, 20 Apr 2007 19:08:43 +0200
Subject: [PATCH] winealsa: implement opening/closing of device and device caps, try 4
---
dlls/winealsa.drv/mixer.c | 379 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 376 insertions(+), 3 deletions(-)
diff --git a/dlls/winealsa.drv/mixer.c b/dlls/winealsa.drv/mixer.c
index 6d0c161..5189c41 100644
--- a/dlls/winealsa.drv/mixer.c
+++ b/dlls/winealsa.drv/mixer.c
@@ -55,6 +55,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(mixer);
#ifdef HAVE_ALSA
+#define WINE_MIXER_MANUF_ID 0xAA
+#define WINE_MIXER_PRODUCT_ID 0x55
+#define WINE_MIXER_VERSION 0x0100
+
/* Generic notes:
* In windows it seems to be required for all controls to have a volume switch
* In alsa that's optional
@@ -96,6 +100,20 @@ static const char * getMessage(UINT uMsg)
return str;
}
+/* A simple declaration of a line control
+ * These are each of the channels that show up
+ */
+typedef struct line {
+ /* Name we present to outside world */
+ WCHAR name[MAXPNAMELEN];
+
+ DWORD component;
+ DWORD dst;
+ DWORD capt;
+ DWORD chans;
+ snd_mixer_elem_t *elem;
+} line;
+
/* Mixer device */
typedef struct mixer
{
@@ -103,12 +121,58 @@ typedef struct mixer
WCHAR mixername[MAXPNAMELEN];
int chans, dests;
+ LPDRVCALLBACK callback;
+ DWORD_PTR callbackpriv;
+ HDRVR hmx;
+
+ line *lines;
} mixer;
#define MAX_MIXERS 32
static int cards = 0;
static mixer mixdev[MAX_MIXERS];
+static HANDLE thread;
+static int elem_callback(snd_mixer_elem_t *elem, unsigned int mask);
+static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam);
+static CRITICAL_SECTION elem_crst;
+static int msg_pipe[2];
+static LONG refcnt;
+
+/* found channel names in alsa lib, alsa api doesn't have another way for this
+ * map name -> componenttype, worst case we get a wrong componenttype which is
+ * mostly harmless
+ */
+
+static const struct mixerlinetype {
+ const char *name; DWORD cmpt;
+} converttable[] = {
+ { "Master", MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, },
+ { "Capture", MIXERLINE_COMPONENTTYPE_DST_WAVEIN, },
+ { "PCM", MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, },
+ { "PC Speaker", MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER, },
+ { "Synth", MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER, },
+ { "Headphone", MIXERLINE_COMPONENTTYPE_DST_HEADPHONES, },
+ { "Mic", MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE, },
+ { "Aux", MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED, },
+ { "CD", MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC, },
+ { "Line", MIXERLINE_COMPONENTTYPE_SRC_LINE, },
+ { "Phone", MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE, },
+};
+
+/* Map name to MIXERLINE_COMPONENTTYPE_XXX */
+static int getcomponenttype(const char *name)
+{
+ int x;
+ for (x=0; x< sizeof(converttable)/sizeof(converttable[0]); ++x)
+ if (!strcasecmp(name, converttable[x].name))
+ {
+ TRACE("%d -> %s\n", x, name);
+ return converttable[x].cmpt;
+ }
+ WARN("Unknown mixer name %s, probably harmless\n", name);
+ return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
+}
/* Is this control suited for showing up? */
static int blacklisted(snd_mixer_elem_t *elem)
@@ -125,24 +189,48 @@ static int blacklisted(snd_mixer_elem_t *elem)
return blisted;
}
+/* get amount of channels for elem */
+/* Officially we should keep capture/playback seperated,
+ * but that's not going to work in the alsa api */
+static int chans(mixer *mmixer, snd_mixer_elem_t * elem, DWORD capt)
+{
+ int ret=0, chn;
+
+ if (capt && snd_mixer_selem_has_capture_volume(elem)) {
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ if (snd_mixer_selem_has_capture_channel(elem, chn))
+ ++ret;
+ } else {
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ if (snd_mixer_selem_has_playback_channel(elem, chn))
+ ++ret;
+ }
+ if (!ret)
+ FIXME("Mixer channel %s was found for %s, but no channels were found? Wrong selection!\n", snd_mixer_selem_get_name(elem), (snd_mixer_selem_has_playback_volume(elem) ? "playback" : "capture"));
+ return ret;
+}
+
static void ALSA_MixerInit(void)
{
int x, mixnum = 0;
for (x = 0; x < MAX_MIXERS; ++x)
{
- int card, err;
+ int card, err, y;
char cardind[6], cardname[10];
BOOL hascapt=0, hasmast=0;
+ line *mline;
snd_ctl_t *ctl;
snd_mixer_elem_t *elem, *mastelem = NULL, *captelem = NULL;
snd_ctl_card_info_t *info = NULL;
snd_ctl_card_info_alloca(&info);
+ mixdev[mixnum].lines = NULL;
+ mixdev[mixnum].callback = 0;
snprintf(cardind, sizeof(cardind), "%d", x);
card = snd_card_get_index(cardind);
- if (card < 0 || card > MAX_MIXERS - 1)
+ if (card < 0)
continue;
snprintf(cardname, sizeof(cardname), "hw:%d", card);
@@ -227,6 +315,70 @@ static void ALSA_MixerInit(void)
}
mixdev[mixnum].chans += 2; /* Capture/Master */
+ mixdev[mixnum].lines = calloc(sizeof(MIXERLINEW), mixdev[mixnum].chans);
+ err = -ENOMEM;
+ if (!mixdev[mixnum].lines)
+ goto eclose;
+
+ /* Master control */
+ mline = &mixdev[mixnum].lines[0];
+ MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(mastelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
+ mline->component = getcomponenttype("Master");
+ mline->dst = 0;
+ mline->capt = 0;
+ mline->elem = mastelem;
+ mline->chans = chans(&mixdev[mixnum], mastelem, 0);
+
+ /* Capture control
+ * Note: since mmixer->dests = 1, it means only playback control is visible
+ * This makes sense, because if there are no capture sources capture control
+ * can't do anything and should be invisible */
+
+ mline++;
+ MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(captelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
+ mline->component = getcomponenttype("Capture");
+ mline->dst = 1;
+ mline->capt = 1;
+ mline->elem = captelem;
+ mline->chans = chans(&mixdev[mixnum], captelem, 1);
+
+ snd_mixer_elem_set_callback(mastelem, &elem_callback);
+ snd_mixer_elem_set_callback_private(mastelem, &mixdev[mixnum]);
+
+ if (mixdev[mixnum].dests == 2)
+ {
+ snd_mixer_elem_set_callback(captelem, &elem_callback);
+ snd_mixer_elem_set_callback_private(captelem, &mixdev[mixnum]);
+ }
+
+ y=2;
+ for (elem = snd_mixer_first_elem(mixdev[mixnum].mix); elem; elem = snd_mixer_elem_next(elem))
+ if (elem != mastelem && elem != captelem && !blacklisted(elem))
+ {
+ const char * name = snd_mixer_selem_get_name(elem);
+ DWORD comp = getcomponenttype(name);
+ snd_mixer_elem_set_callback(elem, &elem_callback);
+ snd_mixer_elem_set_callback_private(elem, &mixdev[mixnum]);
+
+ if (snd_mixer_selem_has_playback_volume(elem))
+ {
+ mline = &mixdev[mixnum].lines[y++];
+ mline->component = comp;
+ MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
+ mline->capt = mline->dst = 0;
+ mline->elem = elem;
+ mline->chans = chans(&mixdev[mixnum], elem, 0);
+ }
+ if (snd_mixer_selem_has_capture_switch(elem))
+ {
+ mline = &mixdev[mixnum].lines[y++];
+ mline->component = comp;
+ MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
+ mline->capt = mline->dst = 1;
+ mline->elem = elem;
+ mline->chans = chans(&mixdev[mixnum], elem, 1);
+ }
+ }
TRACE("%s: Amount of controls: %i/%i, name: %s\n", cardname, mixdev[mixnum].dests, mixdev[mixnum].chans, debugstr_w(mixdev[mixnum].mixername));
mixnum++;
@@ -234,22 +386,234 @@ static void ALSA_MixerInit(void)
eclose:
WARN("Error occured initialising mixer: %s\n", snd_strerror(err));
+ if (mixdev[mixnum].lines)
+ free(mixdev[mixnum].lines);
snd_mixer_close(mixdev[mixnum].mix);
}
cards = mixnum;
+
+ InitializeCriticalSection(&elem_crst);
+ elem_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MIXER.elem_crst");
TRACE("\n");
}
static void ALSA_MixerExit(void)
{
int x;
- TRACE("\n");
+ if (refcnt && write(msg_pipe[1], &x, sizeof(x)) > 0)
+ {
+ ERR("Apparantly thread is still alive, expect deadlock\n");
+ WaitForSingleObject(thread, -1);
+ refcnt = 0;
+ }
+
+ TRACE("Cleaning up\n");
+
+ elem_crst.DebugInfo->Spare[0] = 0;
+ DeleteCriticalSection(&elem_crst);
for (x = 0; x < cards; ++x)
+ {
snd_mixer_close(mixdev[x].mix);
+ free(mixdev[x].lines);
+ }
cards = 0;
}
+static mixer* MIX_GetMix(UINT wDevID)
+{
+ mixer *mmixer;
+
+ if (wDevID < 0 || wDevID >= cards)
+ {
+ WARN("Invalid mixer id: %d\n", wDevID);
+ return NULL;
+ }
+
+ mmixer = &mixdev[wDevID];
+ return mmixer;
+}
+
+/* Since alsa doesn't tell what exactly changed, just assume all affected controls changed */
+static int elem_callback(snd_mixer_elem_t *elem, unsigned int type)
+{
+ mixer *mmixer = snd_mixer_elem_get_callback_private(elem);
+ int x;
+
+ if (type != SND_CTL_EVENT_MASK_VALUE)
+ return 0;
+
+ assert(mmixer);
+
+ EnterCriticalSection(&elem_crst);
+
+ if (!mmixer->callback)
+ goto out;
+
+ for (x=0; x<mmixer->chans; ++x)
+ {
+ if (elem != mmixer->lines[x].elem)
+ continue;
+
+ TRACE("Found changed control %s\n", debugstr_w(mmixer->lines[x].name));
+ mmixer->callback(mmixer->hmx, MM_MIXM_LINE_CHANGE, mmixer->callbackpriv, x, 0);
+ }
+
+ out:
+ LeaveCriticalSection(&elem_crst);
+
+ return 0;
+}
+
+static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam)
+{
+ struct pollfd *pfds = NULL;
+ int x, y, err, mcnt, count = 1;
+
+ TRACE("%p\n", lParam);
+
+ for (x = 0; x < cards; ++x)
+ count += snd_mixer_poll_descriptors_count(mixdev[x].mix);
+
+ TRACE("Counted %d descriptors\n", count);
+ pfds = HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct pollfd));
+
+ if (!pfds)
+ {
+ WARN("Out of memory\n");
+ goto die;
+ }
+
+ pfds[0].fd = msg_pipe[0];
+ pfds[0].events = POLLIN;
+
+ y = 1;
+ for (x = 0; x < cards; ++x)
+ y += snd_mixer_poll_descriptors(mixdev[x].mix, &pfds[y], count - y);
+
+ while ((err = poll(pfds, (unsigned int) count, -1)) >= 0 || errno == EINTR || errno == EAGAIN)
+ {
+ if (pfds[0].revents & POLLIN)
+ break;
+
+ mcnt = 1;
+ for (x = y = 0; x < cards; ++x)
+ {
+ int j, max = snd_mixer_poll_descriptors_count(mixdev[x].mix);
+ for (j = 0; j < max; ++j)
+ if (pfds[mcnt+j].revents)
+ {
+ y += snd_mixer_handle_events(mixdev[x].mix);
+ break;
+ }
+ mcnt += max;
+ }
+ if (y)
+ TRACE("Handled %d events\n", y);
+ }
+
+ die:
+ TRACE("Shutting down\n");
+ if (pfds)
+ HeapFree(GetProcessHeap(), 0, pfds);
+
+ y = read(msg_pipe[0], &x, sizeof(x));
+ close(msg_pipe[1]);
+ close(msg_pipe[0]);
+ return 0;
+}
+
+static DWORD MIX_Open(UINT wDevID, LPMIXEROPENDESC desc, DWORD_PTR flags)
+{
+ mixer *mmixer = MIX_GetMix(wDevID);
+ if (!mmixer)
+ return MMSYSERR_BADDEVICEID;
+
+ flags &= CALLBACK_TYPEMASK;
+ switch (flags)
+ {
+ case CALLBACK_NULL:
+ goto done;
+
+ case CALLBACK_FUNCTION:
+ break;
+
+ default:
+ FIXME("Unhandled callback type: %08lx\n", flags & CALLBACK_TYPEMASK);
+ return MIXERR_INVALVALUE;
+ }
+
+ mmixer->callback = (LPDRVCALLBACK)desc->dwCallback;
+ mmixer->callbackpriv = desc->dwInstance;
+ mmixer->hmx = (HDRVR)desc->hmx;
+
+ done:
+ if (InterlockedIncrement(&refcnt) == 1)
+ {
+ if (pipe(msg_pipe) >= 0)
+ {
+ thread = CreateThread(NULL, 0, ALSA_MixerPollThread, NULL, 0, NULL);
+ if (!thread)
+ {
+ close(msg_pipe[0]);
+ close(msg_pipe[1]);
+ msg_pipe[0] = msg_pipe[1] = -1;
+ }
+ }
+ else
+ msg_pipe[0] = msg_pipe[1] = -1;
+ }
+
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD MIX_Close(UINT wDevID)
+{
+ int x;
+ mixer *mmixer = MIX_GetMix(wDevID);
+ if (!mmixer)
+ return MMSYSERR_BADDEVICEID;
+
+ EnterCriticalSection(&elem_crst);
+ mmixer->callback = 0;
+ LeaveCriticalSection(&elem_crst);
+
+ if (!InterlockedDecrement(&refcnt))
+ {
+ if (write(msg_pipe[1], &x, sizeof(x)) > 0)
+ {
+ TRACE("Shutting down thread...\n");
+ WaitForSingleObject(thread, -1);
+ TRACE("Done\n");
+ }
+ }
+
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD MIX_GetDevCaps(UINT wDevID, LPMIXERCAPS2W caps, DWORD_PTR parm2)
+{
+ mixer *mmixer = MIX_GetMix(wDevID);
+ MIXERCAPS2W capsW;
+
+ if (!caps)
+ return MMSYSERR_INVALPARAM;
+
+ if (!mmixer)
+ return MMSYSERR_BADDEVICEID;
+
+ memset(&capsW, 0, sizeof(MIXERCAPS2W));
+
+ capsW.wMid = WINE_MIXER_MANUF_ID;
+ capsW.wPid = WINE_MIXER_PRODUCT_ID;
+ capsW.vDriverVersion = WINE_MIXER_VERSION;
+
+ lstrcpynW(capsW.szPname, mmixer->mixername, sizeof(capsW.szPname)/sizeof(WCHAR));
+ capsW.cDestinations = mmixer->dests;
+ memcpy(caps, &capsW, min(parm2, sizeof(capsW)));
+ return MMSYSERR_NOERROR;
+}
+
#endif /*HAVE_ALSA*/
/**************************************************************************
@@ -273,6 +637,15 @@ DWORD WINAPI ALSA_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
case DRVM_DISABLE:
ret = MMSYSERR_NOERROR; break;
+ case MXDM_OPEN:
+ ret = MIX_Open(wDevID, (LPMIXEROPENDESC) dwParam1, dwParam2); break;
+
+ case MXDM_CLOSE:
+ ret = MIX_Close(wDevID); break;
+
+ case MXDM_GETDEVCAPS:
+ ret = MIX_GetDevCaps(wDevID, (LPMIXERCAPS2W)dwParam1, dwParam2); break;
+
case MXDM_GETNUMDEVS:
ret = cards; break;
--
1.4.4.2
More information about the wine-patches
mailing list