#include #include #include 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) { if (stream != NULL) { Pa_StopStream(stream); Pa_CloseStream(stream); stream = NULL; } 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; }