static void dump_parm(snd_pcm_hw_params_t *params) { snd_output_t *log; snd_output_stdio_attach(&log, stderr, 0); snd_pcm_hw_params_dump(params, log); snd_output_close(log); }
void set_hwparams(snd_pcm_t *phandle) { int err; snd_pcm_hw_params_t *params; err = snd_output_stdio_attach(&log, stderr, 0); if (err < 0) { fprintf(stderr, "cannot attach output stdio\n"); exit(0); } snd_pcm_hw_params_alloca(¶ms); err = snd_pcm_hw_params_any(phandle, params); if (err < 0) { fprintf(stderr, "Broken configuration for this PCM: no configurations available\n"); exit(0); } err = snd_pcm_hw_params_set_access(phandle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { fprintf(stderr, "Access type not available\n"); exit(0); } err = snd_pcm_hw_params_set_format(phandle, params, SND_PCM_FORMAT_S16_LE); if (err < 0) { fprintf(stderr, "cannot set format\n"); exit(0); } err = snd_pcm_hw_params_set_channels(phandle, params, 2); if (err < 0) { fprintf(stderr, "cannot set channels 2\n"); exit(0); } err = snd_pcm_hw_params_set_rate_near(phandle, params, 44100, 0); if (err < 0) { fprintf(stderr, "cannot set rate\n"); exit(0); } err = snd_pcm_hw_params_set_period_size_near(phandle, params, period_size); if (err < 0) { fprintf(stderr, "cannot set period size\n"); exit(0); } err = snd_pcm_hw_params(phandle, params); if (err < 0) { fprintf(stderr, "Unable to install hw params:\n"); exit(0); } snd_pcm_hw_params_dump(params, log); }
int ai_alsa_setup(audio_in_t *ai) { snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t buffer_size, period_size; int err; int dir; unsigned int rate; snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(ai->alsa.handle, params); if (err < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Broken configuration for this PCM: no configurations available.\n"); return -1; } err = snd_pcm_hw_params_set_access(ai->alsa.handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Access type not available.\n"); return -1; } err = snd_pcm_hw_params_set_format(ai->alsa.handle, params, SND_PCM_FORMAT_S16_LE); if (err < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Sample format not available.\n"); return -1; } err = snd_pcm_hw_params_set_channels(ai->alsa.handle, params, ai->req_channels); if (err < 0) { snd_pcm_hw_params_get_channels(params, &ai->channels); mp_tmsg(MSGT_TV, MSGL_ERR, "Channel count not available - reverting to default: %d\n", ai->channels); } else { ai->channels = ai->req_channels; } dir = 0; rate = ai->req_samplerate; err = snd_pcm_hw_params_set_rate_near(ai->alsa.handle, params, &rate, &dir); if (err < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Cannot set samplerate.\n"); } ai->samplerate = rate; dir = 0; ai->alsa.buffer_time = 1000000; err = snd_pcm_hw_params_set_buffer_time_near(ai->alsa.handle, params, &ai->alsa.buffer_time, &dir); if (err < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Cannot set buffer time.\n"); } dir = 0; ai->alsa.period_time = ai->alsa.buffer_time / 4; err = snd_pcm_hw_params_set_period_time_near(ai->alsa.handle, params, &ai->alsa.period_time, &dir); if (err < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Cannot set period time.\n"); } err = snd_pcm_hw_params(ai->alsa.handle, params); if (err < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Unable to install hardware parameters: %s", snd_strerror(err)); snd_pcm_hw_params_dump(params, ai->alsa.log); return -1; } dir = -1; snd_pcm_hw_params_get_period_size(params, &period_size, &dir); snd_pcm_hw_params_get_buffer_size(params, &buffer_size); ai->alsa.chunk_size = period_size; if (period_size == buffer_size) { mp_tmsg(MSGT_TV, MSGL_ERR, "Can't use period equal to buffer size (%u == %lu)\n", ai->alsa.chunk_size, (long)buffer_size); return -1; } snd_pcm_sw_params_current(ai->alsa.handle, swparams); err = snd_pcm_sw_params_set_avail_min(ai->alsa.handle, swparams, ai->alsa.chunk_size); err = snd_pcm_sw_params_set_start_threshold(ai->alsa.handle, swparams, 0); err = snd_pcm_sw_params_set_stop_threshold(ai->alsa.handle, swparams, buffer_size); if (snd_pcm_sw_params(ai->alsa.handle, swparams) < 0) { mp_tmsg(MSGT_TV, MSGL_ERR, "Unable to install software parameters:\n"); snd_pcm_sw_params_dump(swparams, ai->alsa.log); return -1; } if (mp_msg_test(MSGT_TV, MSGL_V)) { snd_pcm_dump(ai->alsa.handle, ai->alsa.log); } ai->alsa.bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE); ai->alsa.bits_per_frame = ai->alsa.bits_per_sample * ai->channels; ai->blocksize = ai->alsa.chunk_size * ai->alsa.bits_per_frame / 8; ai->samplesize = ai->alsa.bits_per_sample; ai->bytes_per_sample = ai->alsa.bits_per_sample/8; return 0; }
void *capture_thread(void *data) { AudioCapture *capture = (AudioCapture *)data; // enif_keep_resource(capture); detect_pcm(capture); set_volume(capture, 100); snd_pcm_open(&capture->handle, capture->pcm_name, SND_PCM_STREAM_CAPTURE, 0); if(!capture->handle) { fprintf(stderr, "No PCM!!\r\n"); exit(1); } snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_malloc(&hw_params); if(!hw_params) { fprintf(stderr, "Damn!! No hw_params\r\n"); exit(1); } snd_pcm_hw_params_any(capture->handle, hw_params); snd_pcm_hw_params_set_access(capture->handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format(capture->handle, hw_params, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_rate_near(capture->handle, hw_params, &capture->sample_rate, 0); snd_pcm_hw_params_set_channels(capture->handle, hw_params, capture->channels); // int buffer_size = 16384; // int period_time = 32000; // int period_size = 1024; // snd_pcm_hw_params_set_period_time_near(capture->handle, hw_params, &period_time, 0); // snd_pcm_hw_params_set_period_size_near(capture->handle, hw_params, &period_size, 0); // snd_pcm_hw_params_set_buffer_size_near(capture->handle, hw_params, &buffer_size); // capture->last_dts = 0; fprintf(stderr, "Setting params: %p, %p\r\n", capture->handle, hw_params); snd_pcm_hw_params(capture->handle, hw_params); snd_pcm_hw_params_free(hw_params); snd_pcm_prepare(capture->handle); snd_output_t *log; snd_output_stdio_attach(&log, stderr, 0); snd_pcm_hw_params_dump(hw_params, log); fprintf(stderr, "Started capture\r\n"); char *buffer = (char *)malloc(8192); // char *ptr = buffer; int size = 0; while(capture->thread_started) { int r = snd_pcm_readi(capture->handle, buffer + size, 1024); size += r*2*capture->channels; if(size < capture->frame_size) { continue; } ErlNifEnv* env = enif_alloc_env(); ErlNifBinary frame; enif_alloc_binary(capture->frame_size, &frame); memmove(frame.data, buffer, frame.size); size -= frame.size; memmove(buffer, buffer + frame.size, size); ErlNifUInt64 dts = (uint64_t)capture->counter*1024ULL*1000 / capture->sample_rate; // fprintf(stderr, "A: %d -> %d\r\n", capture->counter, dts); if(capture->last_dts > dts) { fprintf(stderr, "Achtung! ALSA audio jump: %u, %u, %u\r\n", (unsigned)capture->counter, (unsigned)capture->last_dts, (unsigned)dts); } capture->last_dts = dts; enif_send(NULL, &capture->owner_pid, env, enif_make_tuple4(env, enif_make_atom(env, "alsa"), enif_make_resource(env, capture), enif_make_uint64(env, dts), enif_make_binary(env, &frame) ) ); enif_release_binary(&frame); enif_free_env(env); capture->counter++; } fprintf(stderr, "Capture thread stopping\r\n"); // enif_release_resource(capture); snd_pcm_close(capture->handle); return 0; }
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; snd_pcm_uframes_t xfer_align; 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); } err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); 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); err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align); if (err < 0) { error(_("Unable to obtain xfer align\n")); exit(EXIT_FAILURE); } if (sleep_min) xfer_align = 1; err = snd_pcm_sw_params_set_sleep_min(handle, swparams, sleep_min); assert(err >= 0); 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); // round up to closest transfer boundary n = (buffer_size / xfer_align) * xfer_align; start_threshold = n; 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); stop_threshold = buffer_size; err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); assert(err >= 0); err = snd_pcm_sw_params_set_xfer_align(handle, swparams, xfer_align); 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); } 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); }
static int aplaypop_open(void) { int err; snd_pcm_t *handle; if (pcm_handle) return 0; snd_pcm_info_t *info; snd_pcm_info_alloca(&info); snd_output_t *log; err = snd_output_stdio_attach(&log, stderr, 0); assert(err == 0); err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0); if (err != 0) { fprintf(stderr, "snd_pcm_open(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_nonblock(handle, 0); if (err != 0) { fprintf(stderr, "snd_pcm_nonblock(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_info(handle, info); if (err != 0) { fprintf(stderr, "snd_pcm_info(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } // DOESN'T WORK! err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, CHANNELS, RATE, 1, 50000); if (err != 0) { fprintf(stderr, "snd_pcm_set_params(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } // RIGHT WAY: snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; unsigned int channels = CHANNELS; unsigned int rate = RATE; snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(handle, hwparams); if (err != 0) { fprintf(stderr, "Broken configuration for this PCM: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (err != 0) { fprintf(stderr, "snd_pcm_hw_params_set_access(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_format(handle, hwparams, format); if (err != 0) { fprintf(stderr, "snd_pcm_hw_params_set_format(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_channels(handle, hwparams, channels); if (err != 0) { fprintf(stderr, "snd_pcm_hw_params_set_channels(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, 0); if (err != 0) { fprintf(stderr, "snd_pcm_hw_params_set_rate_near(): %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } /* unsigned buffer_time = 0; snd_pcm_uframes_t buffer_frames = 0; if (buffer_time == 0 && buffer_frames == 0) { err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0); assert(err == 0); if (buffer_time > 500000) buffer_time = 500000; } unsigned period_time = 0; snd_pcm_uframes_t period_frames = 0; 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, hwparams, &period_time, 0); else err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_frames, 0); assert(err == 0); if (buffer_time > 0) err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0); else err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_frames); assert(err == 0); int monotonic = snd_pcm_hw_params_is_monotonic(hwparams); int can_pause = snd_pcm_hw_params_can_pause(hwparams); */ err = snd_pcm_hw_params(handle, hwparams); if (err != 0) { fprintf(stderr, "snd_pcm_hw_params(): %s\n", snd_strerror(err)); snd_pcm_hw_params_dump(hwparams, log); exit(EXIT_FAILURE); } snd_pcm_uframes_t chunk_size = 0; snd_pcm_hw_params_get_period_size(hwparams, &chunk_size, 0); snd_pcm_uframes_t buffer_size; snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); if (chunk_size == buffer_size) { fprintf(stderr, "Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size); exit(EXIT_FAILURE); } snd_pcm_sw_params_current(handle, swparams); err = snd_pcm_sw_params_set_avail_min(handle, swparams, chunk_size); assert(err == 0); /* round up to closest transfer boundary */ int start_delay = 0; snd_pcm_uframes_t start_threshold; if (start_delay <= 0) start_threshold = buffer_size + (double) rate * start_delay / 1000000; else start_threshold = (double) rate * start_delay / 1000000; start_threshold = start_threshold < 1 ? 1 : start_threshold > buffer_size ? buffer_size : start_threshold; err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); assert(err == 0); int stop_delay = 0; snd_pcm_uframes_t stop_threshold; 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); err = snd_pcm_sw_params(handle, swparams); if (err != 0) { fprintf(stderr, "snd_pcm_sw_params(): %s\n", snd_strerror(err)); snd_pcm_sw_params_dump(swparams, log); exit(EXIT_FAILURE); } // END OF THE RIGHT WAY // snd_pcm_dump(handle, log); size_t bits_per_sample = snd_pcm_format_physical_width(format); size_t bits_per_frame = bits_per_sample * channels; size_t chunk_bytes = chunk_size * bits_per_frame / 8; //audiobuf = realloc(audiobuf, chunk_bytes); fprintf(stderr, "%s: %s, Rate %d Hz, Channels=%u\n", snd_pcm_format_name(format), snd_pcm_format_description(format), rate, channels); fprintf(stderr, " bits_per_sample=%u, bits_per_frame=%u, chunk_bytes=%u\n", bits_per_sample, bits_per_frame, chunk_bytes); frame_bytes = bits_per_frame / 8; pcm_handle = handle; return 0; }
int ga_alsa_set_param(struct Xcap_alsa_param *param) { snd_pcm_hw_params_t *hwparams = NULL; snd_pcm_sw_params_t *swparams = NULL; size_t bits_per_sample; unsigned int rate; unsigned int buffer_time = 500000; // in the unit of microsecond unsigned int period_time = 125000; // = buffer_time/4; int monotonic = 0; snd_pcm_uframes_t start_threshold, stop_threshold; int err; // snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); if((err = snd_pcm_hw_params_any(param->handle, hwparams)) < 0) { ga_error("ALSA: set_param - no configurations available\n"); return -1; } if((err = snd_pcm_hw_params_set_access(param->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { ga_error("ALSA: set_param - access type (interleaved) not available\n"); return -1; } if((err = snd_pcm_hw_params_set_format(param->handle, hwparams, param->format)) < 0) { ga_error("ALSA: set_param - unsupported sample format.\n"); return -1; } if((err = snd_pcm_hw_params_set_channels(param->handle, hwparams, param->channels)) < 0) { ga_error("ALSA: set_param - channles count not available\n"); return -1; } rate = param->samplerate; if((err = snd_pcm_hw_params_set_rate_near(param->handle, hwparams, &rate, 0)) < 0) { ga_error("ALSA: set_param - set rate failed.\n"); return -1; } if((double)param->samplerate*1.05 < rate || (double)param->samplerate*0.95 > rate) { ga_error("ALSA: set_param/warning - inaccurate rate (req=%iHz, got=%iHz)\n", param->samplerate, rate); } // period_time = buffer_time/4; if((err = snd_pcm_hw_params_set_period_time_near(param->handle, hwparams, &period_time, 0)) < 0) { ga_error("ALSA: set_param - set period time failed.\n"); return -1; } if((err = snd_pcm_hw_params_set_buffer_time_near(param->handle, hwparams, &buffer_time, 0)) < 0) { ga_error("ALSA: set_param - set buffer time failed.\n"); return -1; } // monotonic = snd_pcm_hw_params_is_monotonic(hwparams); if((err = snd_pcm_hw_params(param->handle, hwparams)) < 0) { ga_error("ALSA: set_param - unable to install hw params:"); snd_pcm_hw_params_dump(hwparams, sndlog); return -1; } snd_pcm_hw_params_get_period_size(hwparams, ¶m->chunk_size, 0); snd_pcm_hw_params_get_buffer_size(hwparams, ¶m->buffer_size); if(param->chunk_size == param->buffer_size) { ga_error("ALSA: set_param - cannot use period equal to buffer size (%lu==%lu)\n", param->chunk_size, param->buffer_size); return -1; } // snd_pcm_sw_params_current(param->handle, swparams); err = snd_pcm_sw_params_set_avail_min(param->handle, swparams, param->chunk_size); // start_delay = 1 for capture start_threshold = (double) param->samplerate * /*start_delay=*/ 1 / 1000000; if(start_threshold < 1) start_threshold = 1; if(start_threshold > param->buffer_size) start_threshold = param->buffer_size; if((err = snd_pcm_sw_params_set_start_threshold(param->handle, swparams, start_threshold)) < 0) { ga_error("ALSA: set_param - set start threshold failed.\n"); return -1; } // stop_delay = 0 stop_threshold = param->buffer_size; if((err = snd_pcm_sw_params_set_stop_threshold(param->handle, swparams, stop_threshold)) < 0) { ga_error("ALSA: set_param - set stop threshold failed.\n"); return -1; } // if(snd_pcm_sw_params(param->handle, swparams) < 0) { ga_error("ALSA: set_param - unable to install sw params:"); snd_pcm_sw_params_dump(swparams, sndlog); return -1; } bits_per_sample = snd_pcm_format_physical_width(param->format); if(param->bits_per_sample != bits_per_sample) { ga_error("ALSA: set_param - BPS/HW configuration mismatched %d != %d)\n", param->bits_per_sample, bits_per_sample); } param->bits_per_frame = param->bits_per_sample * param->channels; param->chunk_bytes = param->chunk_size * param->bits_per_frame / 8; return 0; }
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 */ }
bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize) { if (handle == nullptr) return false; JUCE_ALSA_LOG ("ALSADevice::setParameters(" << deviceID << ", " << (int) sampleRate << ", " << numChannels << ", " << bufferSize << ")"); snd_pcm_hw_params_t* hwParams; snd_pcm_hw_params_alloca (&hwParams); if (snd_pcm_hw_params_any (handle, hwParams) < 0) { // this is the error message that aplay returns when an error happens here, // it is a bit more explicit that "Invalid parameter" error = "Broken configuration for this PCM: no configurations available"; return false; } if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0) // works better for plughw.. isInterleaved = true; else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0) isInterleaved = false; else { jassertfalse; return false; } enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17, onlyUseLower24Bits = 1 << 18 }; const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32 | isFloatBit | isLittleEndianBit, SND_PCM_FORMAT_FLOAT_BE, 32 | isFloatBit, SND_PCM_FORMAT_S32_LE, 32 | isLittleEndianBit, SND_PCM_FORMAT_S32_BE, 32, SND_PCM_FORMAT_S24_3LE, 24 | isLittleEndianBit, SND_PCM_FORMAT_S24_3BE, 24, SND_PCM_FORMAT_S24_LE, 32 | isLittleEndianBit | onlyUseLower24Bits, SND_PCM_FORMAT_S16_LE, 16 | isLittleEndianBit, SND_PCM_FORMAT_S16_BE, 16 }; bitDepth = 0; for (int i = 0; i < numElementsInArray (formatsToTry); i += 2) { if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0) { const int type = formatsToTry [i + 1]; bitDepth = type & 255; converter.reset (createConverter (isInput, bitDepth, (type & isFloatBit) != 0, (type & isLittleEndianBit) != 0, (type & onlyUseLower24Bits) != 0, numChannels, isInterleaved)); break; } } if (bitDepth == 0) { error = "device doesn't support a compatible PCM format"; JUCE_ALSA_LOG ("Error: " + error); return false; } int dir = 0; unsigned int periods = 4; snd_pcm_uframes_t samplesPerPeriod = (snd_pcm_uframes_t) bufferSize; if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, (unsigned int ) numChannels)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params (handle, hwParams))) { return false; } snd_pcm_uframes_t frames = 0; if (JUCE_ALSA_FAILED (snd_pcm_hw_params_get_period_size (hwParams, &frames, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) latency = 0; else latency = (int) frames * ((int) periods - 1); // (this is the method JACK uses to guess the latency..) JUCE_ALSA_LOG ("frames: " << (int) frames << ", periods: " << (int) periods << ", samplesPerPeriod: " << (int) samplesPerPeriod); snd_pcm_sw_params_t* swParams; snd_pcm_sw_params_alloca (&swParams); snd_pcm_uframes_t boundary; if (JUCE_ALSA_FAILED (snd_pcm_sw_params_current (handle, swParams)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_get_boundary (swParams, &boundary)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_size (handle, swParams, boundary)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_stop_threshold (handle, swParams, boundary)) || JUCE_ALSA_FAILED (snd_pcm_sw_params (handle, swParams))) { return false; } #if JUCE_ALSA_LOGGING // enable this to dump the config of the devices that get opened snd_output_t* out; snd_output_stdio_attach (&out, stderr, 0); snd_pcm_hw_params_dump (hwParams, out); snd_pcm_sw_params_dump (swParams, out); #endif numChannelsRunning = numChannels; return true; }
int ai_alsa_setup(audio_in_t *ai) { snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; int buffer_size; int err; unsigned int rate; snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(ai->alsa.handle, params); if (err < 0) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_PcmBrokenConfig); return -1; } err = snd_pcm_hw_params_set_access(ai->alsa.handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_UnavailableAccessType); return -1; } err = snd_pcm_hw_params_set_format(ai->alsa.handle, params, SND_PCM_FORMAT_S16_LE); if (err < 0) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_UnavailableSampleFmt); return -1; } err = snd_pcm_hw_params_set_channels(ai->alsa.handle, params, ai->req_channels); if (err < 0) { ai->channels = snd_pcm_hw_params_get_channels(params); mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_UnavailableChanCount, ai->channels); } else { ai->channels = ai->req_channels; } err = snd_pcm_hw_params_set_rate_near(ai->alsa.handle, params, ai->req_samplerate, 0); assert(err >= 0); rate = err; ai->samplerate = rate; ai->alsa.buffer_time = 1000000; ai->alsa.buffer_time = snd_pcm_hw_params_set_buffer_time_near(ai->alsa.handle, params, ai->alsa.buffer_time, 0); assert(ai->alsa.buffer_time >= 0); ai->alsa.period_time = ai->alsa.buffer_time / 4; ai->alsa.period_time = snd_pcm_hw_params_set_period_time_near(ai->alsa.handle, params, ai->alsa.period_time, 0); assert(ai->alsa.period_time >= 0); err = snd_pcm_hw_params(ai->alsa.handle, params); if (err < 0) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_CannotInstallHWParams); snd_pcm_hw_params_dump(params, ai->alsa.log); return -1; } ai->alsa.chunk_size = snd_pcm_hw_params_get_period_size(params, 0); buffer_size = snd_pcm_hw_params_get_buffer_size(params); if (ai->alsa.chunk_size == buffer_size) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_PeriodEqualsBufferSize, ai->alsa.chunk_size, (long)buffer_size); return -1; } snd_pcm_sw_params_current(ai->alsa.handle, swparams); err = snd_pcm_sw_params_set_sleep_min(ai->alsa.handle, swparams,0); assert(err >= 0); err = snd_pcm_sw_params_set_avail_min(ai->alsa.handle, swparams, ai->alsa.chunk_size); assert(err >= 0); err = snd_pcm_sw_params_set_start_threshold(ai->alsa.handle, swparams, 0); assert(err >= 0); err = snd_pcm_sw_params_set_stop_threshold(ai->alsa.handle, swparams, buffer_size); assert(err >= 0); assert(err >= 0); if (snd_pcm_sw_params(ai->alsa.handle, swparams) < 0) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_CannotInstallSWParams); snd_pcm_sw_params_dump(swparams, ai->alsa.log); return -1; } if (mp_msg_test(MSGT_TV, MSGL_V)) { snd_pcm_dump(ai->alsa.handle, ai->alsa.log); } ai->alsa.bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE); ai->alsa.bits_per_frame = ai->alsa.bits_per_sample * ai->channels; ai->blocksize = ai->alsa.chunk_size * ai->alsa.bits_per_frame / 8; ai->samplesize = ai->alsa.bits_per_sample; ai->bytes_per_sample = ai->alsa.bits_per_sample/8; return 0; }
bool AlsaSource::SetupHW () { bool result = false; bool rw_available = false; bool mmap_available = false; #if DEBUG bool debug = debug_flags & RUNTIME_DEBUG_AUDIO; #else bool debug = false; #endif snd_pcm_hw_params_t *params = NULL; snd_output_t *output = NULL; guint32 buffer_time = 100000; // request 0.1 seconds of buffer time. int err = 0; int dir = 0; unsigned int rate = GetSampleRate (); unsigned int actual_rate = rate; guint32 channels = GetChannels (); if (debug) { err = snd_output_stdio_attach (&output, stdout, 0); if (err < 0) LOG_AUDIO ("AlsaSource::SetupHW (): Could not create alsa output: %s\n", snd_strerror (err)); } err = snd_pcm_hw_params_malloc (¶ms); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (malloc): %s\n", snd_strerror (err)); return false; } // choose all parameters err = snd_pcm_hw_params_any (pcm, params); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (no configurations available): %s\n", snd_strerror (err)); goto cleanup; } if (debug && output != NULL) { LOG_AUDIO ("AlsaSource::SetupHW (): hw configurations:\n"); snd_pcm_hw_params_dump (params, output); } // enable software resampling err = snd_pcm_hw_params_set_rate_resample (pcm, params, 1); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (could not enable resampling): %s\n", snd_strerror (err)); goto cleanup; } // test for available transfer modes if (!(moonlight_flags & RUNTIME_INIT_AUDIO_ALSA_MMAP)) { err = snd_pcm_hw_params_test_access (pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup: RW access mode not supported (%s).\n", snd_strerror (err)); } else { rw_available = true; } } if (!(moonlight_flags & RUNTIME_INIT_AUDIO_ALSA_RW)) { err = snd_pcm_hw_params_test_access (pcm, params, SND_PCM_ACCESS_MMAP_INTERLEAVED); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup: MMAP access mode not supported (%s).\n", snd_strerror (err)); } else { mmap_available = true; } } if (mmap_available) { mmap = true; } else if (rw_available) { mmap = false; } else { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed, no available access mode\n"); goto cleanup; } LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup: using %s access mode.\n", mmap ? "MMAP" : "RW"); // set transfer mode (mmap or rw in our case) err = snd_pcm_hw_params_set_access (pcm, params, mmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED : SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (access type not available for playback): %s\n", snd_strerror (err)); goto cleanup; } // set audio format switch (GetInputBytesPerSample ()) { case 1: // 8 bit audio err = snd_pcm_hw_params_set_format (pcm, params, SND_PCM_FORMAT_S16); SetOutputBytesPerSample (2); break; case 2: // 16 bit audio err = snd_pcm_hw_params_set_format (pcm, params, SND_PCM_FORMAT_S16); SetOutputBytesPerSample (2); break; case 3: // 24 bit audio // write as 32 bit audio, this is a lot easier to write to than 24 bit. err = snd_pcm_hw_params_set_format (pcm, params, SND_PCM_FORMAT_S32); SetOutputBytesPerSample (4); break; default: LOG_AUDIO ("AlsaSource::SetupHW (): Invalid input bytes per sample, expected 1, 2 or 3, got %i\n", GetInputBytesPerSample ()); goto cleanup; } if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (sample format not available for playback): %s\n", snd_strerror (err)); goto cleanup; } // set channel count err = snd_pcm_hw_params_set_channels (pcm, params, channels); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (channels count %i not available for playback): %s\n", channels, snd_strerror (err)); goto cleanup; } // set sample rate err = snd_pcm_hw_params_set_rate_near (pcm, params, &actual_rate, 0); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (sample rate %i Hz not available for playback): %s\n", rate, snd_strerror (err)); goto cleanup; } else if (actual_rate != rate) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (sample rate %i Hz not available for playback, only got %i Hz).\n", rate, actual_rate); goto cleanup; } // set the buffer time err = snd_pcm_hw_params_set_buffer_time_near (pcm, params, &buffer_time, &dir); if (err < 0) { LOG_AUDIO ("AudioNode::SetupHW (): Audio HW setup failed (unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror (err)); goto cleanup; } // write the parameters to device err = snd_pcm_hw_params (pcm, params); if (err < 0) { LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (unable to set hw params for playback: %s)\n", snd_strerror (err)); if (debug && output != NULL) { LOG_AUDIO ("AlsaSource::SetupHW (): current hw configurations:\n"); snd_pcm_hw_params_dump (params, output); } goto cleanup; } if (debug) { LOG_AUDIO ("AlsaSource::SetupHW (): hardware pause support: %s\n", snd_pcm_hw_params_can_pause (params) == 0 ? "no" : "yes"); LOG_AUDIO ("AlsaSource::SetupHW (): succeeded\n"); if (output != NULL) snd_pcm_hw_params_dump (params, output); } result = true; cleanup: snd_pcm_hw_params_free (params); return result; }
static size_t 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(AHandle, params); if (err < 0) { fprintf(stderr, "Broken configuration for this PCM: no configurations available"); exit (1); } err = snd_pcm_hw_params_set_access(AHandle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { fprintf(stderr, "Access type not available"); exit(1); } err = snd_pcm_hw_params_set_format(AHandle, params, DEFAULT_FORMAT); if (err < 0) { fprintf(stderr, "Sample format non available"); exit(1); } err = snd_pcm_hw_params_set_channels(AHandle, params, CHANNELS); if (err < 0) { fprintf(stderr, "Channels count non available"); exit(1); } rate = DEFAULT_SPEED; hwparams.rate=DEFAULT_SPEED; hwparams.format=DEFAULT_FORMAT; hwparams.channels=CHANNELS; err = snd_pcm_hw_params_set_rate_near(AHandle, params, &hwparams.rate, 0); assert(err >= 0); 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(AHandle, params, &period_time, 0); else err = snd_pcm_hw_params_set_period_size_near(AHandle, params, &period_frames, 0); assert(err >= 0); if (buffer_time > 0) { err = snd_pcm_hw_params_set_buffer_time_near(AHandle, params, &buffer_time, 0); } else { err = snd_pcm_hw_params_set_buffer_size_near(AHandle, params, &buffer_frames); } assert(err >= 0); monotonic = snd_pcm_hw_params_is_monotonic(params); can_pause = snd_pcm_hw_params_can_pause(params); err = snd_pcm_hw_params(AHandle, params); if (err < 0) { fprintf(stderr, "Unable to install hw params:"); snd_pcm_hw_params_dump(params, Log); exit(1); } 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) { fprintf(stderr, "Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size); exit(1); } snd_pcm_sw_params_current(AHandle, swparams); if (avail_min < 0) n = chunk_size; else n = (double) rate * avail_min / 1000000; err = snd_pcm_sw_params_set_avail_min(AHandle, swparams, 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(AHandle, 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(AHandle, swparams, stop_threshold); assert(err >= 0); if (snd_pcm_sw_params(AHandle, swparams) < 0) { fprintf(stderr, "unable to install sw params:"); snd_pcm_sw_params_dump(swparams, Log); exit(1); } snd_pcm_dump(AHandle, Log); bits_per_sample = snd_pcm_format_physical_width(DEFAULT_FORMAT); bits_per_frame = bits_per_sample * hwparams.channels; chunk_bytes = chunk_size * bits_per_frame / 8; buffer_frames = buffer_size; /* for position test */ return chunk_bytes; }
static int set_params(AudioDevice *ad, AudioFormat *format_p, int *ch_p, unsigned int *rate_p) { ALSA_data *alsa = (ALSA_data *)ad->private_data; int r, err; snd_pcm_format_t f; AudioFormat format = *format_p; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; int ch = *ch_p; unsigned int rate = *rate_p; unsigned int buffer, period; snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); ad->format = _AUDIO_FORMAT_UNSET; *format_p = _AUDIO_FORMAT_UNSET; *ch_p = 0; *rate_p = 0; /* format part */ switch (format) { case _AUDIO_FORMAT_MU_LAW: f = SND_PCM_FORMAT_MU_LAW; break; case _AUDIO_FORMAT_A_LAW: f = SND_PCM_FORMAT_A_LAW; break; case _AUDIO_FORMAT_ADPCM: f = SND_PCM_FORMAT_IMA_ADPCM; break; case _AUDIO_FORMAT_U8: f = SND_PCM_FORMAT_U8; break; case _AUDIO_FORMAT_S8: f = SND_PCM_FORMAT_S8; break; case _AUDIO_FORMAT_U16_LE: f = SND_PCM_FORMAT_U16_LE; break; case _AUDIO_FORMAT_U16_BE: f = SND_PCM_FORMAT_U16_BE; break; case _AUDIO_FORMAT_S16_LE: f = SND_PCM_FORMAT_S16_LE; break; case _AUDIO_FORMAT_S16_BE: f = SND_PCM_FORMAT_S16_BE; break; #if 0 case _AUDIO_FORMAT_S32_LE: f = SND_PCM_FORMAT_U32_LE; break; case _AUDIO_FORMAT_S32_BE: f = SND_PCM_FORMAT_S32_BE; break; #endif default: show_message_fnc("format %d is invalid.\n", format); return 0; } if ((err = snd_pcm_hw_params_any(alsa->fd, hwparams)) < 0) { show_message_fnc("snd_pcm_hw_params_any() failed.\n"); return 0; } if ((err = snd_pcm_hw_params_set_access(alsa->fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { show_message_fnc("snd_pcm_hw_params_set_access() failed.\n"); return 0; } if ((err = snd_pcm_hw_params_set_format(alsa->fd, hwparams, f)) < 0) { show_message_fnc("snd_pcm_hw_params_set_format() failed.\n"); return 0; } ad->format = format; *format_p = format; /* channel part */ if ((err = snd_pcm_hw_params_set_channels(alsa->fd, hwparams, ch)) < 0) { show_message_fnc("snd_pcm_hw_params_set_channels() failed.\n"); return 0; } *ch_p = ch; /* rate part */ if ((err = snd_pcm_hw_params_set_rate_near(alsa->fd, hwparams, &rate, 0)) < 0) { show_message_fnc("snd_pcm_hw_params_set_rate_near() failed.\n"); return 0; } *rate_p = rate; /* buffer & period */ buffer = 500000; period = 500000 / 4; if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa->fd, hwparams, &buffer, 0)) < 0) { show_message_fnc("snd_pcm_hw_params_set_buffer_near() failed.\n"); return 0; } r = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa->buffer_size); if ((err = snd_pcm_hw_params_set_period_time_near(alsa->fd, hwparams, &period, 0)) < 0) { show_message_fnc("snd_pcm_hw_params_set_period_near() failed.\n"); return 0; } r = snd_pcm_hw_params_get_period_size(hwparams, &alsa->period_size, 0); if ((err = snd_pcm_hw_params(alsa->fd, hwparams)) < 0) { perror("snd_pcm_hw_params"); err_message_fnc("snd_pcm_hw_params() failed.\n"); snd_pcm_hw_params_dump(hwparams, alsa->log); return 0; } r = snd_pcm_hw_params_get_channels(hwparams, &ad->channels); r = snd_pcm_hw_params_get_rate(hwparams, &ad->speed, 0); r = r; // dummy #ifdef DEBUG { snd_pcm_format_t form; debug_message_fnc("format "); r = snd_pcm_hw_params_get_format(hwparams, &form); switch (form) { case SND_PCM_FORMAT_MU_LAW: debug_message("MU_LAW "); break; case SND_PCM_FORMAT_A_LAW: debug_message("A_LAW "); break; case SND_PCM_FORMAT_IMA_ADPCM: debug_message("ADPCM "); break; case SND_PCM_FORMAT_U8: debug_message("U8 "); break; case SND_PCM_FORMAT_S8: debug_message("S8 "); break; case SND_PCM_FORMAT_U16_LE: debug_message("U16LE "); break; case SND_PCM_FORMAT_U16_BE: debug_message("U16BE "); break; case SND_PCM_FORMAT_S16_LE: debug_message("S16LE "); break; case SND_PCM_FORMAT_S16_BE: debug_message("S16BE "); break; #if 0 case SND_PCM_FORMAT_U32_LE: debug_message("U32LE "); break; case SND_PCM_FORMAT_S32_BE: debug_message("S32BE "); break; #endif default: debug_message("UNKNOWN "); break; } debug_message("%d ch %d Hz buffer %ld period %ld OK\n", ad->channels, ad->speed, alsa->buffer_size, alsa->period_size); } #endif /* sw_params */ if ((err = snd_pcm_sw_params_current(alsa->fd, swparams)) < 0) { show_message_fnc("snd_pcm_sw_params_any() failed.\n"); return 0; } if ((err = snd_pcm_sw_params(alsa->fd, swparams)) < 0) { show_message_fnc("snd_pcm_sw_params() failed.\n"); return 0; } debug_message_fnc("1 sample -> %ld bytes\n", snd_pcm_samples_to_bytes(alsa->fd, 1)); return 1; }
bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize) { if (handle == 0) return false; snd_pcm_hw_params_t* hwParams; snd_pcm_hw_params_alloca (&hwParams); if (failed (snd_pcm_hw_params_any (handle, hwParams))) return false; if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0) isInterleaved = false; else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0) isInterleaved = true; else { jassertfalse; return false; } enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17 }; const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32 | isFloatBit | isLittleEndianBit, SND_PCM_FORMAT_FLOAT_BE, 32 | isFloatBit, SND_PCM_FORMAT_S32_LE, 32 | isLittleEndianBit, SND_PCM_FORMAT_S32_BE, 32, SND_PCM_FORMAT_S24_3LE, 24 | isLittleEndianBit, SND_PCM_FORMAT_S24_3BE, 24, SND_PCM_FORMAT_S16_LE, 16 | isLittleEndianBit, SND_PCM_FORMAT_S16_BE, 16 }; bitDepth = 0; for (int i = 0; i < numElementsInArray (formatsToTry); i += 2) { if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0) { bitDepth = formatsToTry [i + 1] & 255; const bool isFloat = (formatsToTry [i + 1] & isFloatBit) != 0; const bool isLittleEndian = (formatsToTry [i + 1] & isLittleEndianBit) != 0; converter = createConverter (isInput, bitDepth, isFloat, isLittleEndian, numChannels); break; } } if (bitDepth == 0) { error = "device doesn't support a compatible PCM format"; DBG ("ALSA error: " + error + "\n"); return false; } int dir = 0; unsigned int periods = 4; snd_pcm_uframes_t samplesPerPeriod = bufferSize; if (failed (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) || failed (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels)) || failed (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) || failed (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) || failed (snd_pcm_hw_params (handle, hwParams))) { return false; } snd_pcm_uframes_t frames = 0; if (failed (snd_pcm_hw_params_get_period_size (hwParams, &frames, &dir)) || failed (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) latency = 0; else latency = frames * (periods - 1); // (this is the method JACK uses to guess the latency..) snd_pcm_sw_params_t* swParams; snd_pcm_sw_params_alloca (&swParams); snd_pcm_uframes_t boundary; if (failed (snd_pcm_sw_params_current (handle, swParams)) || failed (snd_pcm_sw_params_get_boundary (swParams, &boundary)) || failed (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0)) || failed (snd_pcm_sw_params_set_silence_size (handle, swParams, boundary)) || failed (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod)) || failed (snd_pcm_sw_params_set_stop_threshold (handle, swParams, boundary)) || failed (snd_pcm_sw_params (handle, swParams))) { return false; } #if 0 // enable this to dump the config of the devices that get opened snd_output_t* out; snd_output_stdio_attach (&out, stderr, 0); snd_pcm_hw_params_dump (hwParams, out); snd_pcm_sw_params_dump (swParams, out); #endif numChannelsRunning = numChannels; return true; }
AudioStream* AlsaBackend::OpenStream(StreamDirection dir, DeviceInfo* device, StreamSpec& spec, SOUNDCARD_CALLBACK callback, void* userdata) { if (!device->probed) if (!ProbeDevice(device, spec)) return NULL; if (!device->mixer) { Log("Device was probed but no mixer set, trying to enable now."); device->mixer = new AlsaVolumeControl; static_cast<AlsaVolumeControl*>(device->mixer)->ProbeMixer(device); } snd_pcm_stream_t stream; if (dir == INPUT) stream = SND_PCM_STREAM_CAPTURE; else if (dir >= OUTPUT) stream = SND_PCM_STREAM_PLAYBACK; else { return NULL; } AudioStream *audioStream = new AudioStream; MutexLock lock(audioStream->mutex); Log("AlsaBackend.open_stream_lock"); //int result; snd_pcm_t *phandle = 0; int openMode = SND_PCM_ASYNC; snd_output_t *out; _EE_CALL(snd_pcm_open(&phandle, device->guid, stream, openMode), false, phandle, "pcm device %s won't open for %s", device->displayName, dir==INPUT?"input":"output"); // Fill the parameter structure. AlsaHardwareParams alsa_hw_params(phandle); #define DUMP_PARAMS(out, title, func) \ if (!snd_output_buffer_open(&out)) \ { \ char* str; \ Log(title); \ func; \ snd_output_putc(out, '\0'); \ if (snd_output_buffer_string(out, &str) > 0) \ Log("%s",str); \ snd_output_close(out); \ } DUMP_PARAMS(out, "AlsaBackend: dump hardware params just after device open:", snd_pcm_hw_params_dump(alsa_hw_params, out)); // Set up interleaving audioStream->userInterleaved = true; audioStream->deviceInterleaved = true; bool use_mmap = false; alsa_hw_params.set_params(&spec, &use_mmap); audioStream->deviceFormat = audioStream->userFormat = spec.format; audioStream->deviceChannels = audioStream->userChannels = spec.channels; DUMP_PARAMS(out, "AlsaBackend: dump hardware params after installation:", snd_pcm_hw_params_dump(alsa_hw_params, out)); AlsaSoftwareParams sw_params(phandle); sw_params.set_params(spec.fragmentFrames, true); DUMP_PARAMS(out, "AlsaBackend: dump software params after installation:", snd_pcm_sw_params_dump(sw_params, out)); // Allocate the ApiHandle if necessary and then save. AlsaHandle *apiInfo = 0; if (audioStream->apiHandle == 0) { apiInfo = new AlsaHandle; if (!apiInfo) { Log("error allocating AlsaHandle memory"); delete audioStream; return NULL; } audioStream->apiHandle = (void *) apiInfo; apiInfo->handle = 0; } else { apiInfo = (AlsaHandle *) audioStream->apiHandle; } apiInfo->handle = phandle; apiInfo->xrun = false; apiInfo->type = device->type; audioStream->spec = spec; audioStream->running = 0; audioStream->dir = dir; audioStream->mixer = device->mixer; // Setup callback thread. audioStream->callback.object = this; audioStream->callback.stream = audioStream; audioStream->callback.callback = callback; audioStream->callback.userdata = userdata; audioStream->callback.apiInfo = apiInfo; audioStream->callback.isRunning = 1; // Allocate necessary internal buffer. audioStream->buffer = new PhantomRingbuffer(spec.FrameBytes(), spec.bufferFrames, spec.fragmentFrames); if (audioStream->buffer == NULL) { Log("error allocating ringbuffer memory"); if (apiInfo) { if (apiInfo->handle) snd_pcm_close(apiInfo->handle); delete apiInfo; apiInfo = NULL; audioStream->apiHandle = 0; } delete audioStream; return NULL; } p_alsathread[audioStream] = new AlsaThread(&audioStream->callback, dir == INPUT ? "alsa_capture_thread" : "alsa_playback_thread"); pthread_create(&thrd,NULL,&(AlsaThread::run),p_alsathread[audioStream]); pthread_detach(thrd); Log("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)", (double) (spec.bufferFrames * spec.FrameBytes()) / (double) (spec.fragmentFrames * spec.FrameBytes()), (long unsigned) spec.fragmentFrames * spec.FrameBytes(), spec.FragmentMs(), (long unsigned) spec.bufferFrames * spec.FrameBytes(), spec.BufferMs()); return audioStream; }