diff --git a/.gitignore b/.gitignore
index bfd5ee2507a88b5ff0c4b8c185ab39d056e9f0bb..acf09d2b0104c7981c29d4b4fc7e0eb5b20348c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ cmake_install.cmake
 .cache/*
 Debug/*
 compile_commands.json
+build/*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0f11058346d8fdc2b869223465711b16b5258f79..3f1f09b48d35ce63ed520cc461be4accd6ee1c76 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,8 +24,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 #    endif()
 #endif()
 
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+
 find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
 find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
+find_package(Threads REQUIRED)
+find_package(X11 REQUIRED)
+find_library(AVCODEC_LIBRARY avcodec)
+find_library(AVUTIL_LIBRARY avutil)
+find_library(SWRESAMPLE_LIBRARY swresample)
+find_library(AVFORMAT_LIBRARY avformat)
 
 set(PROJECT_SOURCES
         main.cpp
@@ -51,6 +59,12 @@ else()
 endif()
 
 target_link_libraries(Violet PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
+target_link_libraries(Violet PRIVATE Threads::Threads)
+target_link_libraries(Violet PRIVATE ${X11_LIBRARIES})
+target_link_libraries(Violet PRIVATE ${AVCODEC_LIBRARY})
+target_link_libraries(Violet PRIVATE ${AVUTIL_LIBRARY})
+target_link_libraries(Violet PRIVATE ${SWRESAMPLE_LIBRARY})
+target_link_libraries(Violet PRIVATE ${AVFORMAT_LIBRARY})
 
 set_target_properties(Violet PROPERTIES
     MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 6fdfb0fa2cfee732776ac651bef9441d3446ff24..067fadfe18d3e25bcee4c597dffec7798b5cfad9 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -2,26 +2,15 @@
 #include <iostream>
 #include <string.h>
 #include <stdio.h>
+#include <CImg.h>
 
-typedef struct WAV_HEADER {
-  uint8_t RIFF[4];      // RIFF Header Magic header
-  uint32_t chunkSize;   // RIFF Chunk Size
-  uint8_t FORMAT[4];    // WAVE Header
-} wav_header;
-
-typedef struct FTM_CHUNK_DATA {
-  uint16_t audioFormat;     // Audio format
-  uint16_t numOfChan;       // Number of channels
-  uint32_t sampleRate;      // Sampling Frequency in Hz
-  uint32_t byteRate;        // bytes per second
-  uint16_t blockAlign;      // 2=16-bit mono, 4=16-bit stereo
-  uint16_t bitsPerSample;   // Number of bits per sample
-} wav_fmt_chunk_data;
-
-typedef struct DATA_CHUNK_PARAM {
-  uint32_t chunkID;   // "data"  string
-  uint32_t chunkSize;   // Sampled data length
-} data_chunk_param;
+extern "C" {
+#include <libavutil/opt.h>
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswresample/swresample.h>
+#include <libavcodec/avfft.h>
+}
 
 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
@@ -45,31 +34,171 @@ MainWindow::~MainWindow()
 {
 }
 
+int decode_audio_file(const char* path, const int sample_rate, double** data, int* size) {
+
+    // initialize all muxers, demuxers and protocols for libavformat
+    // (does nothing if called twice during the course of one program execution)
+    av_register_all();
+
+    // get format from audio file
+    AVFormatContext* format = avformat_alloc_context();
+    if (avformat_open_input(&format, path, NULL, NULL) != 0) {
+        fprintf(stderr, "Could not open file '%s'\n", path);
+        return -1;
+    }
+    if (avformat_find_stream_info(format, NULL) < 0) {
+        fprintf(stderr, "Could not retrieve stream info from file '%s'\n", path);
+        return -1;
+    }
+
+    // Find the index of the first audio stream
+    int stream_index =- 1;
+    for (int i=0; i<format->nb_streams; i++) {
+        if (format->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+            stream_index = i;
+            break;
+        }
+    }
+    if (stream_index == -1) {
+        fprintf(stderr, "Could not retrieve audio stream from file '%s'\n", path);
+        return -1;
+    }
+    AVStream* stream = format->streams[stream_index];
+
+    // find & open codec
+    AVCodecContext* codec = stream->codec;
+    if (avcodec_open2(codec, avcodec_find_decoder(codec->codec_id), NULL) < 0) {
+        fprintf(stderr, "Failed to open decoder for stream #%u in file '%s'\n", stream_index, path);
+        return -1;
+    }
+
+    // prepare resampler
+    struct SwrContext* swr = swr_alloc();
+    av_opt_set_int(swr, "in_channel_count",  codec->channels, 0);
+    av_opt_set_int(swr, "out_channel_count", 1, 0);
+    av_opt_set_int(swr, "in_channel_layout",  codec->channel_layout, 0);
+    av_opt_set_int(swr, "out_channel_layout", AV_CH_LAYOUT_MONO, 0);
+    av_opt_set_int(swr, "in_sample_rate", codec->sample_rate, 0);
+    av_opt_set_int(swr, "out_sample_rate", sample_rate, 0);
+    av_opt_set_sample_fmt(swr, "in_sample_fmt",  codec->sample_fmt, 0);
+    av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_DBL,  0);
+    swr_init(swr);
+    if (!swr_is_initialized(swr)) {
+        fprintf(stderr, "Resampler has not been properly initialized\n");
+        return -1;
+    }
+
+    // prepare to read data
+    AVPacket packet;
+    av_init_packet(&packet);
+    AVFrame* frame = av_frame_alloc();
+    if (!frame) {
+        fprintf(stderr, "Error allocating the frame\n");
+        return -1;
+    }
+
+    // iterate through frames
+    *data = NULL;
+    *size = 0;
+    while (av_read_frame(format, &packet) >= 0) {
+        // decode one frame
+        int gotFrame;
+        if (avcodec_decode_audio4(codec, frame, &gotFrame, &packet) < 0) {
+            break;
+        }
+        if (!gotFrame) {
+            continue;
+        }
+        // resample frames
+        double* buffer;
+        av_samples_alloc((uint8_t**) &buffer, NULL, 1, frame->nb_samples, AV_SAMPLE_FMT_DBL, 0);
+        int frame_count = swr_convert(swr, (uint8_t**) &buffer, frame->nb_samples, (const uint8_t**) frame->data, frame->nb_samples);
+        // append resampled frames to data
+        *data = (double*) realloc(*data, (*size + frame->nb_samples) * sizeof(double));
+        memcpy(*data + *size, buffer, frame_count * sizeof(double));
+        *size += frame_count;
+    }
+
+    // clean up
+    av_frame_free(&frame);
+    swr_free(&swr);
+    avcodec_close(codec);
+    avformat_free_context(format);
+
+    // success
+    return 0;
+}
+
+#define MAXPIXVALUE 12000
 void MainWindow::openAudioFile(){
-  QString filename = QFileDialog::getOpenFileName(this, "Select a file");
-  wav_header wavHeader;
-  QMessageBox msg;
-  data_chunk_param dataChunkParam;
-  wav_fmt_chunk_data wavFmtChunkData;
-  FILE *wavFile = fopen(filename.toStdString().c_str(), "r");
-
-  fread(&wavHeader, 1, sizeof(wav_header), wavFile);
-  while (1) {
-    fread(&dataChunkParam, 1, sizeof(dataChunkParam), wavFile);
-    if (dataChunkParam.chunkID == 0x20746d66)
-        break;
-    fseek(wavFile, dataChunkParam.chunkSize, SEEK_CUR);
-  }
-  fread(&wavFmtChunkData, 1, dataChunkParam.chunkSize, wavFile);
-  while (1) {
-    fread(&dataChunkParam, 1, sizeof(dataChunkParam), wavFile);
-    if (dataChunkParam.chunkID == 0x61746164)
-        break;
-    fseek(wavFile, dataChunkParam.chunkSize, SEEK_CUR);
-  }
-
-  uint16_t* data = (uint16_t*)malloc(dataChunkParam.chunkSize);
-  fread(data, 1, dataChunkParam.chunkSize, wavFile);
-
-  fclose(wavFile);
+    QString filename = QFileDialog::getOpenFileName(this, "Select a file");
+    QMessageBox msg;
+    FILE *audioFile = fopen(filename.toStdString().c_str(), "r");
+    fclose(audioFile);
+
+    int sample_rate = 44100;
+    double* data;
+    int size;
+    if (decode_audio_file(filename.toStdString().c_str(), sample_rate, &data, &size) != 0){
+        printf("ERROR\n");
+        return;
+    }
+
+    /*
+    for (int j = size/2; j < size/2 + 20; j++){
+        if (data[j] != 0)
+            fprintf(stderr, "data %d: %f\n", j, data[j]);
+    }
+    */
+
+    int i; // just some iterator indices
+    int chunk_size = 512;
+    int overlap = 128;
+    int decal = chunk_size - overlap;
+    fprintf(stderr, "size,chunk_size,overlap,decal: %d,%d,%d,%d\n", size, chunk_size, overlap, decal);
+
+    FFTSample* chunk_data = (FFTSample*)av_malloc_array(2*chunk_size, sizeof(FFTSample));
+    RDFTContext* ctx = av_rdft_init((int) log2(chunk_size), DFT_R2C);
+    int x = 0;
+
+    int width = (size - chunk_size) / decal;
+    int height = chunk_size;
+    int16_t* pixs = (int16_t*)malloc(sizeof(int16_t) * width * height / 2);
+
+    for (i = 0; i < size - chunk_size; i+= decal) {
+        for (int j = 0; j < chunk_size; j++){
+            float curr_dat = data[i+j];
+            double window_modifier = (0.5 * (1 - cos(2 * M_PI * j / (chunk_size - 1)))); // Hann (Hanning) window function
+            //float value = (float) (window_modifier * ((curr_dat) / 32768.0f)); // Convert to float and apply
+            float value = (float) (window_modifier * curr_dat); // Convert to float and apply
+
+            // cap values above 1 and below -1
+            if (value > 1.0) {
+                value = 1;
+            } else if (value < -1.0) {
+                value = -1;
+            }
+            chunk_data[j] = value;
+        }
+
+        av_rdft_calc(ctx, chunk_data);
+
+        for (int j = 0; j < chunk_size / 2; j++){
+            float im = chunk_data[j*2];
+            float re = chunk_data[j*2 + 1];
+            double mag = sqrt(im * im + re * re);
+            /*
+            if ( i == 100*decal && j < 10 )
+                fprintf(stderr, "im,re,mag: %f,%f,%f\n", im, re, mag);
+            */
+            pixs[j*width+x] = (int16_t) (mag) * MAXPIXVALUE;
+        }
+        x++;
+    }
+
+    cimg_library::CImg<int16_t> img(pixs, width, height / 2);
+    img.mirror("y").save_png("out.png");
+
+    av_rdft_end(ctx);
+    av_free(data);
 }