This patch adds multiple sound card support to the ALSA driver.
My system shows 3 sound devices: a builtin sound card, a builtin modem and a usb sound card.
The wave regression tests have problems with the modem. I'm not sure it is an ALSA problem or a problem with this patch.
Could someone familiar with ALSA look this patch over. I would also like feedback from any brave testers that have multiple sound cards.
Thanks.
Index: dlls/winmm/winealsa/audio.c =================================================================== RCS file: /home/wine/wine/dlls/winmm/winealsa/audio.c,v retrieving revision 1.65 diff -u -p -r1.65 audio.c --- dlls/winmm/winealsa/audio.c 17 Mar 2005 18:54:20 -0000 1.65 +++ dlls/winmm/winealsa/audio.c 17 Mar 2005 20:51:58 -0000 @@ -74,8 +74,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(wave); snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);
-#define MAX_WAVEOUTDRV (1) -#define MAX_WAVEINDRV (1) +#define MAX_WAVEOUTDRV (6) +#define MAX_WAVEINDRV (6)
/* state diagram for waveOut writing: * @@ -389,6 +389,7 @@ static int ALSA_InitializeVolumeCtl(WINE snd_hctl_elem_t * elem; int nCtrls; int i; + char device[8];
snd_ctl_card_info_alloca(&cardinfo); memset(cardinfo,0,snd_ctl_card_info_sizeof()); @@ -416,13 +417,15 @@ static int ALSA_InitializeVolumeCtl(WINE } \ } while(0)
- EXIT_ON_ERROR( snd_ctl_open(&ctl,"hw",0) , "ctl open failed" ); + sprintf(device, "hw:%ld", ALSA_WodNumDevs); + + EXIT_ON_ERROR( snd_ctl_open(&ctl,device,0) , "ctl open failed" ); EXIT_ON_ERROR( snd_ctl_card_info(ctl, cardinfo), "card info failed"); EXIT_ON_ERROR( snd_ctl_elem_list(ctl, elemlist), "elem list failed");
nCtrls = snd_ctl_elem_list_get_count(elemlist);
- EXIT_ON_ERROR( snd_hctl_open(&hctl,"hw",0), "hctl open failed"); + EXIT_ON_ERROR( snd_hctl_open(&hctl,device,0), "hctl open failed"); EXIT_ON_ERROR( snd_hctl_load(hctl), "hctl load failed" );
elem=snd_hctl_first_elem(hctl); @@ -636,21 +639,21 @@ static char* ALSA_strdup(char *s) { /****************************************************************** * ALSA_GetDeviceFromReg * - * Returns either "default" or reads the registry so the user can + * Returns either "plug:hw" or reads the registry so the user can * override the playback/record device used. */ static char *ALSA_GetDeviceFromReg(const char *value) { DWORD res; DWORD type; - HKEY playbackKey = 0; + HKEY key = 0; char *result = NULL; DWORD resultSize;
- res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\Wine\Wine\Config\ALSA", 0, KEY_QUERY_VALUE, &playbackKey); + res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\Wine\Wine\Config\ALSA", 0, KEY_QUERY_VALUE, &key); if (res != ERROR_SUCCESS) goto end;
- res = RegQueryValueExA(playbackKey, value, NULL, &type, NULL, &resultSize); + res = RegQueryValueExA(key, value, NULL, &type, NULL, &resultSize); if (res != ERROR_SUCCESS) goto end;
if (type != REG_SZ) { @@ -659,11 +662,15 @@ static char *ALSA_GetDeviceFromReg(const }
result = HeapAlloc(GetProcessHeap(), 0, resultSize); - res = RegQueryValueExA(playbackKey, value, NULL, NULL, result, &resultSize); + res = RegQueryValueExA(key, value, NULL, NULL, result, &resultSize);
end: - if (!result) result = ALSA_strdup("default"); - if (playbackKey) RegCloseKey(playbackKey); + if (!result) + result = ALSA_strdup("plug:hw"); + + if (key) + RegCloseKey(key); + return result; }
@@ -685,24 +692,7 @@ LONG ALSA_WaveInit(void) int err=0; WINE_WAVEOUT* wwo; WINE_WAVEIN* wwi; - static const WCHAR init_out[] = {'S','B','1','6',' ','W','a','v','e',' ','O','u','t',0}; - static const WCHAR init_in [] = {'S','B','1','6',' ','W','a','v','e',' ','I','n',0}; - - wwo = &WOutDev[0]; - - /* FIXME: use better values */ - wwo->device = ALSA_GetDeviceFromReg("PlaybackDevice"); - TRACE("using waveout device "%s"\n", wwo->device); - - snprintf(wwo->interface_name, sizeof(wwo->interface_name), "winealsa: %s", wwo->device); - - wwo->caps.wMid = 0x0002; - wwo->caps.wPid = 0x0104; - strcpyW(wwo->caps.szPname, init_out); - wwo->caps.vDriverVersion = 0x0100; - wwo->caps.dwFormats = 0x00000000; - wwo->caps.dwSupport = WAVECAPS_VOLUME; - strcpy(wwo->ds_desc.szDrvname, "winealsa.drv"); + int i;
if (!wine_dlopen("libasound.so.2", RTLD_LAZY|RTLD_GLOBAL, NULL, 0)) { @@ -710,191 +700,226 @@ LONG ALSA_WaveInit(void) return -1; }
- snd_pcm_info_alloca(&info); - snd_pcm_hw_params_alloca(&hw_params); + ALSA_WodNumDevs = 0;
-#define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0) + for (i = 0; i < MAX_WAVEOUTDRV; i++) + { + char device[64]; + char * regdev; + WCHAR nameW[64]; + snd_pcm_format_mask_t * fmask; + snd_pcm_access_mask_t * acmask;
- h = NULL; - ALSA_WodNumDevs = 0; - EXIT_ON_ERROR( snd_pcm_open(&h, wwo->device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) , "open pcm" ); - if (!h) return -1; - ALSA_WodNumDevs++; - - EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" ); - - TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n", - snd_pcm_info_get_device(info), - snd_pcm_info_get_id(info), - snd_pcm_info_get_name(info), - snd_pcm_info_get_subdevice(info), - snd_pcm_info_get_subdevice_name(info), - snd_pcm_info_get_subdevices_avail(info), - snd_pcm_info_get_subdevices_count(info), - snd_pcm_stream_name(snd_pcm_info_get_stream(info)), - (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX")); + wwo = &WOutDev[ALSA_WodNumDevs];
- strcpy(wwo->ds_desc.szDesc, snd_pcm_info_get_name(info)); - EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" ); -#undef EXIT_ON_ERROR + regdev = ALSA_GetDeviceFromReg("PlaybackDevice"); + sprintf(device, "%s:%d", regdev, i); + HeapFree(GetProcessHeap(), 0, regdev); + wwo->device = HeapAlloc(GetProcessHeap(), 0, strlen(device)); + strcpy(wwo->device, device); + TRACE("using waveout device "%s"\n", wwo->device); + + snprintf(wwo->interface_name, sizeof(wwo->interface_name), "winealsa: %s", wwo->device); + + wwo->caps.wMid = 0x0002; + wwo->caps.wPid = 0x0104; + wwo->caps.vDriverVersion = 0x0100; + wwo->caps.dwFormats = 0x00000000; + wwo->caps.dwSupport = WAVECAPS_VOLUME; + strcpy(wwo->ds_desc.szDrvname, "winealsa.drv");
- err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir); - err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir); - err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin); - err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax); - if (TRACE_ON(wave)) - ALSA_TraceParameters(hw_params, NULL, TRUE); + snd_pcm_info_alloca(&info); + snd_pcm_hw_params_alloca(&hw_params);
- { - snd_pcm_format_mask_t * fmask; +#define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0) + + h = NULL; + snd_pcm_open(&h, wwo->device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if (!h) + break; + + EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" ); + + TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n", + snd_pcm_info_get_device(info), + snd_pcm_info_get_id(info), + snd_pcm_info_get_name(info), + snd_pcm_info_get_subdevice(info), + snd_pcm_info_get_subdevice_name(info), + snd_pcm_info_get_subdevices_avail(info), + snd_pcm_info_get_subdevices_count(info), + snd_pcm_stream_name(snd_pcm_info_get_stream(info)), + (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX")); + + strcpy(wwo->ds_desc.szDesc, snd_pcm_info_get_name(info)); + MultiByteToWideChar(CP_ACP, 0, wwo->ds_desc.szDesc, -1, nameW, sizeof(nameW)/sizeof(WCHAR)); + strcpyW(wwo->caps.szPname, nameW); + EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" ); +#undef EXIT_ON_ERROR + + err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir); + err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir); + err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin); + err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax); + if (TRACE_ON(wave)) + ALSA_TraceParameters(hw_params, NULL, TRUE);
snd_pcm_format_mask_alloca(&fmask); snd_pcm_hw_params_get_format_mask(hw_params, fmask);
#define X(r,v) \ - if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \ - { \ - if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \ - { \ + if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \ + { \ + if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \ + { \ if (chmin <= 1 && 1 <= chmax) \ wwo->caps.dwFormats |= WAVE_FORMAT_##v##M08; \ if (chmin <= 2 && 2 <= chmax) \ wwo->caps.dwFormats |= WAVE_FORMAT_##v##S08; \ - } \ - if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \ - { \ + } \ + if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \ + { \ if (chmin <= 1 && 1 <= chmax) \ wwo->caps.dwFormats |= WAVE_FORMAT_##v##M16; \ if (chmin <= 2 && 2 <= chmax) \ wwo->caps.dwFormats |= WAVE_FORMAT_##v##S16; \ - } \ - } - X(11025,1); - X(22050,2); - X(44100,4); - X(48000,48); - X(96000,96); + } \ + } + X(11025,1); + X(22050,2); + X(44100,4); + X(48000,48); + X(96000,96); #undef X - }
- if ( chmin > 1) FIXME("-\n"); - wwo->caps.wChannels = chmax; - if (chmin <= 2 && 2 <= chmax) - wwo->caps.dwSupport |= WAVECAPS_LRVOLUME; + if (chmin > 1) + FIXME("-\n"); + wwo->caps.wChannels = chmax; + if (chmin <= 2 && 2 <= chmax) + wwo->caps.dwSupport |= WAVECAPS_LRVOLUME;
- /* FIXME: always true ? */ - wwo->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE; + /* FIXME: always true ? */ + wwo->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
- { - snd_pcm_access_mask_t * acmask; snd_pcm_access_mask_alloca(&acmask); snd_pcm_hw_params_get_access_mask(hw_params, acmask);
/* FIXME: NONITERLEAVED and COMPLEX are not supported right now */ if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) ) wwo->caps.dwSupport |= WAVECAPS_DIRECTSOUND; - }
- TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n", - wwo->caps.dwFormats, wwo->caps.dwSupport); + TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n", + wwo->caps.dwFormats, wwo->caps.dwSupport);
- snd_pcm_close(h); + snd_pcm_close(h);
- ALSA_InitializeVolumeCtl(wwo); + ALSA_InitializeVolumeCtl(wwo);
- wwi = &WInDev[0]; + ALSA_WodNumDevs++; + }
- /* FIXME: use better values */ - wwi->device = ALSA_GetDeviceFromReg("RecordDevice"); - TRACE("using wavein device "%s"\n", wwi->device); + ALSA_WidNumDevs = 0;
- snprintf(wwi->interface_name, sizeof(wwi->interface_name), "winealsa: %s", wwi->device); + for (i = 0; i < MAX_WAVEINDRV; i++) + { + char device[64]; + char * regdev; + WCHAR nameW[64]; + snd_pcm_format_mask_t * fmask; + snd_pcm_access_mask_t * acmask;
- wwi->caps.wMid = 0x0002; - wwi->caps.wPid = 0x0104; - strcpyW(wwi->caps.szPname, init_in); - wwi->caps.vDriverVersion = 0x0100; - wwi->caps.dwFormats = 0x00000000; - wwi->caps.dwSupport = WAVECAPS_VOLUME; - strcpy(wwi->ds_desc.szDrvname, "winealsa.drv"); + wwi = &WInDev[ALSA_WidNumDevs];
- snd_pcm_info_alloca(&info); - snd_pcm_hw_params_alloca(&hw_params); + regdev = ALSA_GetDeviceFromReg("CaptureDevice"); + sprintf(device, "%s:%d", regdev, i); + HeapFree(GetProcessHeap(), 0, regdev); + wwi->device = HeapAlloc(GetProcessHeap(), 0, strlen(device)); + strcpy(wwi->device, device); + + TRACE("using wavein device "%s"\n", wwi->device); + + snprintf(wwi->interface_name, sizeof(wwi->interface_name), "winealsa: %s", wwi->device); + + wwi->caps.wMid = 0x0002; + wwi->caps.wPid = 0x0104; + wwi->caps.vDriverVersion = 0x0100; + wwi->caps.dwFormats = 0x00000000; + wwi->caps.dwSupport = WAVECAPS_VOLUME; + strcpy(wwi->ds_desc.szDrvname, "winealsa.drv");
-#define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0) + snd_pcm_info_alloca(&info); + snd_pcm_hw_params_alloca(&hw_params);
- h = NULL; - ALSA_WidNumDevs = 0; - EXIT_ON_ERROR( snd_pcm_open(&h, wwi->device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) , "open pcm" ); - if (!h) return -1; - ALSA_WidNumDevs++; - - EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" ); - - TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n", - snd_pcm_info_get_device(info), - snd_pcm_info_get_id(info), - snd_pcm_info_get_name(info), - snd_pcm_info_get_subdevice(info), - snd_pcm_info_get_subdevice_name(info), - snd_pcm_info_get_subdevices_avail(info), - snd_pcm_info_get_subdevices_count(info), - snd_pcm_stream_name(snd_pcm_info_get_stream(info)), - (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX")); +#define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0)
- strcpy(wwi->ds_desc.szDesc, snd_pcm_info_get_name(info)); - EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" ); + h = NULL; + snd_pcm_open(&h, wwi->device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + if (!h) + break; + + EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" ); + + TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n", + snd_pcm_info_get_device(info), + snd_pcm_info_get_id(info), + snd_pcm_info_get_name(info), + snd_pcm_info_get_subdevice(info), + snd_pcm_info_get_subdevice_name(info), + snd_pcm_info_get_subdevices_avail(info), + snd_pcm_info_get_subdevices_count(info), + snd_pcm_stream_name(snd_pcm_info_get_stream(info)), + (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX")); + + strcpy(wwi->ds_desc.szDesc, snd_pcm_info_get_name(info)); + MultiByteToWideChar(CP_ACP, 0, wwi->ds_desc.szDesc, -1, nameW, sizeof(nameW)/sizeof(WCHAR)); + strcpyW(wwi->caps.szPname, nameW); + EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" ); #undef EXIT_ON_ERROR - err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir); - err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir); - err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin); - err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax); + err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir); + err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir); + err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin); + err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
- if (TRACE_ON(wave)) - ALSA_TraceParameters(hw_params, NULL, TRUE); - - { - snd_pcm_format_mask_t * fmask; + if (TRACE_ON(wave)) + ALSA_TraceParameters(hw_params, NULL, TRUE);
snd_pcm_format_mask_alloca(&fmask); snd_pcm_hw_params_get_format_mask(hw_params, fmask);
#define X(r,v) \ - if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \ - { \ - if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \ - { \ + if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \ + { \ + if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \ + { \ if (chmin <= 1 && 1 <= chmax) \ wwi->caps.dwFormats |= WAVE_FORMAT_##v##M08; \ if (chmin <= 2 && 2 <= chmax) \ wwi->caps.dwFormats |= WAVE_FORMAT_##v##S08; \ - } \ - if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \ - { \ + } \ + if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \ + { \ if (chmin <= 1 && 1 <= chmax) \ wwi->caps.dwFormats |= WAVE_FORMAT_##v##M16; \ if (chmin <= 2 && 2 <= chmax) \ wwi->caps.dwFormats |= WAVE_FORMAT_##v##S16; \ - } \ - } - X(11025,1); - X(22050,2); - X(44100,4); - X(48000,48); - X(96000,96); + } \ + } + X(11025,1); + X(22050,2); + X(44100,4); + X(48000,48); + X(96000,96); #undef X - }
- if ( chmin > 1) FIXME("-\n"); - wwi->caps.wChannels = chmax; - if (chmin <= 2 && 2 <= chmax) - wwi->caps.dwSupport |= WAVECAPS_LRVOLUME; + if (chmin > 1) + FIXME("-\n"); + wwi->caps.wChannels = chmax; + if (chmin <= 2 && 2 <= chmax) + wwi->caps.dwSupport |= WAVECAPS_LRVOLUME;
- /* FIXME: always true ? */ - wwi->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE; + /* FIXME: always true ? */ + wwi->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
- { - snd_pcm_access_mask_t * acmask; snd_pcm_access_mask_alloca(&acmask); snd_pcm_hw_params_get_access_mask(hw_params, acmask);
@@ -904,12 +929,14 @@ LONG ALSA_WaveInit(void) wwi->caps.dwSupport |= WAVECAPS_DIRECTSOUND; #endif } - }
- TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n", - wwi->caps.dwFormats, wwi->caps.dwSupport); + TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n", + wwi->caps.dwFormats, wwi->caps.dwSupport);
- snd_pcm_close(h); + snd_pcm_close(h); + + ALSA_WidNumDevs++; + }
return 0; }