#include #include #include typedef struct { int device_num; int chanel_cnt; float *chanels; } AudioData; typedef struct { int device_num; } SelectedDevice; AudioData audiodata; 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; for (int i = 0; i < data->chanel_cnt; i++) { if (data->chanels[i] > 0) { data->chanels[i] -= 0.005; } } for (unsigned long i = 0; i < framesPerBuffer * data->chanel_cnt; i += data->chanel_cnt) { for (int j = 0; j < data->chanel_cnt; j++) { data->chanels[j] = MAX(data->chanels[j], fabs(in[i + j])); } } 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; } int device_num = ((SelectedDevice *)data)->device_num; printf("Device %d selected\n", device_num); PaStreamParameters inputParameters; memset(&inputParameters, 0, sizeof(inputParameters)); inputParameters.channelCount = Pa_GetDeviceInfo(device_num)->maxInputChannels; inputParameters.device = device_num; inputParameters.hostApiSpecificStreamInfo = NULL; inputParameters.sampleFormat = paFloat32; inputParameters.suggestedLatency = Pa_GetDeviceInfo(device_num)->defaultLowInputLatency; audiodata.device_num = device_num; audiodata.chanel_cnt = inputParameters.channelCount; audiodata.chanels = (float *)calloc(audiodata.chanel_cnt, sizeof(float)); Pa_OpenStream(&stream, &inputParameters, NULL, Pa_GetDeviceInfo(device_num)->defaultSampleRate, FRAMES_PER_BUFFER, paNoFlag, patestCallback, &audiodata); Pa_StartStream(stream); } static void draw(cairo_t *cr, GtkWidget *area) { float window_width = 900; float window_height = 900; float radius = 200; float space = 50; float delta, x, y; delta = 2 * M_PI / audiodata.chanel_cnt; cairo_set_source_rgba(cr, 1, 1, 1, 1); cairo_arc(cr, window_width / 2, window_height / 2, space, 0, 2 * M_PI); cairo_fill(cr); cairo_set_source_rgba(cr, 1, 0, 0, 1); for (int i = 0; i < audiodata.chanel_cnt; i += 2) { x = (radius + space) * cos(i * delta / 2); y = (radius + space) * sin(i * delta / 2); cairo_arc(cr, window_width / 2 - x, window_height / 2 - y, audiodata.chanels[i] * radius, 0, 2 * M_PI); cairo_arc(cr, window_width / 2 + x, window_height / 2 + y, audiodata.chanels[i + 1] * radius, 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, (GtkWidget *)data); } static void refresh(GtkWidget *grid) { Pa_Terminate(); 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); } GtkWidget *button; for (int i = 0; i < numDevices; i++) { const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i); SelectedDevice *sel_dev = (SelectedDevice *)malloc(sizeof(SelectedDevice)); sel_dev->device_num = i; button = gtk_button_new_with_label(deviceInfo->name); g_signal_connect(button, "clicked", G_CALLBACK(select_device), sel_dev); gtk_grid_attach(GTK_GRID(grid), button, 0, numDevices - i - 1, 1, 1); } gtk_widget_queue_draw(grid); } static void close_window(GtkWindow *window) { if (stream != NULL) { Pa_StopStream(stream); Pa_CloseStream(stream); stream = NULL; } Pa_Terminate(); gtk_window_destroy(window); } 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, 900, 900); 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(); refresh(grid); // init button for refresh button = gtk_button_new_with_label("Refresh"); g_signal_connect_swapped(button, "clicked", G_CALLBACK(refresh), grid); 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(close_window), 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; }