/************************************************************************** * ALSA_TraceParameters [internal] * * used to trace format changes, hw and sw parameters */ void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full) { int err; snd_pcm_format_t format; snd_pcm_access_t access; #define X(x) ((x)? "true" : "false") if (full) TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s " "halfd=%s joint=%s\n", X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)), X(snd_pcm_hw_params_can_overrange(hw_params)), X(snd_pcm_hw_params_can_pause(hw_params)), X(snd_pcm_hw_params_can_resume(hw_params)), X(snd_pcm_hw_params_can_sync_start(hw_params)), X(snd_pcm_hw_params_is_batch(hw_params)), X(snd_pcm_hw_params_is_block_transfer(hw_params)), X(snd_pcm_hw_params_is_double(hw_params)), X(snd_pcm_hw_params_is_half_duplex(hw_params)), X(snd_pcm_hw_params_is_joint_duplex(hw_params))); #undef X err = snd_pcm_hw_params_get_access(hw_params, &access); if (err >= 0) { TRACE("access=%s\n", snd_pcm_access_name(access)); } else { snd_pcm_access_mask_t * acmask; acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof()); snd_pcm_hw_params_get_access_mask(hw_params, acmask); for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++) if (snd_pcm_access_mask_test(acmask, access)) TRACE("access=%s\n", snd_pcm_access_name(access)); HeapFree( GetProcessHeap(), 0, acmask ); } err = snd_pcm_hw_params_get_format(hw_params, &format); if (err >= 0) { TRACE("format=%s\n", snd_pcm_format_name(format)); } else { snd_pcm_format_mask_t * fmask; fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof()); snd_pcm_hw_params_get_format_mask(hw_params, fmask); for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++) if ( snd_pcm_format_mask_test(fmask, format) ) TRACE("format=%s\n", snd_pcm_format_name(format)); HeapFree( GetProcessHeap(), 0, fmask ); } do { int err=0; unsigned int val=0; err = snd_pcm_hw_params_get_channels(hw_params, &val); if (err<0) { unsigned int min = 0; unsigned int max = 0; err = snd_pcm_hw_params_get_channels_min(hw_params, &min), err = snd_pcm_hw_params_get_channels_max(hw_params, &max); TRACE("channels_min=%u, channels_min_max=%u\n", min, max); } else { TRACE("channels=%d\n", val); } } while(0); do { int err=0; snd_pcm_uframes_t val=0; err = snd_pcm_hw_params_get_buffer_size(hw_params, &val); if (err<0) { snd_pcm_uframes_t min = 0; snd_pcm_uframes_t max = 0; err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min), err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max); TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max); } else { TRACE("buffer_size=%lu\n", val); } } while(0); #define X(x) do { \ int err=0; \ int dir=0; \ unsigned int val=0; \ err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \ if (err<0) { \ unsigned int min = 0; \ unsigned int max = 0; \ err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \ err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \ TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \ } else \ TRACE(#x "=%d\n", val); \ } while(0) X(rate); X(buffer_time); X(periods); do { int err=0; int dir=0; snd_pcm_uframes_t val=0; err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir); if (err<0) { snd_pcm_uframes_t min = 0; snd_pcm_uframes_t max = 0; err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir), err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir); TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max); } else { TRACE("period_size=%lu\n", val); } } while(0); X(period_time); #undef X if (!sw) return; }
static void set_params(void) { snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t buffer_size; int err; size_t n; unsigned int rate; snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(handle, params); if (err < 0) { error("Broken configuration for this PCM: no configurations available"); exit(EXIT_FAILURE); } if (mmap_flag) { snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof()); snd_pcm_access_mask_none(mask); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX); err = snd_pcm_hw_params_set_access_mask(handle, params, mask); } else if (interleaved) err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); else err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED); if (err < 0) { error("Access type not available"); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_format(handle, params, hwparams.format); if (err < 0) { error("Sample format non available"); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels); if (err < 0) { error("Channels count non available"); exit(EXIT_FAILURE); } #if 0 err = snd_pcm_hw_params_set_periods_min(handle, params, 2); assert(err >= 0); #endif rate = hwparams.rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &hwparams.rate, 0); assert(err >= 0); if ((float)rate * 1.05 < hwparams.rate || (float)rate * 0.95 > hwparams.rate) { if (!quiet_mode) { char plugex[64]; const char *pcmname = snd_pcm_name(handle); fprintf(stderr, "Warning: rate is not accurate (requested = %iHz, got = %iHz)\n", rate, hwparams.rate); if (! pcmname || strchr(snd_pcm_name(handle), ':')) *plugex = 0; else snprintf(plugex, sizeof(plugex), "(-Dplug:%s)", snd_pcm_name(handle)); fprintf(stderr, " please, try the plug plugin %s\n", plugex); } } rate = hwparams.rate; if (buffer_time == 0 && buffer_frames == 0) { err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); assert(err >= 0); if (buffer_time > 500000) buffer_time = 500000; } if (period_time == 0 && period_frames == 0) { if (buffer_time > 0) period_time = buffer_time / 4; else period_frames = buffer_frames / 4; } if (period_time > 0) err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0); else err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_frames, 0); assert(err >= 0); if (buffer_time > 0) { err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0); } else { err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_frames); } assert(err >= 0); err = snd_pcm_hw_params(handle, params); if (err < 0) { error("Unable to install hw params:"); snd_pcm_hw_params_dump(params, log); exit(EXIT_FAILURE); } snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); snd_pcm_hw_params_get_buffer_size(params, &buffer_size); if (chunk_size == buffer_size) { error("Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size); exit(EXIT_FAILURE); } snd_pcm_sw_params_current(handle, swparams); if (avail_min < 0) n = chunk_size; else n = (double) rate * avail_min / 1000000; err = snd_pcm_sw_params_set_avail_min(handle, swparams, n); if (err < 0) { error("Setting sw params failed\n"); } /* round up to closest transfer boundary */ n = buffer_size; if (start_delay <= 0) { start_threshold = n + (double) rate * start_delay / 1000000; } else start_threshold = (double) rate * start_delay / 1000000; if (start_threshold < 1) start_threshold = 1; if (start_threshold > n) start_threshold = n; err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); assert(err >= 0); if (stop_delay <= 0) stop_threshold = buffer_size + (double) rate * stop_delay / 1000000; else stop_threshold = (double) rate * stop_delay / 1000000; err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); assert(err >= 0); if (snd_pcm_sw_params(handle, swparams) < 0) { error("unable to install sw params:"); snd_pcm_sw_params_dump(swparams, log); exit(EXIT_FAILURE); } if (verbose) snd_pcm_dump(handle, log); bits_per_sample = snd_pcm_format_physical_width(hwparams.format); bits_per_frame = bits_per_sample * hwparams.channels; chunk_bytes = chunk_size * bits_per_frame / 8; audiobuf = realloc(audiobuf, chunk_bytes); if (audiobuf == NULL) { error("not enough memory"); exit(EXIT_FAILURE); } // fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size); /* stereo VU-meter isn't always available... */ if (vumeter == VUMETER_STEREO) { if (hwparams.channels != 2 || !interleaved || verbose > 2) vumeter = VUMETER_MONO; } /* show mmap buffer arragment */ if (mmap_flag && verbose) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset; int i; err = snd_pcm_mmap_begin(handle, &areas, &offset, &chunk_size); if (err < 0) { error("snd_pcm_mmap_begin problem: %s", snd_strerror(err)); exit(EXIT_FAILURE); } for (i = 0; i < hwparams.channels; i++) fprintf(stderr, "mmap_area[%i] = %p,%u,%u (%u)\n", i, areas[i].addr, areas[i].first, areas[i].step, snd_pcm_format_physical_width(hwparams.format)); /* not required, but for sure */ snd_pcm_mmap_commit(handle, offset, 0); } buffer_frames = buffer_size; /* for position test */ }
/*---------------------------------------------------------------------------- ** ALSA_ComputeCaps ** ** Given an ALSA PCM, figure out our HW CAPS structure info. ** ctl can be null, pcm is required, as is all output parms. ** */ static int ALSA_ComputeCaps(snd_ctl_t *ctl, snd_pcm_t *pcm, WORD *channels, DWORD *flags, DWORD *formats, DWORD *supports) { snd_pcm_hw_params_t *hw_params; snd_pcm_format_mask_t *fmask; snd_pcm_access_mask_t *acmask; unsigned int ratemin = 0; unsigned int ratemax = 0; unsigned int chmin = 0; unsigned int chmax = 0; int rc, dir = 0; hw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof() ); fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof() ); acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof() ); if ((rc = snd_pcm_hw_params_any(pcm, hw_params)) < 0) goto done; snd_pcm_hw_params_get_format_mask(hw_params, fmask); if ((rc = snd_pcm_hw_params_get_access_mask(hw_params, acmask)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_channels_min(hw_params, &chmin)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_channels_max(hw_params, &chmax)) < 0) goto done; #define X(r,v) \ if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \ { \ if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \ { \ if (chmin <= 1 && 1 <= chmax) \ *formats |= WAVE_FORMAT_##v##M08; \ if (chmin <= 2 && 2 <= chmax) \ *formats |= WAVE_FORMAT_##v##S08; \ } \ if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \ { \ if (chmin <= 1 && 1 <= chmax) \ *formats |= WAVE_FORMAT_##v##M16; \ if (chmin <= 2 && 2 <= chmax) \ *formats |= WAVE_FORMAT_##v##S16; \ } \ } X(11025,1); X(22050,2); X(44100,4); X(48000,48); X(96000,96); #undef X if (chmin > 1) FIXME("Device has a minimum of %d channels\n", chmin); *channels = chmax; /* FIXME: is sample accurate always true ? ** Can we do WAVECAPS_PITCH, WAVECAPS_SYNC, or WAVECAPS_PLAYBACKRATE? */ *supports |= WAVECAPS_SAMPLEACCURATE; *supports |= WAVECAPS_DIRECTSOUND; /* check for volume control support */ if (ctl) { if (snd_ctl_name(ctl)) { snd_hctl_t *hctl; if (snd_hctl_open(&hctl, snd_ctl_name(ctl), 0) >= 0) { snd_hctl_load(hctl); if (!ALSA_CheckSetVolume( hctl, NULL, NULL, NULL, NULL, NULL, NULL, NULL )) { *supports |= WAVECAPS_VOLUME; if (chmin <= 2 && 2 <= chmax) *supports |= WAVECAPS_LRVOLUME; } snd_hctl_free(hctl); snd_hctl_close(hctl); } } } *flags = DSCAPS_CERTIFIED | DSCAPS_CONTINUOUSRATE; *flags |= DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO; *flags |= DSCAPS_SECONDARY8BIT | DSCAPS_SECONDARY16BIT; if (*formats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 | WAVE_FORMAT_96M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_96M16) ) *flags |= DSCAPS_PRIMARYMONO; if (*formats & (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) ) *flags |= DSCAPS_PRIMARYSTEREO; if (*formats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 | WAVE_FORMAT_96M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_96S08) ) *flags |= DSCAPS_PRIMARY8BIT; if (*formats & (WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) ) *flags |= DSCAPS_PRIMARY16BIT; rc = 0; done: if (rc < 0) ERR("failed: %s(%d)\n", snd_strerror(rc), rc); HeapFree( GetProcessHeap(), 0, hw_params ); HeapFree( GetProcessHeap(), 0, fmask ); HeapFree( GetProcessHeap(), 0, acmask ); return rc; }