├── .dockerignore ├── CMakeLists.txt ├── Dockerfile ├── Makefile ├── README.md ├── audioInformation.c ├── audioInformation.h ├── dataprocessing.c ├── dataprocessing.h ├── docker-compose.yml ├── fftw-3.3.4.tar.gz ├── rebooted.gif └── visualize.c /.dockerignore: -------------------------------------------------------------------------------- 1 | *.mp3 2 | *.wav 3 | fftw-3.3.4 4 | rebooted.gif 5 | Dockerfile 6 | .dockerignore 7 | .git 8 | .gitignore 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake entry point. 2 | 3 | 4 | cmake_minimum_required (VERSION 2.6) 5 | project (MUSIC_VISUALIZER_REBOOTED) 6 | 7 | find_library(DFT 8 | 9 | NAMES 10 | fftw3 11 | PATHS 12 | /usr/lib 13 | /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} 14 | /usr/local/lib 15 | ) 16 | find_library(MATH_L 17 | 18 | NAMES 19 | m 20 | PATHS 21 | /usr/lib 22 | /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} 23 | /usr/local/lib 24 | ) 25 | find_library(THREAD_L 26 | 27 | NAMES 28 | pthread 29 | PATHS 30 | /usr/lib 31 | /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} 32 | /usr/local/lib 33 | ) 34 | find_library(SDL 35 | NAMES 36 | SDL2 37 | PATHS 38 | /usr/lib 39 | /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} 40 | /usr/local/lib 41 | ) 42 | 43 | message(${CMAKE_LIBRARY_ARCHITECTURE}) 44 | 45 | if(MATH_L) 46 | message(${MATH_L}) 47 | endif(MATH_L) 48 | if(SDL) 49 | message(${SDL}) 50 | endif(SDL) 51 | if(DFT) 52 | message(${DFT}) 53 | endif(DFT) 54 | 55 | 56 | #include_directories( 57 | # include 58 | # ) 59 | 60 | file(GLOB SOURCES *.c) 61 | file(GLOB INCLUDES *.h) 62 | 63 | ##list command line warnings 64 | 65 | set(WARNINGS 66 | -Wall -Wextra -Wpedantic -Wformat=2 67 | -Wno-unused-parameter -Wshadow -Wwrite-strings 68 | -Wredundant-decls -Wmissing-include-dirs 69 | ) 70 | 71 | ## compile for DEBUGGING: 72 | ## NOTE: This works also: set(CMAKE_CXX_FLAGS "-ggdb") 73 | 74 | 75 | ## List libraries 76 | 77 | 78 | 79 | set(ALL_LIBS 80 | ${MATH_L} 81 | ${DFT} 82 | ${SDL} 83 | ${THREAD_L} 84 | ${WARNINGS} 85 | ) 86 | 87 | 88 | 89 | 90 | ##Output Assembly 91 | 92 | #set_property(SOURCE ${MY_SOURCE_FILES} PROPERTY COMPILE_FLAGS -save-temps) 93 | 94 | ##Link files and libraries to executable 95 | 96 | set(binary_name "visual.exe") 97 | 98 | add_executable(${binary_name} 99 | ${SOURCES} ${INCLUDES} 100 | 101 | ) 102 | target_link_libraries(${binary_name} 103 | ${ALL_LIBS} 104 | ) 105 | 106 | ## Optimize and dubug enabled if GNU compiler 107 | if(CMAKE_COMPILER_IS_GNUCC) 108 | message("Optimize and debug enabled!") 109 | set(CMAKE_CXX_FLAGS "-O0 -fbuiltin -g -std=c++11") 110 | set(CMAKE_C_FLAGS "-fbuiltin -g -std=c99") 111 | endif(CMAKE_COMPILER_IS_GNUCC) 112 | 113 | 114 | 115 | # HOW-TO. You can safely remove anything below. 116 | 117 | # test if linux, mac or windows : 118 | 119 | if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 120 | message("You're on Mac OS !") 121 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 122 | message("You're on Linux !") 123 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 124 | message("You're on Windows !") 125 | endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Yes this is weird but you have to repeat the "if" here. 126 | 127 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amd64/ubuntu:22.04 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | 5 | RUN apt-get update -y && \ 6 | apt-get install -y --no-install-recommends \ 7 | make \ 8 | pkg-config \ 9 | gdb \ 10 | gcc \ 11 | libsdl2-dev \ 12 | libpipewire-0.3-dev \ 13 | libspa-0.2-dev \ 14 | bash \ 15 | neovim \ 16 | ffmpeg \ 17 | pipewire \ 18 | libsdl2-2.0-0 && \ 19 | apt-get clean 20 | 21 | WORKDIR /opt/visualizer 22 | 23 | COPY . ./ 24 | 25 | RUN tar -zxvf ./fftw-3.3.4.tar.gz && \ 26 | cd fftw-3.3.4 && \ 27 | ./configure && make && make install 28 | 29 | RUN mkdir Music 30 | 31 | RUN make 32 | 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS=`pkg-config --cflags fftw3 sdl2` 3 | CFLAGS += -Wall -Wextra -Wpedantic \ 4 | -Wformat=2 -Wno-unused-parameter -Wshadow \ 5 | -Wwrite-strings -Wstrict-prototypes -Wold-style-definition \ 6 | -Wredundant-decls -Wnested-externs -Wmissing-include-dirs 7 | 8 | ifeq ($(CC),gcc) 9 | CFLAGS += -Wjump-misses-init -Wlogical-op 10 | endif 11 | 12 | CFLAGS += -O3 -g -ggdb -std=c99 -fbuiltin 13 | 14 | LDFLAGS=`pkg-config --libs fftw3 sdl2` 15 | LDFLAGS += -lm 16 | 17 | visual.exe : visualize.o dataprocessing.o audioInformation.o 18 | $(CC) -o $@ $^ $(LDFLAGS) 19 | 20 | visualize.o : visualize.c dataprocessing.h audioInformation.h 21 | $(CC) $(CFLAGS) -c $*.c 22 | 23 | dataprocessing.o : dataprocessing.c dataprocessing.h 24 | $(CC) $(CFLAGS) -c $*.c 25 | 26 | audioInformation.o: audioInformation.c audioInformation.h 27 | $(CC) $(CFLAGS) -c $< 28 | 29 | clean: 30 | rm -rf *.o visual.exe 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Music-Visualizer-Reboot 2 | 3 | - Terminal-based Music Visualizer that displays the power spectrum of a .wav file 4 | - Music visualizer using SDL2 for audio play and FFTW for Fourier Transformation 5 | 6 | ![alt tag](https://github.com/Crelloc/Music-Visualizer-Reboot/blob/master/rebooted.gif) 7 | 8 | Video Demonstration: https://www.youtube.com/watch?v=YaeXZrTrX-Y&t=790s 9 | 10 | For simpler version of this code, checkout: 11 | https://github.com/Crelloc/terminal-music-visualizer 12 | 13 | # Motivation 14 | 15 | - Was planning to design and build a LED music visualizer as an Arduino project but wanted a better understanding of how music visualization software actually works. 16 | 17 | Video Demonstration for LED music visualizer: https://www.youtube.com/watch?v=___XwMbhV4k 18 | 19 | # Software (prerequisites) to run the program using Docker containers 20 | - Docker server: https://docs.docker.com/engine/install/#server 21 | - Docker Compose: https://docs.docker.com/compose/install/linux/#install-the-plugin-manually 22 | 23 | # Software (prerequisites) if not using Docker 24 | 25 | - SDL2: https://wiki.libsdl.org/SDL2/Installation 26 | - Note: make sure to install sdl2, alsa, and pulseaudio development (or any development files like pipewire that your systems uses to play sound ) files before installing SDL2. 27 | - FFTW: http://www.fftw.org/download.html 28 | - pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/ 29 | - GDB: for debugging on Linux 30 | - gcc: C compiler for Linux 31 | - make: build automation tool 32 | - Optional (ffmpeg): will be used to convert audio to .wav 33 | 34 | 35 | # How To Build and Run with Docker 36 | 37 | Make sure you install docker and docker compose from the links given in the **Software (prerequisites) to run the program using Docker containers** 38 | 39 | 40 | ## Add path to your music folder on your host machine: 41 | 42 | edit the [.env](.env) file: 43 | 44 | the [.env](.env) file should be read by [docker-compose.yml](docker-compose.yml) by default 45 | 46 | ```bash 47 | ENV_MUSIC_PATH="/home/some_user/Music" 48 | ``` 49 | 50 | Set path to music folder that has audio files. 51 | 52 | Note: this visualizer runs .wav files. If you only have mp3 files 53 | you can convert the mp3 files into .wav files using ffmpeg which is 54 | automatically installed inside the container using the command: 55 | 56 | ffmpeg -i path/to/audio/file output/path/for/.wav 57 | 58 | for example: 59 | 60 | ```bash 61 | ffmpeg -i Music/song.mp3 Music/song.wav 62 | 63 | ``` 64 | **MAKE SURE THE FILE PATH AND NAME OF FILE DOESN'T HAVE SPACES AND SPECIAL CHARACTERS IN IT** 65 | 66 | ## Build and run docker image: 67 | 68 | ```bash 69 | sudo docker compose build --no-cache && sudo docker compose up --force-recreate --remove-orphans -d 70 | ``` 71 | ## Log into the container: 72 | 73 | ```bash 74 | docker exec -ti visualizer-container bash 75 | 76 | ``` 77 | 78 | ## Synopsis (run the visualizer): 79 | 80 | ./visual.exe [-f] /PATH/TO/WAV/AUDIO/FILE 81 | 82 | For example: 83 | 84 | ```bash 85 | ./visual.exe -f Music/Classic.wav 86 | 87 | ``` 88 | Note: Supports only wav audio files and make sure that the "/PATH/TO/WAV/AUDIO/FILE" doesn't have spaces in it. 89 | 90 | ## Stop container: 91 | 92 | ```bash 93 | sudo docker stop visualizer-container 94 | 95 | ``` 96 | 97 | ## Troubleshooting audio not playing: 98 | 99 | Check for the correct path of audio socket on your system: 100 | 101 | ```bash 102 | pactl info | grep '^Server String'; 103 | 104 | ``` 105 | 106 | and add the correct path in [docker-compose.yml](docker-compose.yml) 107 | 108 | 109 | ```yaml 110 | ... 111 | volumes: 112 | ... 113 | - /run/user/1000/pulse/native:/tmp/pulse/native 114 | ... 115 | 116 | ``` 117 | 118 | Other troubleshooting references: 119 | - https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e?permalink_comment_id=3976309 120 | - https://stackoverflow.com/questions/28985714/run-apps-using-audio-in-a-docker-container/75775875#75775875 121 | - https://leimao.github.io/blog/Docker-Container-Audio/ 122 | 123 | # How To Build and Run without docker 124 | 125 | ## Install prerequisites: 126 | 127 | ### For Ubuntu (or debian-based): 128 | 129 | ```bash 130 | sudo apt-get update && sudo apt-get install gdb gcc pkg-config make 131 | 132 | ``` 133 | if your system is using **pulseaudio** to play sound install the development files for 134 | pulseaudio and alsa 135 | 136 | ```bash 137 | sudo apt-get install libasound2-dev libpulse-dev 138 | 139 | ``` 140 | if your system is using **pipewire** to play sound install the development files for 141 | pipewire 142 | 143 | ```bash 144 | sudo apt-get install libpipewire-0.3-dev libspa-0.2-dev 145 | 146 | ``` 147 | 148 | Install sdl2 development files and sdl application 149 | 150 | ```bash 151 | sudo apt-get install libsdl2-dev libsdl2-2.0-0 152 | 153 | ``` 154 | ## clone repo and change directory into it: 155 | 156 | ```bash 157 | git clone https://github.com/Crelloc/Music-Visualizer-Reboot.git && cd Music-Visualizer-Reboot 158 | 159 | ``` 160 | ## Install fftw3.3.4.tar.gz from project directory or install updated version from http://www.fftw.org/download.html 161 | ```bash 162 | tar -xvf fftw-3.3.4.tar.gz && \ 163 | cd fftw-3.3.4 && \ 164 | chmod +x ./configure && \ 165 | ./configure && \ 166 | make && \ 167 | sudo make install 168 | 169 | 170 | ``` 171 | ## Go back to project directory and reate executable for visualize.c 172 | 173 | ```bash 174 | cd .. && make 175 | 176 | ``` 177 | 178 | ## Synopsis: 179 | 180 | 181 | ```bash 182 | ./visual.exe [-f] /PATH/TO/WAV/AUDIO/FILE 183 | 184 | For example: 185 | ./visual.exe -f ~/Music/Classic.wav 186 | ``` 187 | Note: Supports only wav audio files and make sure that the "/PATH/TO/WAV/AUDIO/FILE" doesn't have spaces in it. 188 | 189 | # Brief overview about how the program works 190 | - Analyzes the digital information in the wav file. 191 | - Computes the fourier transformation every 'N' frames. Eg. computes the fourier transformation (FT) on 'N' L channel data and also computes the FT on 'N' R channel data. 192 | - Gathers frequency and magnitude information from the results of the fourier transformation and records it by storing into an array. 193 | - After analysis is finished the program will play music and display the corresponding information at the terminal. 194 | -------------------------------------------------------------------------------- /audioInformation.c: -------------------------------------------------------------------------------- 1 | #include "audioInformation.h" 2 | 3 | extern volatile int keeprunning; 4 | extern volatile int packet_pos; 5 | extern volatile int print_spectrum; 6 | extern const int BUCKETS; 7 | 8 | void outputpowerspectrum(Visualizer_Pkg_ptr package) 9 | { 10 | int totchannels = GetSDL_AudioSpec(package)->channels; 11 | struct FFTW_Results* res= GetFFTW_Results(package); 12 | int thickness = 2, minutes, seconds, b, c, t, p, energy; 13 | double n_samples, tot_seconds; 14 | 15 | /*PRINT GRID*/ 16 | if(system("clear") < 0){} 17 | 18 | for(c = 0; c < totchannels; ++c){ 19 | for(b = 0; b < BUCKETS; ++b){ 20 | if((c+1)%2 == 0) 21 | energy = res[packet_pos].peakmagMatrix[c][b]; 22 | else 23 | energy = res[packet_pos].peakmagMatrix[c][BUCKETS-b-1]; 24 | for(t = 0; t < thickness; ++t){ 25 | for (p = 0; p < energy; ++p) 26 | putchar('|'); 27 | 28 | putchar('>'); 29 | putchar('\n'); 30 | } 31 | } 32 | putchar('\n'); 33 | fflush(stdout); 34 | } 35 | 36 | /*OUTPUT REMAINING TIME*/ 37 | n_samples = GetAudioData(package)->currentLength; 38 | n_samples /= (package->bitsize/8); 39 | n_samples /= totchannels; 40 | 41 | tot_seconds = n_samples / GetSDL_AudioSpec(package)->freq; 42 | minutes = tot_seconds / 60; 43 | seconds = (int)tot_seconds%60; 44 | 45 | printf("\n%02d:%02d\n", minutes, seconds); 46 | fflush(stdout); 47 | } 48 | 49 | void MyAudioCallback(void* userdata, Uint8* stream, int streamLength) 50 | { 51 | struct Visualizer_Pkg* package = (struct Visualizer_Pkg*)userdata; 52 | struct AudioData* audio= GetAudioData(package); 53 | Uint32 length; 54 | 55 | if(audio->currentLength == 0){ 56 | return; 57 | } 58 | outputpowerspectrum(package); 59 | 60 | length = (Uint32)streamLength; 61 | length = (length > audio->currentLength ? audio->currentLength : length); 62 | 63 | SDL_memcpy(stream, audio->currentPos, length); 64 | 65 | audio->currentPos += length; 66 | audio->currentLength -= length; 67 | 68 | packet_pos++; 69 | } 70 | 71 | /*function Get8bitAudioSample hasnt been implemented*/ 72 | double Get8bitAudioSample(Uint8* bytebuffer,SDL_AudioFormat format) 73 | { 74 | return 0.0; 75 | } 76 | 77 | double Get16bitAudioSample(Uint8* bytebuffer, SDL_AudioFormat format) 78 | { 79 | 80 | Uint16 val = 0x0; 81 | 82 | if(SDL_AUDIO_ISLITTLEENDIAN(format)) 83 | val = (uint16_t)bytebuffer[0] | ((uint16_t)bytebuffer[1] << 8); 84 | 85 | else 86 | val = ((uint16_t)bytebuffer[0] << 8) | (uint16_t)bytebuffer[1]; 87 | 88 | if(SDL_AUDIO_ISSIGNED(format)) 89 | return ((int16_t)val)/32768.0; 90 | 91 | return val/65535.0; 92 | } 93 | /*function Get32bitAudioSample hasnt been implemented*/ 94 | 95 | double Get32bitAudioSample(Uint8* bytebuffer, SDL_AudioFormat format) 96 | { 97 | return 0.0; 98 | } 99 | 100 | struct AudioData* GetAudioData(Visualizer_Pkg_ptr package) 101 | { 102 | return package->AudioData_ptr; 103 | } 104 | 105 | SDL_AudioSpec* GetSDL_AudioSpec(Visualizer_Pkg_ptr package) 106 | { 107 | return package->wavSpec_ptr; 108 | } 109 | 110 | struct FFTW_Results* GetFFTW_Results(Visualizer_Pkg_ptr package) 111 | { 112 | return package->FFTW_Results_ptr; 113 | } 114 | 115 | struct FFTWop* GetFFTWop(Visualizer_Pkg_ptr package) 116 | { 117 | return package->fftw_ptr; 118 | } 119 | -------------------------------------------------------------------------------- /audioInformation.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOINFORMATION_H 2 | #define AUDIOINFORMATION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct AudioData; 9 | struct Visualizer_Pkg; 10 | struct FFTWop; 11 | struct FFTW_Results; 12 | typedef struct Visualizer_Pkg* Visualizer_Pkg_ptr; 13 | 14 | void outputpowerspectrum(Visualizer_Pkg_ptr package); 15 | void MyAudioCallback(void* userdata, Uint8* stream, int streamLength); 16 | double Get8bitAudioSample(Uint8* bytebuffer, SDL_AudioFormat format); 17 | double Get16bitAudioSample(Uint8* bytebuffer, SDL_AudioFormat format); 18 | double Get32bitAudioSample(Uint8* bytebuffer, SDL_AudioFormat format); 19 | 20 | struct AudioData* GetAudioData(Visualizer_Pkg_ptr); 21 | SDL_AudioSpec* GetSDL_AudioSpec(Visualizer_Pkg_ptr); 22 | struct FFTW_Results* GetFFTW_Results(Visualizer_Pkg_ptr); 23 | struct FFTWop* GetFFTWop(Visualizer_Pkg_ptr); 24 | 25 | 26 | struct AudioData 27 | { 28 | Uint8* currentPos; 29 | Uint32 currentLength; 30 | Uint8* wavStart; 31 | Uint32 wavLength; 32 | }; 33 | 34 | struct FFTWop 35 | { 36 | fftw_complex *in; 37 | fftw_complex *out; 38 | fftw_plan p; 39 | int index; 40 | 41 | }; 42 | 43 | struct FFTW_Results 44 | { 45 | double* peakfreq; 46 | double* peakpower; 47 | double** peakmagMatrix; 48 | char*** outputMatrix; 49 | double phase; 50 | }; 51 | 52 | struct Visualizer_Pkg 53 | { 54 | char* filename; 55 | int total_packets; 56 | int total_frames; 57 | int frame_size; 58 | int bitsize; 59 | 60 | SDL_AudioDeviceID device; 61 | SDL_AudioSpec* wavSpec_ptr; 62 | struct AudioData* AudioData_ptr; 63 | struct FFTW_Results* FFTW_Results_ptr; 64 | struct FFTWop* fftw_ptr; 65 | 66 | 67 | double (*GetAudioSample)(Uint8*, SDL_AudioFormat); 68 | void (*setupDFT)(Visualizer_Pkg_ptr, Uint8*, int ); 69 | }; 70 | 71 | 72 | 73 | #endif //AUDIOINFORMATION_H 74 | -------------------------------------------------------------------------------- /dataprocessing.c: -------------------------------------------------------------------------------- 1 | #include "dataprocessing.h" 2 | #include 3 | #include 4 | 5 | enum {re, im}; //real and imaginary 6 | extern volatile int keeprunning; 7 | extern const int BUCKETS; 8 | 9 | 10 | void setupDFTForSound(Visualizer_Pkg_ptr vis_pkg_ptr, Uint8* buffer, int bytesRead) 11 | { 12 | int bytewidth = vis_pkg_ptr->bitsize / 8; 13 | SDL_AudioSpec* wavSpec = GetSDL_AudioSpec(vis_pkg_ptr); 14 | int channels = wavSpec->channels; 15 | SDL_AudioFormat fmt = wavSpec->format; 16 | int frames = bytesRead / (bytewidth * channels); 17 | struct FFTWop* fftwop = GetFFTWop(vis_pkg_ptr); 18 | int count = 0, c; 19 | 20 | for(c = 0; c < channels; ++c){ 21 | fftwop[c].p = fftw_plan_dft_1d(frames, fftwop[c].in, 22 | fftwop[c].out, FFTW_FORWARD, FFTW_MEASURE); 23 | } 24 | 25 | while(count < frames){ 26 | for(c = 0; c< channels; ++c){ 27 | fftwop[c].in[count][re] = vis_pkg_ptr->GetAudioSample(buffer, fmt); 28 | fftwop[c].in[count][im] = 0.0; 29 | 30 | buffer+=bytewidth; 31 | } 32 | count++; 33 | } 34 | } 35 | 36 | int getFileSize(FILE *inFile) 37 | { 38 | int fileSize = 0; 39 | fseek(inFile,0,SEEK_END); 40 | 41 | fileSize=ftell(inFile); 42 | 43 | fseek(inFile,0,SEEK_SET); 44 | 45 | return fileSize; 46 | } 47 | 48 | void processWAVFile(Uint32 wavLength, int buffer_size, Visualizer_Pkg_ptr vis_pkg_ptr) 49 | { 50 | struct FFTWop* dft = GetFFTWop(vis_pkg_ptr); 51 | int channels = GetSDL_AudioSpec(vis_pkg_ptr)->channels; 52 | 53 | FILE* wavFile = fopen(vis_pkg_ptr->filename, "r"); 54 | int filesize = getFileSize(wavFile); 55 | 56 | Uint8* buffer = (Uint8*)malloc(buffer_size*sizeof(Uint8)); 57 | 58 | size_t bytesRead; 59 | int packet_index = 0, i; 60 | //Skip header information in .WAV file 61 | bytesRead = fread(buffer, sizeof buffer[0], filesize-wavLength, wavFile); 62 | 63 | //Reading actual audio data 64 | while ((bytesRead = fread(buffer, sizeof buffer[0], 65 | buffer_size/sizeof(buffer[0]), wavFile)) > 0){ 66 | 67 | vis_pkg_ptr->setupDFT(vis_pkg_ptr, buffer, bytesRead); 68 | for(i = 0; i < channels; ++i){ 69 | 70 | fftw_execute(dft[i].p); 71 | analyze_FFTW_Results(vis_pkg_ptr, dft[i], packet_index, i ,bytesRead); 72 | fftw_destroy_plan(dft[i].p); 73 | } 74 | packet_index++; 75 | } 76 | 77 | /*MEMORY MANAGEMENT*/ 78 | free(buffer); 79 | for(i = 0; iwavSpec_ptr->freq / 2; 98 | double freq_bin[] = {19.0, 140.0, 400.0, 2600.0, 5200.0, nyquist }; 99 | 100 | SDL_AudioSpec* wavSpec = GetSDL_AudioSpec(packet); 101 | 102 | int frames = bytesRead / (wavSpec->channels * packet->bitsize / 8); 103 | struct FFTW_Results* results = GetFFTW_Results(packet); 104 | 105 | 106 | for(i = 0; ifreq / frames; 114 | 115 | for (i = 0; i < BUCKETS; ++i){ 116 | if((freq>freq_bin[i]) && (freq <=freq_bin[i+1])){ 117 | if (magnitude > peakmaxArray[i]){ 118 | peakmaxArray[i] = magnitude; 119 | } 120 | } 121 | } 122 | 123 | if(magnitude > peakmax){ 124 | peakmax = magnitude; 125 | max_index = j; 126 | } 127 | } 128 | 129 | results[packet_index].peakpower[ch] = 10*(log10(peakmax)); 130 | results[packet_index].peakfreq[ch] = max_index*(double)wavSpec->freq/frames; 131 | 132 | for(i = 0; i< BUCKETS; ++i){ 133 | results[packet_index].peakmagMatrix[ch][i]=10*(log10(peakmaxArray[i])); 134 | } 135 | 136 | free(peakmaxArray); 137 | } 138 | -------------------------------------------------------------------------------- /dataprocessing.h: -------------------------------------------------------------------------------- 1 | #ifndef DATAPROCESSING_H 2 | #define DATAPROCESSING_H 3 | 4 | #include "audioInformation.h" 5 | #include "SDL2/SDL.h" 6 | #include 7 | 8 | int getFileSize(FILE *inFile); 9 | void analyze_FFTW_Results(Visualizer_Pkg_ptr, struct FFTWop, int, int,size_t); 10 | void setupDFTForSound(Visualizer_Pkg_ptr, Uint8*, int ); 11 | void processWAVFile(Uint32 , int , Visualizer_Pkg_ptr ); 12 | 13 | #endif -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | visualizer-service: 4 | build: 5 | context: ./ 6 | args: 7 | - NO_CACHE=--no-cache 8 | environment: 9 | - XDG_RUNTIME_DIR=/tmp 10 | image: music-visualizer:0.0.1 11 | container_name: visualizer-container 12 | volumes: 13 | - "${ENV_MUSIC_PATH}:/opt/visualizer/Music" 14 | - /run/user/1000/pulse/native:/tmp/pulse/native 15 | devices: 16 | - /dev/snd 17 | stdin_open: true 18 | command: /bin/bash 19 | tty: true 20 | restart: "no" 21 | 22 | -------------------------------------------------------------------------------- /fftw-3.3.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crelloc/Music-Visualizer-Reboot/5adb1f7d8ac768957629162318cc9e0d75a0c0f9/fftw-3.3.4.tar.gz -------------------------------------------------------------------------------- /rebooted.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crelloc/Music-Visualizer-Reboot/5adb1f7d8ac768957629162318cc9e0d75a0c0f9/rebooted.gif -------------------------------------------------------------------------------- /visualize.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 2 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "audioInformation.h" 13 | #include "dataprocessing.h" 14 | 15 | #define EXIT_IF(EXP, device) \ 16 | do { if (EXP){\ 17 | fprintf(stderr, "Failed to allocate memory!\n"); \ 18 | SDL_CloseAudioDevice(device); \ 19 | SDL_Quit(); \ 20 | exit(EXIT_FAILURE);} } \ 21 | while (0) 22 | 23 | /*GLOBAL VARIABLES*/ 24 | volatile int keeprunning = 1; 25 | volatile int packet_pos = 0; 26 | volatile int print_spectrum = 0; 27 | static volatile int time_to_exit = 0; 28 | const int BUCKETS = 5; 29 | char* FILE_PATH; 30 | 31 | static int InitializePackage(SDL_AudioSpec* wavSpec, Uint8* wavStart, Uint32 wavLength, 32 | Visualizer_Pkg_ptr vis_pkg) 33 | { 34 | int ret = 0; 35 | struct AudioData* AudioData_t = malloc(sizeof(struct AudioData)); 36 | if(!AudioData_t){ 37 | fprintf(stderr, "Failed to allocate memory\n"); 38 | ret = 1; 39 | return ret; 40 | } 41 | AudioData_t->currentPos = wavStart; 42 | AudioData_t->wavStart = wavStart; 43 | AudioData_t->wavLength = wavLength; 44 | AudioData_t->currentLength = wavLength; 45 | 46 | wavSpec->callback = MyAudioCallback; 47 | wavSpec->userdata = &(*vis_pkg); 48 | 49 | vis_pkg->filename = FILE_PATH; 50 | vis_pkg->AudioData_ptr = AudioData_t; 51 | vis_pkg->wavSpec_ptr = wavSpec; 52 | return ret; 53 | } 54 | 55 | static void Verify_Audio_Spec(SDL_AudioSpec** wavSpec, SDL_AudioSpec have) 56 | { 57 | 58 | if((*wavSpec)->format != have.format){ 59 | printf("original sample format: %d\n" 60 | "new sample format: %d\n", (*wavSpec)->format, have.format); 61 | (*wavSpec)->format = have.format; 62 | } 63 | if((*wavSpec)->channels != have.channels){ 64 | printf("original sample channels: %d\n" 65 | "new sample channels: %d\n", (*wavSpec)->channels, have.channels); 66 | (*wavSpec)->channels = have.channels; 67 | } 68 | if((*wavSpec)->samples != have.samples ){ 69 | printf("original sample size: %d\n" 70 | "new sample size: %d\n", (*wavSpec)->samples, have.samples); 71 | (*wavSpec)->samples = have.samples; 72 | } 73 | } 74 | 75 | static struct FFTWop* allocate_FFTWop(int channels, int frame_size) 76 | { 77 | int i; 78 | struct FFTWop* fftw_ptr = malloc(channels*sizeof(struct FFTWop)); 79 | if(!fftw_ptr) exit(EXIT_FAILURE); 80 | //allocating space for dft operations 81 | for(i=0; ibitsize / 8 ; 128 | sizeof_packet *= wavSpec->channels; 129 | sizeof_packet *= wavSpec->samples; 130 | printf("buffer size per packet [bytes]: %d\n", sizeof_packet); 131 | 132 | //find the total number of packets 133 | //wavLength is the size of audio data in bytes 134 | audio = GetAudioData(vis_pkg); 135 | return (int)ceil((float)audio->wavLength/sizeof_packet); 136 | } 137 | 138 | 139 | static int Get_total_frames(struct Visualizer_Pkg* vis_pkg, int channels) 140 | { 141 | struct AudioData* audio; 142 | audio = GetAudioData(vis_pkg); 143 | return audio->wavLength /= ((vis_pkg->bitsize/8) * channels); 144 | } 145 | 146 | static double (*set_audio_sample_function(int bitsize))(Uint8 *, SDL_AudioFormat) 147 | { 148 | 149 | return (bitsize == 8 ) ? Get8bitAudioSample 150 | : (bitsize == 16) ? Get16bitAudioSample 151 | : (bitsize == 32) ? Get32bitAudioSample 152 | : NULL; 153 | } 154 | 155 | 156 | static void InitializeVariables(struct Visualizer_Pkg* vis_pkg, SDL_AudioSpec have, 157 | SDL_AudioDeviceID device) 158 | { 159 | SDL_AudioSpec* wavSpec; 160 | 161 | wavSpec = GetSDL_AudioSpec(vis_pkg); 162 | Verify_Audio_Spec(&wavSpec, have); 163 | vis_pkg->bitsize = (int)SDL_AUDIO_BITSIZE(wavSpec->format); 164 | vis_pkg->GetAudioSample = set_audio_sample_function(vis_pkg->bitsize); 165 | assert(vis_pkg->GetAudioSample != NULL); 166 | 167 | vis_pkg->device = device; 168 | vis_pkg->setupDFT = setupDFTForSound; 169 | vis_pkg->total_packets = Get_totalpackets(vis_pkg, wavSpec); 170 | 171 | 172 | //A frame can consist of N channels 173 | vis_pkg->total_frames = Get_total_frames(vis_pkg, wavSpec->channels); 174 | vis_pkg->frame_size = wavSpec->samples; 175 | 176 | //FFTW Results for each packet 177 | vis_pkg->FFTW_Results_ptr = allocate_FFTW_Results(wavSpec->channels, vis_pkg->total_packets); 178 | //EXIT_IF(ret==-1, device); 179 | vis_pkg->fftw_ptr = allocate_FFTWop(wavSpec->channels, vis_pkg->frame_size); 180 | //EXIT_IF(ret==-1, device); 181 | 182 | printf("%d bit data samples\n", vis_pkg->bitsize); 183 | printf("total packets: %d\n", vis_pkg->total_packets); 184 | printf("number of frames per packet: %d\n", vis_pkg->frame_size); 185 | printf("total frames: %d\n", vis_pkg->total_frames); 186 | printf("FILE_PATH: %s\n", vis_pkg->filename); 187 | printf("\nPress ENTER to continue:\n"); 188 | fflush(stdout); 189 | 190 | while(getchar() != 0xa && keeprunning); 191 | } 192 | 193 | 194 | /*TRAP FUNCTION*/ 195 | static void aborted(int sig) 196 | { 197 | printf("\nAborted by signal: %d\n", sig); 198 | keeprunning = 0; 199 | } 200 | 201 | int main(int argc, char** argv) 202 | { 203 | 204 | int opt, i, j; 205 | SDL_AudioSpec wavSpec, have; 206 | SDL_AudioDeviceID device; 207 | Uint8* wavStart; 208 | Uint32 wavLength; 209 | struct Visualizer_Pkg* vis_pkg; 210 | 211 | while((opt = getopt(argc, argv, ":f:")) != -1){ 212 | 213 | switch(opt){ 214 | case 'f': 215 | FILE_PATH = optarg; 216 | break; 217 | case ':': 218 | printf("option needs a value\n"); 219 | goto usage; 220 | case '?': 221 | printf("unknown option: %c\n", optopt); 222 | goto usage; 223 | default: 224 | usage: printf("usage %s [-f] \'PATH/TO/FILE\']\n",argv[0]); 225 | return 1; 226 | } 227 | } 228 | if (optind != argc) goto usage; 229 | 230 | (void) signal(SIGINT, aborted); 231 | (void) signal(SIGTSTP, aborted); 232 | 233 | SDL_Init(SDL_INIT_AUDIO); 234 | if(SDL_LoadWAV(FILE_PATH, &wavSpec, &wavStart, &wavLength) == NULL) 235 | { 236 | // TODO: Proper error handling 237 | SDL_Log("Failed to load wav file: %s", SDL_GetError()); 238 | return 1; 239 | } 240 | 241 | vis_pkg = malloc(sizeof(struct Visualizer_Pkg)); 242 | if(!vis_pkg) goto EXIT; 243 | i = InitializePackage(&wavSpec, wavStart, wavLength, vis_pkg); 244 | if(i) goto EXIT; 245 | 246 | device = SDL_OpenAudioDevice(NULL, 0, &wavSpec, &have, 247 | SDL_AUDIO_ALLOW_ANY_CHANGE); 248 | 249 | if(device == 0){ 250 | SDL_Log("Failed to open audio: %s", SDL_GetError()); 251 | return 1; 252 | } 253 | 254 | InitializeVariables(vis_pkg, have, device); 255 | processWAVFile(wavLength, have.size ,vis_pkg); 256 | 257 | SDL_PauseAudioDevice(device, 0); //play song 258 | 259 | while(GetAudioData(vis_pkg)->currentLength > 0 && keeprunning); 260 | 261 | 262 | //Free vis_pkg data 263 | for(i=0; itotal_packets; ++i){ 264 | free(vis_pkg->FFTW_Results_ptr[i].peakfreq); 265 | free(vis_pkg->FFTW_Results_ptr[i].peakpower); 266 | for(j=0; jFFTW_Results_ptr[i].peakmagMatrix[j]); 268 | } 269 | free(vis_pkg->FFTW_Results_ptr[i].peakmagMatrix); 270 | } 271 | free(vis_pkg->FFTW_Results_ptr); 272 | 273 | SDL_CloseAudioDevice(device); 274 | EXIT: 275 | SDL_FreeWAV(wavStart); 276 | SDL_Quit(); 277 | 278 | printf("goodbye.\n"); 279 | return 0; 280 | } 281 | --------------------------------------------------------------------------------