summaryrefslogblamecommitdiffstats
path: root/zvok.c
blob: 68fda351ec61c4c9efae21f0e726bd9cab309f09 (plain) (tree)
1
2
3
4
5
6
7
8
9
10






                            
                  

                                     










































                                                                                                                                                                                                                                                                                                                                                                   

                                                   




                                  
                              













                                                                                                                                 
                                                 



                                                                              
                            

                         
                                                                                                   



                                                                                     
                                                                           















































































                                                                                                                                                                                                            

                                                                                                                   

                           
                                                                                                               


























                                                                                                 
                              





                                                                                                                  
                                  





                                                                                                                      

                                                                                                                                       















































                                                                                                                                                                                                                                        
#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;
}