diff --git a/.gitignore b/.gitignore index 259148f..aa77b1f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,7 @@ *.exe *.out *.app + +MusicVisualizer/bin/ +MusicVisualizer/MusicVisualizer.xcodeproj/ +MusicVisualizer/obj/ \ No newline at end of file diff --git a/MusicVisualizer/Makefile b/MusicVisualizer/Makefile new file mode 100644 index 0000000..a1425e7 --- /dev/null +++ b/MusicVisualizer/Makefile @@ -0,0 +1,13 @@ +# Attempt to load a config.make file. +# If none is found, project defaults in config.project.make will be used. +ifneq ($(wildcard config.make),) + include config.make +endif + +# make sure the the OF_ROOT location is defined +ifndef OF_ROOT + OF_ROOT=$(realpath ../../../of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release) +endif + +# call the project makefile! +include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk diff --git a/MusicVisualizer/Project.xcconfig b/MusicVisualizer/Project.xcconfig new file mode 100644 index 0000000..4c2b6be --- /dev/null +++ b/MusicVisualizer/Project.xcconfig @@ -0,0 +1,17 @@ +//THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT. +//THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED +OF_PATH = ../../../of_v0.9.8_osx_release + +//THIS HAS ALL THE HEADER AND LIBS FOR OF CORE +#include "../../../of_v0.9.8_osx_release/libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig" + +//ICONS - NEW IN 0072 +ICON_NAME_DEBUG = icon-debug.icns +ICON_NAME_RELEASE = icon.icns +//ICON_FILE_PATH = $(OF_PATH)/libs/openFrameworksCompiled/project/osx/ + +//IF YOU WANT AN APP TO HAVE A CUSTOM ICON - PUT THEM IN YOUR DATA FOLDER AND CHANGE ICON_FILE_PATH to: +ICON_FILE_PATH = bin/data/ + +OTHER_LDFLAGS = $(OF_CORE_LIBS) $(OF_CORE_FRAMEWORKS) +HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS) diff --git a/MusicVisualizer/addons.make b/MusicVisualizer/addons.make new file mode 100644 index 0000000..7a6e880 --- /dev/null +++ b/MusicVisualizer/addons.make @@ -0,0 +1,3 @@ +ofxGui +ofxHistoryPlot +ofxPostProcessing diff --git a/MusicVisualizer/config.make b/MusicVisualizer/config.make new file mode 100644 index 0000000..1570e80 --- /dev/null +++ b/MusicVisualizer/config.make @@ -0,0 +1,142 @@ +################################################################################ +# CONFIGURE PROJECT MAKEFILE (optional) +# This file is where we make project specific configurations. +################################################################################ + +################################################################################ +# OF ROOT +# The location of your root openFrameworks installation +# (default) OF_ROOT = ../../../of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release +################################################################################ +# OF_ROOT = ../../../of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release/of_v0.9.8_osx_release + +################################################################################ +# PROJECT ROOT +# The location of the project - a starting place for searching for files +# (default) PROJECT_ROOT = . (this directory) +# +################################################################################ +# PROJECT_ROOT = . + +################################################################################ +# PROJECT SPECIFIC CHECKS +# This is a project defined section to create internal makefile flags to +# conditionally enable or disable the addition of various features within +# this makefile. For instance, if you want to make changes based on whether +# GTK is installed, one might test that here and create a variable to check. +################################################################################ +# None + +################################################################################ +# PROJECT EXTERNAL SOURCE PATHS +# These are fully qualified paths that are not within the PROJECT_ROOT folder. +# Like source folders in the PROJECT_ROOT, these paths are subject to +# exlclusion via the PROJECT_EXLCUSIONS list. +# +# (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXTERNAL_SOURCE_PATHS = + +################################################################################ +# PROJECT EXCLUSIONS +# These makefiles assume that all folders in your current project directory +# and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations +# to look for source code. The any folders or files that match any of the +# items in the PROJECT_EXCLUSIONS list below will be ignored. +# +# Each item in the PROJECT_EXCLUSIONS list will be treated as a complete +# string unless teh user adds a wildcard (%) operator to match subdirectories. +# GNU make only allows one wildcard for matching. The second wildcard (%) is +# treated literally. +# +# (default) PROJECT_EXCLUSIONS = (blank) +# +# Will automatically exclude the following: +# +# $(PROJECT_ROOT)/bin% +# $(PROJECT_ROOT)/obj% +# $(PROJECT_ROOT)/%.xcodeproj +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXCLUSIONS = + +################################################################################ +# PROJECT LINKER FLAGS +# These flags will be sent to the linker when compiling the executable. +# +# (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ + +# Currently, shared libraries that are needed are copied to the +# $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to +# add a runtime path to search for those shared libraries, since they aren't +# incorporated directly into the final executable application binary. +# TODO: should this be a default setting? +# PROJECT_LDFLAGS=-Wl,-rpath=./libs + +################################################################################ +# PROJECT DEFINES +# Create a space-delimited list of DEFINES. The list will be converted into +# CFLAGS with the "-D" flag later in the makefile. +# +# (default) PROJECT_DEFINES = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_DEFINES = + +################################################################################ +# PROJECT CFLAGS +# This is a list of fully qualified CFLAGS required when compiling for this +# project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS +# defined in your platform specific core configuration files. These flags are +# presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below. +# +# (default) PROJECT_CFLAGS = (blank) +# +# Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in +# your platform specific configuration file will be applied by default and +# further flags here may not be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CFLAGS = + +################################################################################ +# PROJECT OPTIMIZATION CFLAGS +# These are lists of CFLAGS that are target-specific. While any flags could +# be conditionally added, they are usually limited to optimization flags. +# These flags are added BEFORE the PROJECT_CFLAGS. +# +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank) +# +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank) +# +# Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the +# PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration +# file will be applied by default and further optimization flags here may not +# be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE = +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG = + +################################################################################ +# PROJECT COMPILERS +# Custom compilers can be set for CC and CXX +# (default) PROJECT_CXX = (blank) +# (default) PROJECT_CC = (blank) +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CXX = +# PROJECT_CC = diff --git a/MusicVisualizer/openFrameworks-Info.plist b/MusicVisualizer/openFrameworks-Info.plist new file mode 100644 index 0000000..8d64d2b --- /dev/null +++ b/MusicVisualizer/openFrameworks-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + cc.openFrameworks.ofapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CFBundleIconFile + ${ICON} + + diff --git a/MusicVisualizer/src/main.cpp b/MusicVisualizer/src/main.cpp new file mode 100644 index 0000000..9bcdc62 --- /dev/null +++ b/MusicVisualizer/src/main.cpp @@ -0,0 +1,13 @@ +#include "ofMain.h" +#include "ofApp.h" + +//======================================================================== +int main( ){ + ofSetupOpenGL(1500,750,OF_WINDOW); // <-------- setup the GL context + + // this kicks off the running of my app + // can be OF_WINDOW or OF_FULLSCREEN + // pass in width and height too: + ofRunApp(new ofApp()); + +} diff --git a/MusicVisualizer/src/ofApp.cpp b/MusicVisualizer/src/ofApp.cpp new file mode 100644 index 0000000..11836fa --- /dev/null +++ b/MusicVisualizer/src/ofApp.cpp @@ -0,0 +1,511 @@ +#include "ofApp.h" +#include + +//-------------------------------------------------------------- +void ofApp::setup(){ + ofSetDataPathRoot("../Resources/data/"); + filePicked = chooseFile(); + beat.play(); + beat.setLoop(true); + time.load("Raleway-Regular.ttf", 12); + settings.load("Raleway-Regular.ttf", 12); + + fftSmooth = new float [BANDS]; + for (int i = 0; i < BANDS; i++) { + fftSmooth[i] = 0; + } + + //set random positions and movements + for (int i = 0; i < BANDS; i++) { + xValues.push_back(rand() % ofGetWidth()); + int tempHeight = rand() % ofGetHeight(); + if (tempHeight < OVERLAP) { + tempHeight += OVERLAP; + } + yValues.push_back(tempHeight); + movePattern.push_back(rand() % DIRECTIONS); + } + duration = beat.length / beat.internalFreq; + + logo.load("pb_logo.png"); + bg.load("backgrounds/castle.jpg"); + + //create post processing effects + post.init(ofGetWidth(), ofGetHeight()); + post.createPass(); + + zoomPost.init(ofGetWidth(), ofGetHeight()); + zoomPost.createPass(); +} + +//-------------------------------------------------------------- +void ofApp::update(){ + //DERIVED FROM soundPlayerFFTExample within of/examples/sound/ + ofSoundUpdate(); + + //update frequency values with smoothing factor + float * value = ofSoundGetSpectrum(BANDS); + for (int i = 0; i < BANDS; i++) { + fftSmooth[i] *= smoothing; + if (fftSmooth[i] < value[i]) { + fftSmooth[i] = value[i]; + } + } +} + +//-------------------------------------------------------------- +void ofApp::draw(){ + ofBackground(backgroundColor); + + ofSetColor(MAX_COLOR); + + + if (fftSmooth[BASS_FFT] * (smoothMultiplier) > 75) { + zoomPost.begin(); + } else { + post.begin(); + } + + if (pianoSong) { + drawCircles(MAX_COLOR / pianoMult); + } else { + drawCircles(MAX_COLOR); + } + + if (fftSmooth[BASS_FFT] * (smoothMultiplier) > 75) { + zoomPost.end(); + } else { + post.end(); + } + + //non-rainbow preset, show transparent background + if (!rainbowSpec) { + ofSetColor(MAX_COLOR, MAX_COLOR, MAX_COLOR, 2 * MAX_COLOR / 3); + bg.draw(0, 0, ofGetWidth(), ofGetHeight()); + } + + ofSetColor(MAX_COLOR); + + if (showGui) { + gui.draw(); + } + + if (showHist) { + updateHistory(); + } + + drawRects(); + + if (showLogo) { + if (swapColors) { + //automatically switch base colors with time delay + if (fftSmooth[BASS_FFT] * (smoothMultiplier) > 50 && colorSwitchGap > 25) { + colorTint = rand() % 255; + colorSwitchGap = 0; + } + colorSwitchGap++; + } + + ofSetColor(MAX_COLOR); + + logo.setAnchorPoint(logo.getWidth() / 4, logo.getHeight() / 4); + logo.draw((ofGetWidth() - fftSmooth[BASS_FFT] * smoothMultiplier) / 2, (ofGetHeight() - fftSmooth[BASS_FFT] * smoothMultiplier) / 2 + (TOP_PANEL_HEIGHT), (logo.getWidth() / 2) + fftSmooth[BASS_FFT] * smoothMultiplier, (logo.getHeight() / 2) + fftSmooth[BASS_FFT] * (smoothMultiplier)); + } + drawUI(); +} + +//-------------------------------------------------------------- +void ofApp::keyPressed(int key){ + switch (key){ + case ' ': + //play/stop song + if(beat.isPlaying()){ + beat.stop(); + } else { + beat.play(); + } + break; + case OF_KEY_RIGHT: + //fast forward song by ten seconds + beat.setPositionMS( beat.getPositionMS() + TIMESHIFT); + break; + case OF_KEY_LEFT: + //rewind song by ten seconds + beat.setPositionMS( beat.getPositionMS() - TIMESHIFT); + break; + case 's': + //toggle GUI + showGui = !showGui; + break; + case 'h': + //toggle history graph + showHist = !showHist; + break; + case 'n': + //choose new song + beat.stop(); + filePicked = chooseFile(); + beat.play(); + beat.setLoop(true); + break; + case 'p': + //toggle piano preset + pianoSong = !pianoSong; + break; + case 'g': + //toggle guide text + showGuide = !showGuide; + break; + case 'l': + //toggle logo display + showLogo = !showLogo; + break; + case 'b': + //choose new background + beat.stop(); + bgPicked = chooseBG(); + beat.play(); + beat.setLoop(true); + break; + case '1': + //manually change base colors + colorTint = rand() % (MAX_COLOR / 4); + break; + case '2': + colorTint = (rand() % (MAX_COLOR / 4)) + (MAX_COLOR / 4); + break; + case '3': + colorTint = (rand() % (MAX_COLOR / 4)) + (MAX_COLOR / 2); + break; + case '4': + colorTint = (rand() % (MAX_COLOR / 4)) + ((3 * MAX_COLOR) / 4); + break; + case 'r': + //toggle rainbow preset + rainbowSpec = !rainbowSpec; + break; + + } +} + +//-------------------------------------------------------------- +float ofApp::setXY(int i) { + float xPos; + float yPos; + float radius = (fftSmooth[i] * (smoothMultiplier / 16)) * log(i + 0.1) + MIN_SIZE; + + float beatMult = beatSpeed; + + //increase speed of larger particles + if (radius > fastThreshold) { + beatMult = beatSpeed * 4; + } else if (radius > fastThreshold / 2) { + beatMult = beatSpeed * 2; + } + + switch (movePattern[i]) { + case 0: + xPos = (xValues[i] - beatMult); + yPos = (yValues[i] - beatMult); + break; + case 1: + xPos = (xValues[i] - beatMult); + yPos = (yValues[i]); + break; + case 2: + xPos = (xValues[i] - beatMult); + yPos = (yValues[i] + beatMult); + break; + case 3: + xPos = (xValues[i]); + yPos = (yValues[i] + beatMult); + break; + case 4: + xPos = (xValues[i] + beatMult); + yPos = (yValues[i] + beatMult); + break; + case 5: + xPos = (xValues[i] + beatMult); + yPos = (yValues[i]); + break; + case 6: + xPos = (xValues[i] + beatMult); + yPos = (yValues[i] - beatMult); + break; + default: + xPos = (xValues[i]); + yPos = (yValues[i] - beatMult); + break; + } + + //move particle to other side of screen if off-screen + if (xPos < 0) { + xPos+=ofGetWidth(); + } + if (yPos < 0) { + yPos+=ofGetHeight(); + } + if (xPos > ofGetWidth()) { + xPos-=ofGetWidth(); + } + if (yPos > ofGetHeight()) { + yPos-=ofGetHeight(); + } + + xValues[i] = xPos; + yValues[i] = yPos; + + return radius; +} + +//-------------------------------------------------------------- +void ofApp::drawCircles(int count) { + for (int i = 0; i < count; i++) { + ofColor currentColor; + + //color based on rainbow and piano presets + if (rainbowSpec && pianoSong) { + currentColor.setHsb(i * pianoMult, (fftSmooth[i] * 500 + 1) / 1.2, (fftSmooth[i] * 500 + 1) / 1.2); + } else if (rainbowSpec) { + currentColor.setHsb(i, (fftSmooth[i] * 500 + 1) / 1.2, (fftSmooth[i] * 500 + 1) / 1.2); + } else { + currentColor.setHsb(colorTint, colorTint, (MAX_COLOR / 2) + ((fftSmooth[i] * 500 + 1) / 1.2)); + } + + float radius = setXY(i); + + ofSetColor(currentColor, MAX_COLOR); + + if (radius > minShow) { + ofSetCircleResolution(circleRes); + ofDrawCircle(xValues[i], yValues[i], radius); + } + } +} + +//-------------------------------------------------------------- +void ofApp::drawRects() { + int whiteCount = 0; + if (pianoSong) { + for (int i = 0; i < MAX_COLOR / pianoMult; i++) { + ofColor currentColor; + + if (rainbowSpec) { + currentColor.setHsb(i * pianoMult, MAX_COLOR, MAX_COLOR); + } else { + currentColor.setHsb(colorTint, colorTint, (MAX_COLOR / 2) + whiteCount * pianoMult); + } + ofSetColor(currentColor); + + float height = (fftSmooth[i] * smoothMultiplier) * log(i + 1) + MIN_SIZE; + if (i < 2) { + height = (fftSmooth[i] * smoothMultiplier) + MIN_SIZE; + } + if (!rainbowSpec && height > minShow * 10) { + //color rainbow if height large enough + currentColor.setHsb(i * pianoMult, MAX_COLOR, (MAX_COLOR / 2) + whiteCount * pianoMult); + ofSetColor(currentColor); + } + + float width = 9; + + ofDrawRectRounded(i * 2 * width, 25, width, height, 4); + + whiteCount++; + } + } else { + for (int i = 0; i < MAX_COLOR; i++) { + ofColor currentColor; + + if (rainbowSpec) { + currentColor.setHsb(i, MAX_COLOR, MAX_COLOR); + } else { + currentColor.setHsb(colorTint, colorTint, (MAX_COLOR / 2) + whiteCount); + } + ofSetColor(currentColor); + //display every second frequency + if (i % 2 == 0) { + float height = (fftSmooth[i] * smoothMultiplier) * log(i + 1) + MIN_SIZE; + if (i < 2) { + height = (fftSmooth[i] * smoothMultiplier) + MIN_SIZE; + } + float width = 8; + + if (!rainbowSpec && height > minShow * 10) { + //color rainbow if height large enough + currentColor.setHsb(i, MAX_COLOR, (MAX_COLOR / 2) + whiteCount); + ofSetColor(currentColor); + } + + ofDrawRectRounded((i / 1.4) * width, 25, width, height, 4); + + whiteCount++; + } + } + } +} + +//-------------------------------------------------------------- +void ofApp::makeGui() { + if (firstTime) { + gui.setup("panel"); + } + gui.setPosition(ofPoint(ofGetWidth() - 300, 150)); + + colors.setName("Color Settings"); + colors.add(backgroundColor.set("Background Color", 25, 0, MAX_COLOR)); + colors.add(lineColor.set("Line Color", 100, 0, MAX_COLOR)); + colors.add(timeColor.set("Time Color", 200, 0, MAX_COLOR)); + colors.add(topColor.set("Panel Color", 50, 0, MAX_COLOR)); + colors.add(colorTint.set("Color Tint", 20, 0, MAX_COLOR)); + colors.add(swapColors.set("Light Show", false)); + + visuals.setName("Visualization Settings"); + visuals.add(smoothing.set("Smoothing", 0.9, 0.01, 0.99)); + visuals.add(smoothMultiplier.set("Smooth Multiplier", 120, 1, 999)); + visuals.add(beatSpeed.set("Beat Speed", 0.5, 0, 10)); + visuals.add(fastThreshold.set("Fast Threshold", 20, 1, 50)); + visuals.add(minShow.set("Minimum Circle Radius", 12, 1, 200)); + + uiPanel.setName("UI Settings"); + uiPanel.add(showHist.set("Show History 'H'", false)); + uiPanel.add(pianoSong.set("Piano Preset 'P'", false)); + uiPanel.add(showGuide.set("Show Guide 'G'", true)); + uiPanel.add(showLogo.set("Show Logo 'L'", true)); + uiPanel.add(rainbowSpec.set("Rainbow Spectrum 'R'", false)); + + if (firstTime) { + //only draw gui groups once + firstTime = false; + gui.add(colors); + gui.add(visuals); + gui.add(uiPanel); + } +} + +//-------------------------------------------------------------- +void ofApp::makeHistory() { + ofEnableSmoothing(); + ofEnableAlphaBlending(); + ofSetVerticalSync(true); + ofSetFrameRate(60); + + for (int i = 0; i < totalPlots; i++) { + int numSamples = 600; + ofxHistoryPlot* plot = new ofxHistoryPlot( NULL, "", numSamples, false); + plot->setRange(0, 3); + plot->setShowNumericalInfo(false); //don't show text + plot->setLineWidth(3); + plot->setBackgroundColor(ofColor(backgroundColor, 0)); //transparent background + plot->setDrawGrid(false); + plots.push_back(plot); + } +} + +//-------------------------------------------------------------- +void ofApp::updateHistory() { + int plotPoint = 20; + int plotMult = 1; + for (ofxHistoryPlot* plot : plots) { + plot->update(fftSmooth[plotPoint * plotMult]); + ofColor col; + col.setHsb(plotPoint * plotMult, MAX_COLOR, MAX_COLOR); + plot->setColor(col); + plot->draw(0, ofGetHeight() - 250, ofGetWidth(), 240); + plotMult++; + } +} + +//-------------------------------------------------------------- +void ofApp::resetup() { + time.load("Raleway-Regular.ttf", 12); + settings.load("Raleway-Regular.ttf", 12); + + fftSmooth = new float [BANDS]; + for (int i = 0; i < BANDS; i++) { + fftSmooth[i] = 0; + } + + ofBackground(backgroundColor); + + for (int i = 0; i < BANDS; i++) { + xValues.push_back(rand() % ofGetWidth()); + int tempHeight = rand() % ofGetHeight(); + if (tempHeight < OVERLAP) { + tempHeight += OVERLAP; + } + yValues.push_back(tempHeight); + movePattern.push_back(rand() % DIRECTIONS); + } + duration = beat.length / beat.internalFreq; + + makeGui(); + + plots.clear(); //remove previous graphs + makeHistory(); +} + +//-------------------------------------------------------------- +bool ofApp::chooseFile() { + //Open the Open File Dialog + ofFileDialogResult openFileResult= ofSystemLoadDialog("Select a new audio file"); + + //Check if the user opened a file + if (openFileResult.bSuccess){ + ofFile file(openFileResult.getPath()); + if (ofToUpper(file.getExtension()) == "MP3" || ofToUpper(file.getExtension()) == "WAV") { + beat.load(openFileResult.getPath()); + resetup(); + return true; + } + } + return false; +} + +//-------------------------------------------------------------- +bool ofApp::chooseBG() { + //Open the Open File Dialog + ofFileDialogResult openFileResult= ofSystemLoadDialog("Select a new background file"); + + //Check if the user opened a file + if (openFileResult.bSuccess){ + ofFile file(openFileResult.getPath()); + if (ofToUpper(file.getExtension()) == "PNG" || ofToUpper(file.getExtension()) == "JPG") { + bg.load(openFileResult.getPath()); + resetup(); + return true; + } + } + return false; +} + +//-------------------------------------------------------------- +void ofApp::drawUI() { + ofSetColor(topColor); + ofDrawRectangle(0, 0, ofGetWidth(), TOP_PANEL_HEIGHT); + + ofSetColor(MAX_COLOR); + + std::string timeStr = ofToString(beat.getPositionMS() / SECOND / MINUTE); //minutes + if ((beat.getPositionMS() / SECOND) % MINUTE < 10) { + timeStr += ":0" + ofToString((beat.getPositionMS() / SECOND) % MINUTE); //seconds if less than ten + } else { + timeStr += ":" + ofToString((beat.getPositionMS() / SECOND) % MINUTE); //seconds + } + time.drawString(timeStr, WORD_WIDTH - 5, WORD_HEIGHT); + + if (showGuide) { + settings.drawString("settings = 's'", ofGetWidth() - WORD_WIDTH + 5, WORD_HEIGHT); + settings.drawString("new song = 'n'", 5, WORD_HEIGHT); + } + + //static timeline length + ofSetLineWidth(10); + ofSetColor(lineColor); + ofDrawLine(TOP_PANEL_WIDTH + 5, 12, ofGetWidth() - TOP_PANEL_WIDTH, 12); + + //movement based on time duration + ofSetColor(timeColor); + msecondGain = (ofGetWidth() - (TOP_PANEL_WIDTH * 2)) / (duration * SECOND); + ofDrawCircle(TOP_PANEL_WIDTH + msecondGain * beat.getPositionMS(), WORD_HEIGHT - 5, MIN_SIZE - 1 + fftSmooth[BASS_FFT] * 10); +} diff --git a/MusicVisualizer/src/ofApp.h b/MusicVisualizer/src/ofApp.h new file mode 100644 index 0000000..f32bff0 --- /dev/null +++ b/MusicVisualizer/src/ofApp.h @@ -0,0 +1,102 @@ +#pragma once + +#include "ofMain.h" +#include "ofxGui.h" +#include "ofxHistoryPlot.h" +#include "ofxPostProcessing.h" +#include + +class ofApp : public ofBaseApp{ + +public: + void setup(); + void update(); + void draw(); + + void keyPressed(int key); + float setXY(int i); + void drawCircles(int count); + void drawRects(); + void makeGui(); + void makeHistory(); + void updateHistory(); + void resetup(); + bool chooseFile(); + bool chooseBG(); + void drawUI(); + + ofFmodSoundPlayer beat; + ofTrueTypeFont time; + ofTrueTypeFont settings; + + std::vector plots; + int totalPlots = 6; + + ofxPanel gui; + + ofParameterGroup colors; + ofParameter backgroundColor; + ofParameter lineColor; + ofParameter timeColor; + ofParameter topColor; + ofParameter colorTint; + ofParameter swapColors; + + ofParameterGroup visuals; + ofParameter smoothing; + ofParameter smoothMultiplier; + ofParameter beatSpeed; + ofParameter fastThreshold; + ofParameter minShow; + + ofParameterGroup uiPanel; + ofParameter showHist; + ofParameter pianoSong; + ofParameter showGuide; + ofParameter showLogo; + ofParameter rainbowSpec; + + float duration; + float* fftSmooth; + float msecondGain; + + const int OVERLAP = 50; + const int BANDS = 1530; + const int DIRECTIONS = 8; + const int MAX_COLOR = 255; + const int MIN_SIZE = 5; + + const int TOP_PANEL_HEIGHT = 25; + const int TOP_PANEL_WIDTH = 150; + const int WORD_WIDTH = 120; + const int WORD_HEIGHT = 17; + + const int SECOND = 1000; + const int MINUTE = 60; + const int TIMESHIFT = 10000; + + const int BASS_FFT = 10; + + std::vector xValues; + std::vector yValues; + std::vector movePattern; + std::string fileName; + + bool showGui = false; + + float pianoMult = 3; + + bool filePicked = false; + bool bgPicked = false; + + int colorSwitchGap = 0; + + ofImage logo; + ofImage bg; + + int circleRes = 100; + bool firstTime = true; + + ofxPostProcessing post; + ofxPostProcessing zoomPost; +}; diff --git a/README.md b/README.md index 0516b2d..03c6541 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ -# OpenMusicVis -OpenMusicVis - Customizable Music Visualizer built with OpenFrameworks +# CS 126 Final Project: Music Visualizer + +This is a customizable music visualizer built with [openFrameworks](http://openframeworks.cc/). This visualizer takes any mp3/wav file as input and displays a real-time audio spectrum and particle system. It also takes any png/jpg file as image input for the background display. + +## Getting Started + +### Prerequisites + +You will need to download the latest [openFrameworks](http://openframeworks.cc/download/) library. This Music Visualizer uses the [ofxHistoryPlot](https://github.com/armadillu/ofxHistoryPlot) addon to display past data on audio frequencies as well as the [ofxPostProcessing](https://github.com/neilmendoza/ofxPostProcessing) addon for particle effects, so you will need to clone both repositories and add them to the `addons/` folder within your openFrameworks directory. + +### Installing + +Import the project using the Project Generator located within the `projectGenerator-osx` folder. Ensure the **ofxGui**, **ofxHistoryPlot**, and **ofxPostProcessing** addons are selected under the addons dropdown. Follow the setup guides within [here](http://openframeworks.cc/download/) to build and run with your favorite IDE. + +### Usage + +The program will prompt you with the system file directory. You can load a mp3/wav file from anywhere on your computer. The program comes with two presets, which can be toggled with the **p** key. The first preset is intended for solo instrument performances, such as piano and guitar songs. The second preset is for all other song types. Both presets are customizable with the **s** key, which contains the settings panel. + +Press the spacebar to stop and start the current song. Use the right and left arrow keys to fast forward and rewind the audio file by 10 seconds. + +To select a new song, press the **n** key. To change the background image, press the **b** key. + +Many of the elements in the display can be toggled to fit personal preferences. Press the **h** key to toggle the history plot display. Press the **g** key to toggle the guide messages at the top of the screen. Press the **l** key to toggle the image display at the center. + +The tint of the bars can be modified in the settings panel, but can also be changed randomly using the "Light Show" tickbox and the keyboard hotkeys 1-4.