/* * Copyright (C) 2000-2004 James Courtier-Dutton * Copyright (C) 2005 Nathan Hurst */ #include #include #include #include #include #include #include #include #include #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API #include #include #include #include "main.h" #include "errors.h" static void generate_sine(uint8_t *frames, int count, double *_phase, double _freq, unsigned int _rate, unsigned int _channels, bool _enable) { double phase = *_phase; double max_phase = 1.0 / _freq; double step = 1.0 / (double)_rate; double res = 0.0; unsigned int chn; int32_t ires; int16_t *samp16 = (int16_t*) frames; while (count-- > 0) { for(chn = 0; chn < _channels; chn++) { // SND_PCM_FORMAT_S16_LE: if(_enable) { res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0x03fffffff; /* Don't use MAX volume */ } ires = res; *samp16++ = ires >> 16; } phase += step; if (phase >= max_phase) phase -= max_phase; } *_phase = phase; } static int set_hwparams( snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, snd_pcm_format_t format, unsigned int rate, unsigned int channels, snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t *period_size) { unsigned int rrate; int err; snd_pcm_uframes_t period_size_min; snd_pcm_uframes_t period_size_max; snd_pcm_uframes_t buffer_size_min; snd_pcm_uframes_t buffer_size_max; unsigned int period_time = 0; /* period time in us */ unsigned int buffer_time = 0; /* ring buffer length in us */ unsigned int nperiods = 4; /* number of periods */ /* choose all parameters */ err = snd_pcm_hw_params_any(handle, params); if (err < 0) { fprintf(stderr, "Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); return err; } /* set the interleaved read/write format */ err = snd_pcm_hw_params_set_access(handle, params, access); if (err < 0) { fprintf(stderr, "Access type not available for playback: %s\n", snd_strerror(err)); return err; } /* set the sample format */ err = snd_pcm_hw_params_set_format(handle, params, format); if (err < 0) { fprintf(stderr, "Sample format not available for playback: %s\n", snd_strerror(err)); return err; } /* set the count of channels */ err = snd_pcm_hw_params_set_channels(handle, params, channels); if (err < 0) { fprintf(stderr, "Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); return err; } /* set the stream rate */ rrate = rate; err = snd_pcm_hw_params_set_rate(handle, params, rate, 0); if (err < 0) { fprintf(stderr, "Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); return err; } if (rrate != rate) { fprintf(stderr, "Rate doesn't match (requested %iHz, get %iHz, err %d)\n", rate, rrate, err); return -EINVAL; } //printf(_("Rate set to %iHz (requested %iHz)\n"), rrate, rate); /* set the buffer time */ err = snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min); err = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max); err = snd_pcm_hw_params_get_period_size_min(params, &period_size_min, NULL); err = snd_pcm_hw_params_get_period_size_max(params, &period_size_max, NULL); //printf(_("Buffer size range from %lu to %lu\n"),buffer_size_min, buffer_size_max); //printf(_("Period size range from %lu to %lu\n"),period_size_min, period_size_max); if (period_time > 0) { //printf(_("Requested period time %u us\n"), period_time); err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, NULL); if (err < 0) { fprintf(stderr, "Unable to set period time %u us for playback: %s\n", period_time, snd_strerror(err)); return err; } } if (buffer_time > 0) { //printf(_("Requested buffer time %u us\n"), buffer_time); err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, NULL); if (err < 0) { fprintf(stderr, "Unable to set buffer time %u us for playback: %s\n", buffer_time, snd_strerror(err)); return err; } } if (! buffer_time && ! period_time) { //buffer_size = buffer_size_max; //buffer_size = buffer_size_min; *buffer_size = 2048; if (! period_time) *buffer_size = (*buffer_size / nperiods) * nperiods; //printf(_("Using max buffer size %lu\n"), buffer_size); //printf(_("Using min buffer size %lu\n"), *buffer_size); err = snd_pcm_hw_params_set_buffer_size_near(handle, params, buffer_size); if (err < 0) { fprintf(stderr, "Unable to set buffer size %lu for playback: %s\n", *buffer_size, snd_strerror(err)); return err; } } if (! buffer_time || ! period_time) { //printf(_("Periods = %u\n"), nperiods); err = snd_pcm_hw_params_set_periods_near(handle, params, &nperiods, NULL); if (err < 0) { fprintf(stderr, "Unable to set nperiods %u for playback: %s\n", nperiods, snd_strerror(err)); return err; } } /* write the parameters to device */ err = snd_pcm_hw_params(handle, params); if (err < 0) { fprintf(stderr, "Unable to set hw params for playback: %s\n", snd_strerror(err)); return err; } snd_pcm_hw_params_get_buffer_size(params, buffer_size); snd_pcm_hw_params_get_period_size(params, period_size, NULL); //printf(_("was set period_size = %lu\n"),*period_size); //printf(_("was set buffer_size = %lu\n"),*buffer_size); if (2*(*period_size) > *buffer_size) { fprintf(stderr, "buffer to small, could not use\n"); return -EINVAL; } return 0; } static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t *period_size) { int err; /* get the current swparams */ err = snd_pcm_sw_params_current(handle, swparams); if (err < 0) { fprintf(stderr, "Unable to determine current swparams for playback: %s\n", snd_strerror(err)); return err; } /* start the transfer when a buffer is full */ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, *buffer_size - *period_size); if (err < 0) { fprintf(stderr, "Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); return err; } /* allow the transfer when at least period_size frames can be processed */ err = snd_pcm_sw_params_set_avail_min(handle, swparams, *period_size); if (err < 0) { fprintf(stderr, "Unable to set avail min for playback: %s\n", snd_strerror(err)); return err; } /* write the parameters to the playback device */ err = snd_pcm_sw_params(handle, swparams); if (err < 0) { fprintf(stderr, "Unable to set sw params for playback: %s\n", snd_strerror(err)); return err; } return 0; } void *loop_beep(void *arg) { thread_vars_t *thread_vars = (thread_vars_t *)arg; uint8_t *err = &thread_vars->thread_err; while(*err == ERROR_NONE && *thread_vars->main_err_ptr == ERROR_NONE){ if(thread_vars->config->beep_enabled){ snd_pcm_t *handle; int error; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); char *device = "default"; /* playback device */ snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ unsigned int rate = 48000; /* stream rate */ unsigned int channels = 2; /* count of channels */ snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; if ((error = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { printf("Playback open error: %d,%s\n", error,snd_strerror(error)); *err = ERROR_THREAD_ERROR; return NULL; } if ((error = set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED, format, rate, channels, &buffer_size, &period_size)) < 0) { printf("Setting of hwparams failed: %s\n", snd_strerror(error)); snd_pcm_close(handle); *err = ERROR_THREAD_ERROR; return NULL; } if ((error = set_swparams(handle, swparams, &buffer_size, &period_size)) < 0) { printf("Setting of swparams failed: %s\n", snd_strerror(error)); snd_pcm_close(handle); *err = ERROR_THREAD_ERROR; return NULL; } uint8_t *frames; frames = malloc(snd_pcm_frames_to_bytes(handle, period_size)); if (frames == NULL) { fprintf(stderr, "No enough memory\n"); *err = ERROR_THREAD_ERROR; return NULL; } snd_pcm_prepare(handle); double freq = 400.0; double phase = 0.0; if(thread_vars->status->modulation_error_rate > 0 && thread_vars->status->modulation_error_rate <= 310) { freq = 700.0 * (exp((200+(10*thread_vars->status->modulation_error_rate))/1127.0)-1.0); } generate_sine(frames, period_size, &phase, freq, rate, channels, (thread_vars->status->state == STATE_DEMOD_S2)); /* Start playback */ snd_pcm_writei(handle, frames, period_size); snd_pcm_start(handle); snd_pcm_sframes_t avail; while(*err == ERROR_NONE && *thread_vars->main_err_ptr == ERROR_NONE && thread_vars->config->beep_enabled){ /* Wait until device is ready for more data */ if(snd_pcm_wait(handle, 100)) { avail = snd_pcm_avail_update(handle); if(avail < 0 && avail == -32) { /* Handle underrun */ snd_pcm_prepare(handle); } while (avail >= (snd_pcm_sframes_t)period_size) { if(thread_vars->status->modulation_error_rate > 0 && thread_vars->status->modulation_error_rate <= 310) { freq = 700.0 * (exp((200+(10*thread_vars->status->modulation_error_rate))/1127.0)-1.0); } generate_sine(frames, period_size, &phase, freq, rate, channels, (thread_vars->status->state == STATE_DEMOD_S2)); while (snd_pcm_writei(handle, frames, period_size) < 0) { /* Handle underrun */ snd_pcm_prepare(handle); } avail = snd_pcm_avail_update(handle); } } } /* Stop Playback */ snd_pcm_drop(handle); /* Empty buffer */ snd_pcm_drain(handle); free(frames); snd_pcm_close(handle); } usleep(10*1000); // 10ms } return NULL; }