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; }
static void analyze_audio( mlt_filter filter, void* buffer, int samples ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; int result = -1; double loudness = 0.0; ebur128_add_frames_float( pdata->r128, buffer, samples ); if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_program" ) ) { result = ebur128_loudness_global( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "program", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_shortterm" ) ) { result = ebur128_loudness_shortterm( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "shortterm", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_momentary" ) ) { result = ebur128_loudness_momentary( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "momentary", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_range" ) ) { double range = 0; result = ebur128_loudness_range( pdata->r128, &range ); if( result == EBUR128_SUCCESS && range != HUGE_VAL && range != -HUGE_VAL ) { mlt_properties_set_double( properties, "range", range ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_peak" ) ) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for( c = 0; c < pdata->r128->channels; c++ ) { double peak; result = ebur128_sample_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak ) { max_peak = peak; } result = ebur128_prev_sample_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak ) { prev_peak = peak; } } mlt_properties_set_double( properties, "max_peak", 20 * log10(max_peak) ); mlt_properties_set_double( properties, "peak", 20 * log10(prev_peak) ); } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_true_peak" ) ) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for( c = 0; c < pdata->r128->channels; c++ ) { double peak; result = ebur128_true_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak ) { max_peak = peak; } result = ebur128_prev_true_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak ) { prev_peak = peak; } } mlt_properties_set_double( properties, "max_true_peak", 20 * log10(max_peak) ); mlt_properties_set_double( properties, "true_peak", 20 * log10(prev_peak) ); } mlt_properties_set_position( properties, "frames_processed", mlt_properties_get_position( properties, "frames_processed" ) + 1 ); }
int main(int argc, const char *argv[]) { struct sockaddr_in addr, graphiteAddr; int addrlen, sock, packetLength, graphiteSocket; struct ip_mreq mreq; uint8_t packet[1452]; //uint16_t sequenceNumber; //uint32_t timestamp; double audioPayload[480]; ebur128_state* state = NULL; double shortTermLoudness; uint32_t frameCounter = 0; char graphiteOutputBuffer[200]; if (argc != 4) { fprintf(stderr, "Argument Error!\n"); fprintf(stderr, "Correct usage: axialufsgraphite <MC Livewire IP> <Graphite Server IP> <Graphite Metric>\n"); fprintf(stderr, "Example: axialufsgraphite 239.192.2.169 127.0.0.1 Studio442PGM\n"); return 1; } const char *axiaLivewireIP = argv[1]; const char *graphiteServerIP = argv[2]; const char *graphiteMetric = argv[3]; /* setup graphite socket */ graphiteSocket = socket(AF_INET,SOCK_STREAM,0); memset(&graphiteAddr, 0, sizeof(graphiteAddr)); graphiteAddr.sin_family = AF_INET; graphiteAddr.sin_port = htons(2003); inet_pton(AF_INET, graphiteServerIP, &(graphiteAddr.sin_addr)); connect(graphiteSocket,(struct sockaddr *) &graphiteAddr, sizeof(graphiteAddr)); /* setup axia socket (multicast UDP listener) */ sock = socket(AF_INET, SOCK_DGRAM, 0); int reuse = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)) == -1) { fprintf(stderr, "setsockopt: %d\n", errno); return 1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(5004); addr.sin_addr.s_addr = inet_addr(axiaLivewireIP); addrlen = sizeof(addr); if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) == -1) { fprintf(stderr, "bind: %d\n", errno); return 1; } mreq.imr_multiaddr.s_addr = inet_addr(axiaLivewireIP); mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)); /* init ebur128 state */ state = malloc((size_t) sizeof(ebur128_state*)); state = ebur128_init((unsigned) 2, (unsigned) 48000, EBUR128_MODE_S); /* main loop */ while(1){ packetLength = recvfrom(sock, packet, sizeof(packet), 0, (struct sockaddr *) &addr, (socklen_t *) &addrlen); //sequenceNumber = (packet[2] << 8 | packet[3]); //timestamp = (packet[4] << 24 | packet[5] << 16 | packet[6] << 8 | packet[7]); for (int i = 12; i < packetLength; i += 3) { int32_t audioPCM = ((packet[i] << 16) | (packet[i + 1] << 8) | (packet[i + 2])); if (audioPCM & 0x800000) audioPCM |= ~0xffffff; // Convert to signed PCM. double audioFloat = (audioPCM * (1.0 / 0x7fffff)); // Convert to float. audioPayload[((i - 12) / 3)] = audioFloat; } ebur128_add_frames_double(state, audioPayload, (size_t) ((packetLength - 12) / 6)); frameCounter += ((packetLength - 12) / 6); if (frameCounter >= 47999) { frameCounter = 0; ebur128_loudness_shortterm(state, &shortTermLoudness); shortTermLoudness = shortTermLoudness <= -70. ? -70. : shortTermLoudness; sprintf(graphiteOutputBuffer, "%s %f %d\n", graphiteMetric, shortTermLoudness, (int) time(NULL)); send(graphiteSocket, graphiteOutputBuffer, (strlen(graphiteOutputBuffer)), 0); } } free(state); return 0; }