aboutsummaryrefslogtreecommitdiff
path: root/main.c
blob: 64bceaa21fbbb2e3c0f8bc864f749c3abebabf86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <gtk/gtk.h>
#include <cairo/cairo.h>
#include <portaudio.h>

int device = -1;

typedef struct {
	float left;
	float right;
} AudioData;

AudioData audiodata;

struct data {
	int device_num;
};

PaStream *stream = NULL;
GtkWidget *drawing_area;

#define FRAMES_PER_BUFFER 256

int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
	AudioData *data = (AudioData *)userData;
	float *in = (float *)inputBuffer;

	if (data->left > 0) {
		data->left -= 0.005;
	}

	if (data->right > 0) {
		data->right -= 0.005;
	}

	for (unsigned long i = 0; i < framesPerBuffer * 2; i += 2) {
		data->left = MAX(data->left, fabs(in[i]));
		data->right = MAX(data->right, fabs(in[i + 1]));
	}

	if (drawing_area) {
		gtk_widget_queue_draw(GTK_WIDGET(drawing_area));
	}

	return 0;
}

static void select_device(GtkWidget *widget, gpointer data)
{
	struct data *d = (struct data *)data;
	device = d->device_num;
	printf("Device %d  selected\n", d->device_num);

	PaStreamParameters inputParameters;
	memset(&inputParameters, 0, sizeof(inputParameters));
	inputParameters.channelCount = 2;
	inputParameters.device = device;
	inputParameters.hostApiSpecificStreamInfo = NULL;
	inputParameters.sampleFormat = paFloat32;
	inputParameters.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowInputLatency;

	Pa_OpenStream(&stream, &inputParameters, NULL, Pa_GetDeviceInfo(device)->defaultSampleRate, FRAMES_PER_BUFFER, paNoFlag, patestCallback, &audiodata);
	Pa_StartStream(stream);
}

static void draw(cairo_t *cr, GtkWidget *area)
{
	cairo_set_source_rgba(cr, 1, 0, 0, 1);
	cairo_arc(cr, 250, 250, audiodata.left * 250, 0, 2 * M_PI);
	cairo_arc(cr, 800, 250, audiodata.right * 250, 0, 2 * M_PI);
	cairo_fill(cr);

	gtk_widget_queue_draw(area);
}

static void draw_cb(GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer data)
{
	cairo_paint(cr);
	draw(cr, data);
}

static void refresh()
{
}

static void activate(GtkApplication *app, gpointer user_data)
{
	GtkWidget *window;
	GtkWidget *grid;
	GtkWidget *button;

	// init window
	window = gtk_application_window_new(app);
	gtk_window_set_title(GTK_WINDOW(window), "Directional Audio Visualizer");

	// init grid
	grid = gtk_grid_new();
	gtk_window_set_child(GTK_WINDOW(window), grid);

	// init drawing_area
	drawing_area = gtk_drawing_area_new();
	gtk_widget_set_size_request(drawing_area, 1050, 500);
	gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(drawing_area), draw_cb, drawing_area, NULL);
	gtk_grid_attach(GTK_GRID(grid), drawing_area, 1, 2, 100, 100);

	// init audio devices
	Pa_Initialize();
	int numDevices = Pa_GetDeviceCount();

	if (numDevices < 0) {
		printf("Error getting device count.\n");
		exit(EXIT_FAILURE);
	} else if (numDevices == 0) {
		printf("There are no available audio devices on this machine.\n");
		exit(EXIT_SUCCESS);
	}

	// init button for every audio device
	for (int i = 0; i < numDevices; i++) {
		const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
		struct data *d = malloc(sizeof(struct data));
		d->device_num = i;
		button = gtk_button_new_with_label(deviceInfo->name);
		g_signal_connect(button, "clicked", G_CALLBACK(select_device), d);
		gtk_grid_attach(GTK_GRID(grid), button, 0, numDevices - i - 1, 1, 1);
	}

	// init button for refresh
	button = gtk_button_new_with_label("Refresh");
	g_signal_connect_swapped(button, "clicked", G_CALLBACK(refresh), window);
	gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1);

	// init button for quit
	button = gtk_button_new_with_label("Quit");
	g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_window_destroy), window);
	gtk_grid_attach(GTK_GRID(grid), button, 1, 1, 1, 1);

	// set window
	gtk_window_present(GTK_WINDOW(window));
}

int main(int argc, char **argv)
{
	GtkApplication *app;
	int status;

	app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
	g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
	status = g_application_run(G_APPLICATION(app), argc, argv);
	g_object_unref(app);

	return status;
}