diff options
| -rw-r--r-- | .ccls | 1 | ||||
| -rw-r--r-- | .clang-format | 2 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | lib/audio.hpp | 28 | ||||
| -rw-r--r-- | lib/ui.hpp | 36 | ||||
| -rw-r--r-- | src/audio.cpp | 82 | ||||
| -rw-r--r-- | src/main.c | 195 | ||||
| -rw-r--r-- | src/main.cpp | 7 | ||||
| -rw-r--r-- | src/ui.cpp | 162 |
9 files changed, 321 insertions, 198 deletions
@@ -1,4 +1,5 @@ clang +-Ilib -I/usr/include/cairo -I/usr/include/libpng16 -I/usr/include/freetype2 diff --git a/.clang-format b/.clang-format index edb18e6..52768d3 100644 --- a/.clang-format +++ b/.clang-format @@ -508,7 +508,7 @@ PenaltyBreakString: 10 PenaltyExcessCharacter: 100 PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right +PointerAlignment: Left ReflowComments: false SortIncludes: false #SortUsingDeclarations: false # Unknown to clang-format-4.0 @@ -2,9 +2,11 @@ CXX=g++ CC=gcc LDFLAGS=-lportaudio $(shell pkg-config --libs gtk4 cairo) -CXXFLAGS=-Wall $(shell pkg-config --cflags gtk4 cairo) -O3 -MD -MP +CXXFLAGS=-Wall -Ilib $(shell pkg-config --cflags gtk4 cairo) -O0 -g -MD -MP OBJS=\ - src/main.o + src/audio.o \ + src/main.o \ + src/ui.o .PHONY: all build run clean diff --git a/lib/audio.hpp b/lib/audio.hpp new file mode 100644 index 0000000..3502714 --- /dev/null +++ b/lib/audio.hpp @@ -0,0 +1,28 @@ +#ifndef AUDIO_HPP +#define AUDIO_HPP + +#include <portaudio.h> + +#define FRAMES_PER_BUFFER 256 + +class AudioData { + public: + AudioData(); + void set_device_num(int device_num); + int get_channel_cnt(); + float* get_channels(); + + void start_stream(); + void close_stream(); + + private: + PaStream* stream; + int device_num; + int channel_cnt; + float* channels; + + static int patestCallback(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, + void* userData); +}; + +#endif diff --git a/lib/ui.hpp b/lib/ui.hpp new file mode 100644 index 0000000..c656058 --- /dev/null +++ b/lib/ui.hpp @@ -0,0 +1,36 @@ +#ifndef UI_HPP +#define UI_HPP + +#include <gtk/gtk.h> +#include <cairo/cairo.h> +#include "audio.hpp" +#include <bits/stdc++.h> + +using namespace std; + +class GTKUI { + public: + GTKUI(int argc, char* argv[]); + static void draw(cairo_t* cr, GtkWidget* drawing_area, gpointer user_data); + static void draw_cb(GtkDrawingArea* drawing_area, cairo_t* cr, int width, int height, gpointer user_data); + static void select_device(GtkWidget* widget, gpointer user_data); + static void refresh(GtkWidget* widget, gpointer user_data); + static void close_window(GtkWidget* widget, gpointer user_data); + static gboolean drawCallback(GtkWidget* widget, GdkFrameClock* frame_clock, gpointer user_data); + static void activate(GtkApplication* app, gpointer user_data); + + private: + GtkApplication* app; + + GtkWidget* window; + GtkWidget* grid; + GtkWidget* refresh_button; + GtkWidget* quit_button; + vector<GtkWidget*> buttons; + GtkWidget* drawing_area; + + int status; + AudioData* audiodata; +}; + +#endif diff --git a/src/audio.cpp b/src/audio.cpp new file mode 100644 index 0000000..e5aed56 --- /dev/null +++ b/src/audio.cpp @@ -0,0 +1,82 @@ +#include "audio.hpp" +#include <bits/stdc++.h> + +using namespace std; + +AudioData::AudioData() +{ + this->device_num = 0; + this->channel_cnt = 0; + this->channels = NULL; + this->stream = NULL; +} + +int AudioData::get_channel_cnt() +{ + return this->channel_cnt; +} + +float* AudioData::get_channels() +{ + return this->channels; +} + +void AudioData::set_device_num(int device_num) +{ + this->device_num = device_num; +} + +int AudioData::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->channel_cnt; i++) { + if (data->channels[i] > 0) { + data->channels[i] -= 0.005; + } + } + + for (unsigned long i = 0; i < framesPerBuffer * data->channel_cnt; i += data->channel_cnt) { + for (int j = 0; j < data->channel_cnt; j++) { + data->channels[j] = max(data->channels[j], fabs(in[i + j])); + } + } + + return 0; +} + +void AudioData::start_stream() +{ + close_stream(); + Pa_Initialize(); + + 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; + + this->channel_cnt = inputParameters.channelCount; + this->channels = (float*)calloc(this->channel_cnt, sizeof(float)); + + Pa_OpenStream(&stream, &inputParameters, NULL, Pa_GetDeviceInfo(device_num)->defaultSampleRate, FRAMES_PER_BUFFER, paNoFlag, &this->patestCallback, this); + Pa_StartStream(stream); +} + +void AudioData::close_stream() +{ + if (this->stream != NULL) { + Pa_StopStream(this->stream); + Pa_CloseStream(this->stream); + Pa_Terminate(); + this->stream = NULL; + } + + if (this->channels) { + free(this->channels); + this->channels = NULL; + } +} diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 10d00eb..0000000 --- a/src/main.c +++ /dev/null @@ -1,195 +0,0 @@ -#include <gtk/gtk.h> -#include <cairo/cairo.h> -#include <portaudio.h> - -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; -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..1c9a217 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,7 @@ +#include "audio.hpp" +#include "ui.hpp" + +int main(int argc, char* argv[]) +{ + GTKUI* gtk = new GTKUI(argc, argv); +} diff --git a/src/ui.cpp b/src/ui.cpp new file mode 100644 index 0000000..a4b033b --- /dev/null +++ b/src/ui.cpp @@ -0,0 +1,162 @@ +#include <bits/stdc++.h> +#include "ui.hpp" + +using namespace std; + +void GTKUI::draw(cairo_t* cr, GtkWidget* drawing_area, gpointer user_data) +{ + GTKUI* gtk = (GTKUI*)user_data; + AudioData* audiodata = gtk->audiodata; + + float window_width = 900; + float window_height = 900; + float radius = 200; + float space = 50; + float delta, x, y; + + delta = 2 * M_PI / audiodata->get_channel_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->get_channel_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->get_channels()[i] * radius, 0, 2 * M_PI); + cairo_arc(cr, window_width / 2 + x, window_height / 2 + y, audiodata->get_channels()[i + 1] * radius, 0, 2 * M_PI); + } + cairo_fill(cr); + + gtk_widget_queue_draw(drawing_area); +} + +void GTKUI::draw_cb(GtkDrawingArea* drawing_area, cairo_t* cr, int width, int height, gpointer user_data) +{ + GTKUI* gtk = (GTKUI*)user_data; + AudioData* audiodata = gtk->audiodata; + + if (audiodata->get_channel_cnt() == 0) + return; + + cairo_paint(cr); + draw(cr, GTK_WIDGET(drawing_area), user_data); +} + +struct ButtonData { + GTKUI* gtk; + int select_device; +}; + +void GTKUI::select_device(GtkWidget* widget, gpointer user_data) +{ + ButtonData* buttond = (ButtonData*)user_data; + GTKUI* gtk = buttond->gtk; + AudioData* audiodata = gtk->audiodata; + + audiodata->set_device_num(buttond->select_device); + audiodata->start_stream(); + + gtk_widget_add_tick_callback(gtk->drawing_area, gtk->drawCallback, user_data, NULL); +} + +void GTKUI::refresh(GtkWidget* widget, gpointer user_data) +{ + GTKUI* gtk = (GTKUI*)user_data; + AudioData* audiodata = gtk->audiodata; + + 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); + + ButtonData* buttond = new ButtonData; + buttond->gtk = gtk; + buttond->select_device = i; + + button = gtk_button_new_with_label(deviceInfo->name); + gtk->buttons.push_back(button); + g_signal_connect(button, "clicked", G_CALLBACK(select_device), buttond); + gtk_grid_attach(GTK_GRID(gtk->grid), button, 0, numDevices - i - 1, 1, 1); + } + + gtk_widget_queue_draw(gtk->grid); +} + +void GTKUI::close_window(GtkWidget* widget, gpointer user_data) +{ + GTKUI* gtk = (GTKUI*)user_data; + AudioData* audiodata = gtk->audiodata; + + audiodata->close_stream(); + gtk_window_destroy(GTK_WINDOW(gtk->window)); +} + +gboolean GTKUI::drawCallback(GtkWidget* widget, GdkFrameClock* frame_clock, gpointer user_data) +{ + if (widget) { + gtk_widget_queue_draw(GTK_WIDGET(widget)); + } + return true; +} + +void GTKUI::activate(GtkApplication* app, gpointer user_data) +{ + GTKUI* gtk = (GTKUI*)user_data; + + // init window + gtk->window = gtk_application_window_new(app); + gtk_window_set_title(GTK_WINDOW(gtk->window), "Directional Audio Visualizer"); + + // init grid + gtk->grid = gtk_grid_new(); + gtk_window_set_child(GTK_WINDOW(gtk->window), gtk->grid); + + // init drawing_area + gtk->drawing_area = gtk_drawing_area_new(); + gtk_widget_set_size_request(gtk->drawing_area, 900, 900); + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(gtk->drawing_area), draw_cb, user_data, NULL); + + gtk_grid_attach(GTK_GRID(gtk->grid), gtk->drawing_area, 1, 2, 100, 100); + + // init audio devices + Pa_Initialize(); + refresh(gtk->window, user_data); + + // init button for refresh + gtk->refresh_button = gtk_button_new_with_label("Refresh"); + g_signal_connect(gtk->refresh_button, "clicked", G_CALLBACK(gtk->refresh), gtk); + gtk_grid_attach(GTK_GRID(gtk->grid), gtk->refresh_button, 1, 0, 1, 1); + + // init button for quit + gtk->quit_button = gtk_button_new_with_label("Quit"); + g_signal_connect(gtk->quit_button, "clicked", G_CALLBACK(gtk->close_window), gtk); + gtk_grid_attach(GTK_GRID(gtk->grid), gtk->quit_button, 1, 1, 1, 1); + + // set window + gtk_window_present(GTK_WINDOW(gtk->window)); +} + +GTKUI::GTKUI(int argc, char* argv[]) +{ + this->audiodata = new AudioData; + app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS); + g_signal_connect(app, "activate", G_CALLBACK(activate), this); + status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); +} |
