/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; size_t samples; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, NULL)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } const char *channel = NULL; const int16_t *buf = NULL; if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_int16_t(uref, channel, 0, -1, &buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } if (unlikely((uintptr_t)buf & 1)) { upipe_warn(upipe, "unaligned buffer"); } ebur128_add_frames_short(upipe_filter_ebur128->st, buf, samples); uref_sound_plane_unmap(uref, channel, 0, -1); } ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; if (unlikely(upipe_filter_ebur128->output_flow == NULL)) { upipe_err_va(upipe, "invalid input"); uref_free(uref); return; } size_t samples; uint8_t sample_size; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, &sample_size)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } void *buf = NULL; const char *channel = NULL; if (upipe_filter_ebur128->planes == 1) { if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_void(uref, channel, 0, -1, (const void **)&buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } } else { buf = malloc(sample_size * upipe_filter_ebur128->channels * samples); if (buf == NULL) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); uref_free(uref); return; } if (!ubase_check(uref_sound_interleave(uref, (uint8_t *)buf, 0, samples, sample_size, upipe_filter_ebur128->planes))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } if (unlikely((uintptr_t)buf & 1)) upipe_warn(upipe, "unaligned buffer"); switch (upipe_filter_ebur128->fmt) { case UPIPE_FILTER_EBUR128_SHORT: ebur128_add_frames_short(upipe_filter_ebur128->st, (short *)buf, samples); break; case UPIPE_FILTER_EBUR128_INT: ebur128_add_frames_int(upipe_filter_ebur128->st, (int *)buf, samples); break; case UPIPE_FILTER_EBUR128_FLOAT: ebur128_add_frames_float(upipe_filter_ebur128->st, (float *)buf, samples); break; case UPIPE_FILTER_EBUR128_DOUBLE: ebur128_add_frames_double(upipe_filter_ebur128->st, (double *)buf, samples); break; default: upipe_warn_va(upipe, "unknown sample format %d", upipe_filter_ebur128->fmt); break; } if (upipe_filter_ebur128->planes == 1) uref_sound_plane_unmap(uref, channel, 0, -1); else free(buf); ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
void audio_trigger(context_t *context) { int input_fd = -1; static const pa_sample_spec ss = { .format = PA_SAMPLE_S16LE, .rate = 44100, .channels = 1 }; int error; daemon_log(LOG_INFO, "Trigger %s", context->shush_filename); if (context->points_threshold < 0) { daemon_log(LOG_INFO, "Threshold below 0, not triggering sound"); } daemon_log(LOG_INFO, "Opening %s for output", context->output_device ?: "default sink"); pa_simple *pa_output = pa_simple_new( NULL, "shusherd", PA_STREAM_PLAYBACK, context->output_device, "playback", &ss, NULL, NULL, &error); if (!pa_output) { daemon_log(LOG_ERR, "pa_simple_new failed: %s", pa_strerror(error)); goto finish; } input_fd = open(context->shush_filename, O_RDONLY); if (input_fd < 0) { daemon_log(LOG_ERR, "Error reading %s: %s", context->shush_filename, strerror(errno)); goto finish; } for (;;) { uint8_t buf[BUFSIZE]; ssize_t r; r = read(input_fd, buf, sizeof(buf)); if (r < 0) { daemon_log(LOG_ERR, "read() failed: %s", strerror(errno)); goto finish; } if (r == 0) goto finish; if (pa_simple_write(pa_output, buf, (size_t) r, &error) < 0) { daemon_log(LOG_ERR, "pa_simple_write() failed: %s", pa_strerror(errno)); goto finish; } } if (pa_simple_drain(pa_output, &error) < 0) { daemon_log(LOG_ERR, "pa_simple_drain() failed: %s", pa_strerror(errno)); goto finish; } finish: if (input_fd > 0) close(input_fd); if (pa_output) pa_simple_free(pa_output); } void *audio_loop(void *context_p) { context_t *context = (context_t *)context_p; time_t t = time(NULL); double loudness; double points = 0.0; int error; //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); daemon_log(LOG_INFO, "Starting listening..."); const short buf[BUFSIZE/2]; int bytes = 0; int trigger = 0; int latest_trigger_time = 0; while (context->enable_processing) { if ((bytes = pa_simple_read(context->pa_input, (void *)buf, sizeof(buf), &error)) < 0) { daemon_log(LOG_ERR, "pa_simple_read failed: %s", pa_strerror(error)); assert(0); } if ((time(NULL) - latest_trigger_time) < context->cooldown) { continue; } ebur128_add_frames_short(context->ebur128_state, buf, sizeof(buf)/2); if ((time(NULL) - t) > SAMPLE_TIME) { t = time(NULL); ebur128_loudness_shortterm(context->ebur128_state, &loudness); points += 100 - fabs(loudness); daemon_log(LOG_INFO, "Points: %f (%d) (%f)", points, context->points_threshold, loudness); if (points > context->points_threshold) { trigger = 1; } else { points *= context->decay; } } if (trigger) { latest_trigger_time = time(NULL); audio_trigger(context); points = 0; trigger = 0; daemon_log(LOG_INFO, "Waiting %d seconds before listening again", context->cooldown); } } daemon_log(LOG_INFO, "Stopped listening..."); return 0; }