#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <error.h>
#include <unistd.h>
#include <soundio/soundio.h>
#include <fcntl.h>
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define NUM "4"
void dummyhandler (struct context * rc, float * samples, int samples_num, enum action) { // does nothing
}
enum action {
kys, // this is the last time function will be called, free handler data and NULL it
dtmf_1,
dtmf_2,
dtmf_3,
dtmf_4,
dtmf_5,
dtmf_6,
dtmf_7,
dtmf_8,
dtmf_9,
dtmf_0,
dtmf_a,
dtmf_b,
dtmf_c,
dtmf_d,
dtmf_zvezdica,
dtmf_lojtra,
band,
spodnja,
zgornja,
oranžna,
plava
};
enum state {
silence, // initial state, waiting on line for relation. when changing to silence, kill handler.
carrier,
forbidden, // dtmf # detected
called, // set immediately if calling number is empty
nonsense, // set if menu handler doesn't understand and we shouldn't respond
handler, // call handler on every action
playing // ptt is held down and samples are being sent
};
struct context {
double glasnost; // delete
unsigned roger1; // micros when roger1 was received. when roger2 is received and this time is less than 1000, roger was completely received. if this is greater than 1000, reset to 0 indicating no roger1 was present. on completely received roger, kill handler and switch to playing state. switch to playing state only upon completely hearing roger.
enum state state;
double * samples; // samples to play
unsigned samples_length; // when playing, if samples_length is longer than 60 seconds, cut playback for safety -- we could do this in hardware as well.
unsigned eot; // micros when transmission will be over and we have to release PTT
void * handler_data; // assert that it's NULL after sending kys to handler
};
static enum SoundIoFormat prioritized_formats[] = {
// SoundIoFormatFloat32FE,
///SoundIoFormatS32NE,
// SoundIoFormatS32FE,
///SoundIoFormatS24NE,
// SoundIoFormatS24FE,
// SoundIoFormatS16NE,
// SoundIoFormatS16FE,
// SoundIoFormatFloat64NE,
// SoundIoFormatFloat64FE,
///SoundIoFormatU32NE,
// SoundIoFormatU32FE,
///SoundIoFormatU24NE,
// SoundIoFormatU24FE,
// SoundIoFormatU16NE,
// SoundIoFormatU16FE,
///SoundIoFormatS8,
///SoundIoFormatU8,
SoundIoFormatInvalid
};
static void read_callback (struct SoundIoInStream * instream, int frame_count_min __attribute__((unused)), int frame_count_max) {
struct context * rc = instream->userdata;
struct SoundIoChannelArea * areas;
int frame_count = frame_count_max;
int err = soundio_instream_begin_read(instream, &areas, &frame_count);
long long vzorcev = 0;
double glasnost = 0;
if (!frame_count)
return;
if (!areas) // HOLE because of overrun! -- kill handler and change to silence/initial state
rc->glasnost = 0;
else
for (int frame = 0; frame < frame_count; frame++)
for (int ch = 0; ch < instream->layout.channel_count; ch++) {
glasnost += ABS(* (float *) areas[ch].ptr);
vzorcev++;
areas[ch].ptr += areas[ch].step;
}
rc->glasnost = glasnost / vzorcev;
if ((err = soundio_instream_end_read(instream)))
error_at_line(0, 0, __FILE__, __LINE__, "soundio_instream_read_end: %s", soundio_strerror(err));
}
static void overflow_callback (struct SoundIoInStream * instream __attribute__((unused))) {
static int count = 0;
fprintf(stderr, "overflow %d\n", ++count);
}
static void error_callback (struct SoundIoInStream * instream __attribute__((unused)), int err) {
fprintf(stderr, "error %s\n", soundio_strerror(err)); // TODO this is unrecoverable, make exit of program
}
int main (void) {
struct SoundIoInStream * instream = NULL;
struct SoundIoDevice * selected_device = NULL;
int r = 0;
fprintf(stderr, "z okoljsko spremenljivko ID nastaviš id naprave -- idje izpiše program naprave\nz okoljsko spremenljivko ZALEDJE nastaviš zaledje -- program naprave izpiše možna zaledja\n");
enum SoundIoBackend backend = SoundIoBackendNone;
char * device_id = getenv("ID");
if (getenv("ZALEDJE")) {
switch (getenv("ZALEDJE")[0]) {
case 'd':
backend = SoundIoBackendDummy;
break;
case 'a':
backend = SoundIoBackendAlsa;
break;
case 'p':
backend = SoundIoBackendPulseAudio;
break;
case 'j':
backend = SoundIoBackendJack;
break;
case 'c':
backend = SoundIoBackendCoreAudio;
break;
case 'w':
backend = SoundIoBackendWasapi;
break;
}
}
struct SoundIo * soundio = soundio_create();
if (!soundio)
error_at_line(1, ENOMEM, __FILE__, __LINE__, "soundio_create()");
int err = (backend == SoundIoBackendNone) ? soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err) {
error_at_line(0, 0, __FILE__, __LINE__, "soundio_connect: %s", soundio_strerror(err));
r = 2;
goto r;
}
soundio_flush_events(soundio);
if (device_id) {
for (int i = 0; i < soundio_input_device_count(soundio); i++) {
struct SoundIoDevice * device = soundio_get_input_device(soundio, i);
if (!strcmp(device_id, device->id)) {
selected_device = device;
break;
}
soundio_device_unref(device);
}
} else {
int device_index = soundio_default_input_device_index(soundio);
selected_device = soundio_get_input_device(soundio, device_index);
}
if (!selected_device) {
error_at_line(0, 0, __FILE__, __LINE__, "!selected_device");
r = 3;
goto r;
}
fprintf(stderr, "izbrana naprava je %s\n", selected_device->name);
if (selected_device->probe_error) {
error_at_line(0, 0, __FILE__, __LINE__, "unable to probe device: %s", soundio_strerror(selected_device->probe_error));
r = 4;
goto r;
}
soundio_device_sort_channel_layouts(selected_device); // TODO poskusi brez
int sample_rate = 0;
for (int i = 0; i < selected_device->sample_rate_count; i++) {
if (44100 <= selected_device->sample_rates[i].max && 44100 >= selected_device->sample_rates[i].min)
sample_rate = 44100;
}
if (!sample_rate) {
error_at_line(0, 0, __FILE__, __LINE__, "naprava ne podpira vzorčenja na željeni frekvenci");
r = 5;
goto r;
}
if (!selected_device->format_count) {
error_at_line(0, 0, __FILE__, __LINE__, "naprava ne podpira oblik");
r = 6;
goto r;
}
enum SoundIoFormat fmt = SoundIoFormatInvalid;
for (unsigned i = 0; i < sizeof prioritized_formats/sizeof prioritized_formats[0]; i++) {
if (soundio_device_supports_format(selected_device, prioritized_formats[i])) {
fmt = prioritized_formats[i];
break;
}
}
if (fmt == SoundIoFormatInvalid) {
// fmt = selected_device->formats[0];
error_at_line(0, 0, __FILE__, __LINE__, "naprava ne podpira podprte oblike");
r = 7;
goto r;
}
instream = soundio_instream_create(selected_device);
if (!instream) {
error_at_line(0, 0, __FILE__, __LINE__, "oom");
r = 8;
goto r;
}
// sample_rate = 8000;
fprintf(stderr, "hitrost vzorčenja je %d Hz, %s (prepleten)\n", sample_rate, soundio_format_string(fmt));
instream->format = fmt;
instream->sample_rate = sample_rate;
instream->read_callback = read_callback;
instream->overflow_callback = overflow_callback;
instream->error_callback = error_callback;
struct context rc = { 0 };
instream->userdata = &rc;
if ((err = soundio_instream_open(instream))) {
error_at_line(0, 0, __FILE__, __LINE__, "soundio_instream_open: %s (%d)", soundio_strerror(err), err);
r = 9;
goto r;
}
if (instream->bytes_per_sample != 4) {
error_at_line(0, 0, __FILE__, __LINE__, "nisem pričakoval vzorcev, velikih po %d bajtov", instream->bytes_per_sample);
r = 10;
goto r;
}
fprintf(stderr, "pretok: %s\n", instream->layout.name);
if ((err = soundio_instream_start(instream))) {
error_at_line(0, 0, __FILE__, __LINE__, "soundio_instream_start: %s", soundio_strerror(err));
r = 11;
goto r;
}
int najv_gl = 1;
int leds[] = { open("/sys/class/leds/input" NUM "::numlock/brightness", O_WRONLY), open("/sys/class/leds/input" NUM "::capslock/brightness", O_WRONLY), open("/sys/class/leds/input" NUM "::scrolllock/brightness", O_WRONLY) };
while (1) {
soundio_flush_events(soundio);
usleep(10000);
if (rc.glasnost > najv_gl)
najv_gl = rc.glasnost;
if (najv_gl < 1)
najv_gl = 1;
printf("glasnost: ");
for (int i = 0; i < 64; i++)
if (((double) rc.glasnost/najv_gl)*64 > i)
printf("@");
else
printf(" ");
if ((double) rc.glasnost/najv_gl > 0.2)
write(leds[0], "1", 1);
else
write(leds[0], "0", 1);
if ((double) rc.glasnost/najv_gl > 0.5)
write(leds[1], "1", 1);
else
write(leds[1], "0", 1);
if ((double) rc.glasnost/najv_gl > 0.7)
write(leds[2], "1", 1);
else
write(leds[2], "0", 1);
printf("\n");
if (rc.glasnost < najv_gl/2)
najv_gl -= 5;
}
r:
if (instream)
soundio_instream_destroy(instream);
if (selected_device)
soundio_device_unref(selected_device);
soundio_destroy(soundio);
return r;
}