mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2026-02-06 10:25:54 +00:00
refactoring SDL app into a projectM subclass
This commit is contained in:
@ -58,8 +58,8 @@ inline projectMEvent sdl2pmEvent( SDL_Event *event ) { \
|
||||
} \
|
||||
} \
|
||||
|
||||
inline projectMKeycode sdl2pmKeycode( SDL_Keysym keysym ) { \
|
||||
switch ( keysym.sym ) { \
|
||||
inline projectMKeycode sdl2pmKeycode( SDL_Keycode keycode ) { \
|
||||
switch ( keycode ) { \
|
||||
case SDLK_F1: \
|
||||
return PROJECTM_K_F1; \
|
||||
case SDLK_F2: \
|
||||
|
||||
@ -11,7 +11,7 @@ INCLUDE(../cmake/CPack-projectM.cmake)
|
||||
SET(PROJECTM_INCLUDE ${PROJECTM_ROOT_SOURCE_DIR}/libprojectM ${PROJECTM_ROOT_SOURCE_DIR}/libprojectM/Renderer)
|
||||
|
||||
# we're producing an executable called projectMSDL
|
||||
ADD_EXECUTABLE(projectMSDL projectM_SDL_main.cpp
|
||||
ADD_EXECUTABLE(projectMSDL projectM_SDL_main.cpp pmSDL.cpp
|
||||
)
|
||||
|
||||
# openGL
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
161070C41F703F13001905AB /* libprojectM.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 161070C31F703F13001905AB /* libprojectM.a */; };
|
||||
169502001F7009E9008FAF86 /* pmSDL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 169501FE1F7009E9008FAF86 /* pmSDL.cpp */; };
|
||||
C307DFD41D565B57002F6B9E /* presets in CopyFiles */ = {isa = PBXBuildFile; fileRef = C307DFD31D565B57002F6B9E /* presets */; };
|
||||
C345214F1BF022A5001707D2 /* projectM_SDL_main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C345214E1BF022A5001707D2 /* projectM_SDL_main.cpp */; };
|
||||
C34521511BF022B1001707D2 /* libprojectM.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C34521501BF022B1001707D2 /* libprojectM.a */; };
|
||||
C34521571BF0250D001707D2 /* libMilkdropPresetFactory.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C34521561BF0250D001707D2 /* libMilkdropPresetFactory.a */; };
|
||||
C34521591BF02516001707D2 /* libNativePresetFactory.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C34521581BF02516001707D2 /* libNativePresetFactory.a */; };
|
||||
C345215A1BF0259C001707D2 /* libRenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C34521541BF02333001707D2 /* libRenderer.a */; };
|
||||
@ -25,6 +26,15 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
161070C81F704047001905AB /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C34521421BF02293001707D2 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 12;
|
||||
@ -39,10 +49,12 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
161070C31F703F13001905AB /* libprojectM.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libprojectM.a; path = ../libprojectM/libprojectM.a; sourceTree = "<group>"; };
|
||||
169501FE1F7009E9008FAF86 /* pmSDL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pmSDL.cpp; sourceTree = "<group>"; };
|
||||
169501FF1F7009E9008FAF86 /* pmSDL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = pmSDL.hpp; sourceTree = "<group>"; };
|
||||
C307DFD31D565B57002F6B9E /* presets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = presets; path = ../../presets; sourceTree = "<group>"; };
|
||||
C34521441BF02294001707D2 /* SDLprojectM */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SDLprojectM; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C345214E1BF022A5001707D2 /* projectM_SDL_main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = projectM_SDL_main.cpp; sourceTree = SOURCE_ROOT; };
|
||||
C34521501BF022B1001707D2 /* libprojectM.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libprojectM.a; sourceTree = "<group>"; };
|
||||
C34521541BF02333001707D2 /* libRenderer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libRenderer.a; path = ../libprojectM/Renderer/libRenderer.a; sourceTree = "<group>"; };
|
||||
C34521561BF0250D001707D2 /* libMilkdropPresetFactory.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMilkdropPresetFactory.a; path = ../libprojectM/MilkdropPresetFactory/libMilkdropPresetFactory.a; sourceTree = "<group>"; };
|
||||
C34521581BF02516001707D2 /* libNativePresetFactory.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libNativePresetFactory.a; path = ../libprojectM/NativePresetFactory/libNativePresetFactory.a; sourceTree = "<group>"; };
|
||||
@ -62,27 +74,39 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C345215E1BF025CF001707D2 /* CoreFoundation.framework in Frameworks */,
|
||||
C345215C1BF025A9001707D2 /* OpenGL.framework in Frameworks */,
|
||||
C34521651BF025E5001707D2 /* libbz2.a in Frameworks */,
|
||||
C34521661BF025E5001707D2 /* libfreetype.a in Frameworks */,
|
||||
C34521671BF025E5001707D2 /* libftgl.a in Frameworks */,
|
||||
C34521681BF025E5001707D2 /* libGLEW.a in Frameworks */,
|
||||
C34521691BF025E5001707D2 /* libpng15.a in Frameworks */,
|
||||
C345216A1BF025E5001707D2 /* libz.a in Frameworks */,
|
||||
C345215E1BF025CF001707D2 /* CoreFoundation.framework in Frameworks */,
|
||||
C345215C1BF025A9001707D2 /* OpenGL.framework in Frameworks */,
|
||||
161070C41F703F13001905AB /* libprojectM.a in Frameworks */,
|
||||
C345215A1BF0259C001707D2 /* libRenderer.a in Frameworks */,
|
||||
C34521591BF02516001707D2 /* libNativePresetFactory.a in Frameworks */,
|
||||
C34521571BF0250D001707D2 /* libMilkdropPresetFactory.a in Frameworks */,
|
||||
C34521511BF022B1001707D2 /* libprojectM.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
161070CE1F7041B0001905AB /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C345215D1BF025CF001707D2 /* CoreFoundation.framework */,
|
||||
C345215B1BF025A9001707D2 /* OpenGL.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C345213B1BF02293001707D2 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
169501FE1F7009E9008FAF86 /* pmSDL.cpp */,
|
||||
169501FF1F7009E9008FAF86 /* pmSDL.hpp */,
|
||||
C345214E1BF022A5001707D2 /* projectM_SDL_main.cpp */,
|
||||
C307DFD31D565B57002F6B9E /* presets */,
|
||||
C345215F1BF025E5001707D2 /* libbz2.a */,
|
||||
C34521601BF025E5001707D2 /* libfreetype.a */,
|
||||
@ -90,13 +114,11 @@
|
||||
C34521621BF025E5001707D2 /* libGLEW.a */,
|
||||
C34521631BF025E5001707D2 /* libpng15.a */,
|
||||
C34521641BF025E5001707D2 /* libz.a */,
|
||||
C345215D1BF025CF001707D2 /* CoreFoundation.framework */,
|
||||
C345215B1BF025A9001707D2 /* OpenGL.framework */,
|
||||
C34521581BF02516001707D2 /* libNativePresetFactory.a */,
|
||||
C34521561BF0250D001707D2 /* libMilkdropPresetFactory.a */,
|
||||
C34521541BF02333001707D2 /* libRenderer.a */,
|
||||
C34521501BF022B1001707D2 /* libprojectM.a */,
|
||||
C34521461BF02294001707D2 /* SDLprojectM */,
|
||||
161070C31F703F13001905AB /* libprojectM.a */,
|
||||
161070CE1F7041B0001905AB /* Frameworks */,
|
||||
C34521451BF02294001707D2 /* Products */,
|
||||
C3D30B8F1BF02BE5009AAACD /* fonts */,
|
||||
);
|
||||
@ -110,14 +132,6 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C34521461BF02294001707D2 /* SDLprojectM */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C345214E1BF022A5001707D2 /* projectM_SDL_main.cpp */,
|
||||
);
|
||||
path = SDLprojectM;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -128,6 +142,7 @@
|
||||
C34521401BF02293001707D2 /* Sources */,
|
||||
C34521411BF02293001707D2 /* Frameworks */,
|
||||
C34521421BF02293001707D2 /* CopyFiles */,
|
||||
161070C81F704047001905AB /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -174,6 +189,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
169502001F7009E9008FAF86 /* pmSDL.cpp in Sources */,
|
||||
C345214F1BF022A5001707D2 /* projectM_SDL_main.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -279,6 +295,8 @@
|
||||
"$(inherited)",
|
||||
"$(LOCAL_LIBRARY_DIR)/Frameworks",
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
"$(PROJECT_DIR)",
|
||||
"$(SRCROOT)",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/../libprojectM";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
@ -300,6 +318,8 @@
|
||||
"$(inherited)",
|
||||
"$(LOCAL_LIBRARY_DIR)/Frameworks",
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
"$(PROJECT_DIR)",
|
||||
"$(SRCROOT)",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/../libprojectM";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
|
||||
197
src/projectM-sdl/pmSDL.cpp
Normal file
197
src/projectM-sdl/pmSDL.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
//
|
||||
// pmSDL.cpp
|
||||
// SDLprojectM
|
||||
//
|
||||
// Created by Mischa Spiegelmock on 2017-09-18.
|
||||
// Copyright © 2017 MVS Technical Group Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "pmSDL.hpp"
|
||||
|
||||
void projectMSDL::audioInputCallbackF32(void *userdata, unsigned char *stream, int len) {
|
||||
projectMSDL *app = (projectMSDL *) userdata;
|
||||
// printf("LEN: %i\n", len);
|
||||
// stream is (i think) samples*channels floats (native byte order) of len BYTES
|
||||
app->pcm()->addPCMfloat((float *)stream, len/sizeof(float));
|
||||
}
|
||||
|
||||
void projectMSDL::audioInputCallbackS16(void *userdata, unsigned char *stream, int len) {
|
||||
// printf("LEN: %i\n", len);
|
||||
projectMSDL *app = (projectMSDL *) userdata;
|
||||
short pcm16[2][512];
|
||||
|
||||
for (int i = 0; i < 512; i++) { //
|
||||
for (int j = 0; j < app->audioChannelsCount; j++) {
|
||||
pcm16[j][i] = stream[i+j];
|
||||
}
|
||||
}
|
||||
app->pcm()->addPCM16(pcm16);
|
||||
}
|
||||
|
||||
|
||||
int projectMSDL::openAudioInput() {
|
||||
// get audio input device
|
||||
int i, count = SDL_GetNumAudioDevices(true); // capture, please
|
||||
if (count == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "No audio capture devices found");
|
||||
SDL_Quit();
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
SDL_Log("Found audio capture device %d: %s", i, SDL_GetAudioDeviceName(i, true));
|
||||
}
|
||||
|
||||
// params for audio input
|
||||
SDL_AudioSpec want, have;
|
||||
|
||||
// TODO: let user somehow select audio input device
|
||||
SDL_AudioDeviceID selectedAudioDevice = 0; // hardcoded to use first device for now :/
|
||||
|
||||
// requested format
|
||||
SDL_zero(want);
|
||||
want.freq = 48000;
|
||||
want.format = AUDIO_F32; // float
|
||||
want.channels = 2;
|
||||
want.samples = 512;
|
||||
want.callback = projectMSDL::audioInputCallbackF32;
|
||||
want.userdata = this;
|
||||
|
||||
audioDeviceID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(selectedAudioDevice, true), true, &want, &have, 0);
|
||||
|
||||
if (audioDeviceID == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio capture device: %s", SDL_GetError());
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
// read characteristics of opened capture device
|
||||
SDL_Log("Opened audio capture device %i: %s", audioDeviceID, SDL_GetAudioDeviceName(selectedAudioDevice, true));
|
||||
SDL_Log("Sample rate: %i, frequency: %i, channels: %i, format: %i", have.samples, have.freq, have.channels, have.format);
|
||||
audioChannelsCount = have.channels;
|
||||
audioSampleRate = have.freq;
|
||||
audioSampleCount = have.samples;
|
||||
audioFormat = have.format;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void projectMSDL::beginAudioCapture() {
|
||||
// allocate a buffer to store PCM data for feeding in
|
||||
unsigned int maxSamples = audioChannelsCount * audioSampleCount;
|
||||
pcmBuffer = (unsigned char *) malloc(maxSamples);
|
||||
SDL_PauseAudioDevice(audioDeviceID, false);
|
||||
// pm->pcm()->initPCM(maxSamples);
|
||||
}
|
||||
|
||||
void projectMSDL::endAudioCapture() {
|
||||
free(pcmBuffer);
|
||||
SDL_PauseAudioDevice(audioDeviceID, true);
|
||||
}
|
||||
|
||||
void projectMSDL::toggleFullScreen() {
|
||||
if (isFullScreen) {
|
||||
SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
isFullScreen = false;
|
||||
} else {
|
||||
SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN);
|
||||
isFullScreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
void projectMSDL::keyHandler(SDL_Event *sdl_evt) {
|
||||
projectMEvent evt;
|
||||
projectMKeycode key;
|
||||
projectMModifier mod;
|
||||
SDL_Keymod sdl_mod = (SDL_Keymod) sdl_evt->key.keysym.mod;
|
||||
SDL_Keycode sdl_keycode = sdl_evt->key.keysym.sym;
|
||||
|
||||
// handle keyboard input (for our app first, then projectM)
|
||||
switch (sdl_keycode) {
|
||||
case SDLK_f:
|
||||
if (sdl_mod & KMOD_LGUI || sdl_mod & KMOD_RGUI) {
|
||||
// command-f: fullscreen
|
||||
toggleFullScreen();
|
||||
return; // handled
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// translate into projectM codes and perform default projectM handler
|
||||
evt = sdl2pmEvent(sdl_evt);
|
||||
key = sdl2pmKeycode(sdl_keycode);
|
||||
mod = sdl2pmModifier(sdl_mod);
|
||||
key_handler(evt, key, mod);
|
||||
}
|
||||
|
||||
void projectMSDL::addFakePCM() {
|
||||
int i;
|
||||
short pcm_data[2][512];
|
||||
/** Produce some fake PCM data to stuff into projectM */
|
||||
for ( i = 0 ; i < 512 ; i++ ) {
|
||||
if ( i % 2 == 0 ) {
|
||||
pcm_data[0][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
pcm_data[1][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
} else {
|
||||
pcm_data[0][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
pcm_data[1][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
}
|
||||
if ( i % 2 == 1 ) {
|
||||
pcm_data[0][i] = -pcm_data[0][i];
|
||||
pcm_data[1][i] = -pcm_data[1][i];
|
||||
}
|
||||
}
|
||||
|
||||
/** Add the waveform data */
|
||||
pcm()->addPCM16(pcm_data);
|
||||
}
|
||||
|
||||
void projectMSDL::resize(unsigned int width, unsigned int height) {
|
||||
width = width;
|
||||
height = height;
|
||||
settings.windowWidth = width;
|
||||
settings.windowHeight = height;
|
||||
projectM_resetGL(width, height);
|
||||
}
|
||||
|
||||
void projectMSDL::pollEvent() {
|
||||
SDL_Event evt;
|
||||
|
||||
SDL_PollEvent(&evt);
|
||||
switch (evt.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (evt.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
resize(evt.window.data1, evt.window.data2);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
keyHandler(&evt);
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void projectMSDL::renderFrame() {
|
||||
glClearColor( 0.0, 0.0, 0.0, 0.0 );
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
projectM::renderFrame();
|
||||
glFlush();
|
||||
|
||||
SDL_RenderPresent(rend);
|
||||
}
|
||||
|
||||
projectMSDL::projectMSDL(Settings settings, int flags) : projectM(settings, flags) {
|
||||
width = settings.windowWidth;
|
||||
height = settings.windowHeight;
|
||||
done = 0;
|
||||
isFullScreen = false;
|
||||
}
|
||||
|
||||
void projectMSDL::init(SDL_Window *window, SDL_Renderer *renderer) {
|
||||
win = window;
|
||||
rend = renderer;
|
||||
selectRandom(true);
|
||||
projectM_resetGL(width, height);
|
||||
}
|
||||
71
src/projectM-sdl/pmSDL.hpp
Normal file
71
src/projectM-sdl/pmSDL.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// pmSDL.hpp
|
||||
// SDLprojectM
|
||||
//
|
||||
// Created by Mischa Spiegelmock on 2017-09-18.
|
||||
// Copyright © 2017 MVS Technical Group Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef pmSDL_hpp
|
||||
#define pmSDL_hpp
|
||||
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef USE_GLES1
|
||||
#include <GLES/gl.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/gl.h>
|
||||
#endif
|
||||
|
||||
#include <projectM.hpp>
|
||||
#include <sdltoprojectM.h>
|
||||
#include <iostream>
|
||||
|
||||
const float FPS = 60;
|
||||
|
||||
class projectMSDL : projectM {
|
||||
public:
|
||||
bool done;
|
||||
|
||||
projectMSDL(Settings settings, int flags = FLAG_NONE);
|
||||
void init(SDL_Window *window, SDL_Renderer *renderer);
|
||||
int openAudioInput();
|
||||
void beginAudioCapture();
|
||||
void endAudioCapture();
|
||||
void toggleFullScreen();
|
||||
void resize(unsigned int width, unsigned int height);
|
||||
void renderFrame();
|
||||
void pollEvent();
|
||||
|
||||
private:
|
||||
SDL_Window *win;
|
||||
SDL_Renderer *rend;
|
||||
bool isFullScreen;
|
||||
projectM::Settings settings;
|
||||
SDL_AudioDeviceID audioInputDevice;
|
||||
unsigned int width, height;
|
||||
|
||||
// audio input device characteristics
|
||||
unsigned short audioChannelsCount;
|
||||
unsigned short audioSampleRate;
|
||||
unsigned short audioSampleCount;
|
||||
SDL_AudioFormat audioFormat;
|
||||
SDL_AudioDeviceID audioDeviceID;
|
||||
unsigned char *pcmBuffer; // pre-allocated buffer for audioInputCallback
|
||||
|
||||
static void audioInputCallbackF32(void *userdata, unsigned char *stream, int len);
|
||||
static void audioInputCallbackS16(void *userdata, unsigned char *stream, int len);
|
||||
|
||||
|
||||
void addFakePCM();
|
||||
void keyHandler(SDL_Event *);
|
||||
};
|
||||
|
||||
|
||||
#endif /* pmSDL_hpp */
|
||||
@ -5,248 +5,60 @@
|
||||
// Created by Mischa Spiegelmock on 6/3/15.
|
||||
// Copyright (c) 2015 Mischa Spiegelmock. All rights reserved.
|
||||
//
|
||||
// This is an implementation of projectM using libsdl2
|
||||
|
||||
// This is an implementation of projectM using libSDL2
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef USE_GLES1
|
||||
#include <GLES/gl.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/gl.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#endif
|
||||
|
||||
#include <projectM.hpp>
|
||||
#include <sdltoprojectM.h>
|
||||
#include <iostream>
|
||||
const float FPS = 60;
|
||||
|
||||
typedef struct {
|
||||
projectM *pm;
|
||||
SDL_Window *win;
|
||||
SDL_Renderer *rend;
|
||||
bool done;
|
||||
projectM::Settings settings;
|
||||
SDL_AudioDeviceID audioInputDevice;
|
||||
|
||||
// audio input device characteristics
|
||||
unsigned short audioChannelsCount;
|
||||
unsigned short audioSampleRate;
|
||||
unsigned short audioSampleCount;
|
||||
SDL_AudioFormat audioFormat;
|
||||
SDL_AudioDeviceID audioDeviceID;
|
||||
unsigned char *pcmBuffer; // pre-allocated buffer for audioInputCallback
|
||||
} projectMApp;
|
||||
|
||||
void audioInputCallbackF32(void *userdata, unsigned char *stream, int len) {
|
||||
// printf("LEN: %i\n", len);
|
||||
projectMApp *app = (projectMApp *) userdata;
|
||||
// stream is (i think) samples*channels floats (native byte order) of len BYTES
|
||||
app->pm->pcm()->addPCMfloat((float *)stream, len/sizeof(float));
|
||||
}
|
||||
|
||||
void audioInputCallbackS16(void *userdata, unsigned char *stream, int len) {
|
||||
// printf("LEN: %i\n", len);
|
||||
projectMApp *app = (projectMApp *) userdata;
|
||||
short pcm16[2][512];
|
||||
|
||||
for (int i = 0; i < 512; i++) { //
|
||||
for (int j = 0; j < app->audioChannelsCount; j++) {
|
||||
pcm16[j][i] = stream[i+j];
|
||||
}
|
||||
}
|
||||
app->pm->pcm()->addPCM16(pcm16);
|
||||
}
|
||||
|
||||
|
||||
int openAudioInput(projectMApp *app) {
|
||||
// get audio input device
|
||||
int i, count = SDL_GetNumAudioDevices(true); // capture, please
|
||||
if (count == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "No audio capture devices found");
|
||||
SDL_Quit();
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
SDL_Log("Found audio capture device %d: %s", i, SDL_GetAudioDeviceName(i, true));
|
||||
}
|
||||
|
||||
// params for audio input
|
||||
SDL_AudioSpec want, have;
|
||||
|
||||
// TODO: let user somehow select audio input device
|
||||
SDL_AudioDeviceID selectedAudioDevice = 0; // hardcoded to use first device for now :/
|
||||
|
||||
// requested format
|
||||
SDL_zero(want);
|
||||
want.freq = 48000;
|
||||
want.format = AUDIO_F32; // float
|
||||
want.channels = 2;
|
||||
want.samples = 512;
|
||||
want.callback = audioInputCallbackF32;
|
||||
want.userdata = app;
|
||||
|
||||
app->audioDeviceID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(selectedAudioDevice, true), true, &want, &have, 0);
|
||||
|
||||
if (app->audioDeviceID == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio capture device: %s", SDL_GetError());
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
// read characteristics of opened capture device
|
||||
SDL_Log("Opened audio capture device %i: %s", app->audioDeviceID, SDL_GetAudioDeviceName(selectedAudioDevice, true));
|
||||
SDL_Log("Sample rate: %i, frequency: %i, channels: %i, format: %i", have.samples, have.freq, have.channels, have.format);
|
||||
app->audioChannelsCount = have.channels;
|
||||
app->audioSampleRate = have.freq;
|
||||
app->audioSampleCount = have.samples;
|
||||
app->audioFormat = have.format;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void beginAudioCapture(projectMApp *app) {
|
||||
// allocate a buffer to store PCM data for feeding in
|
||||
unsigned int maxSamples = app->audioChannelsCount * app->audioSampleCount;
|
||||
app->pcmBuffer = (unsigned char *) malloc(maxSamples);
|
||||
SDL_PauseAudioDevice(app->audioDeviceID, false);
|
||||
// app->pm->pcm()->initPCM(maxSamples);
|
||||
}
|
||||
|
||||
void endAudioCapture(projectMApp *app) {
|
||||
free(app->pcmBuffer);
|
||||
SDL_PauseAudioDevice(app->audioDeviceID, true);
|
||||
}
|
||||
|
||||
void keyHandler(projectMApp *app, SDL_Event *sdl_evt) {
|
||||
projectMEvent evt;
|
||||
projectMKeycode key;
|
||||
projectMModifier mod;
|
||||
SDL_Keymod sdl_mod;
|
||||
|
||||
/** Translate into projectM codes and process */
|
||||
evt = sdl2pmEvent(sdl_evt);
|
||||
key = sdl2pmKeycode(sdl_evt->key.keysym);
|
||||
sdl_mod = SDL_GetModState();
|
||||
mod = sdl2pmModifier(sdl_mod);
|
||||
if (evt == PROJECTM_KEYDOWN) {
|
||||
app->pm->key_handler( evt, key, mod );
|
||||
}
|
||||
}
|
||||
|
||||
void addFakePCM(projectMApp *app) {
|
||||
int i;
|
||||
short pcm_data[2][512];
|
||||
/** Produce some fake PCM data to stuff into projectM */
|
||||
for ( i = 0 ; i < 512 ; i++ ) {
|
||||
if ( i % 2 == 0 ) {
|
||||
pcm_data[0][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
pcm_data[1][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
} else {
|
||||
pcm_data[0][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
pcm_data[1][i] = (float)( rand() / ( (float)RAND_MAX ) * (pow(2,14) ) );
|
||||
}
|
||||
if ( i % 2 == 1 ) {
|
||||
pcm_data[0][i] = -pcm_data[0][i];
|
||||
pcm_data[1][i] = -pcm_data[1][i];
|
||||
}
|
||||
}
|
||||
|
||||
/** Add the waveform data */
|
||||
app->pm->pcm()->addPCM16(pcm_data);
|
||||
}
|
||||
|
||||
void renderFrame(projectMApp *app) {
|
||||
SDL_Event evt;
|
||||
|
||||
SDL_PollEvent(&evt);
|
||||
switch (evt.type) {
|
||||
case SDL_KEYDOWN:
|
||||
keyHandler(app, &evt);
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
app->done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
glClearColor( 0.0, 0.0, 0.0, 0.0 );
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
app->pm->renderFrame();
|
||||
glFlush();
|
||||
|
||||
SDL_RenderPresent(app->rend);
|
||||
}
|
||||
#include "pmSDL.hpp"
|
||||
|
||||
int main( int argc, char *argv[] ) {
|
||||
projectMApp app;
|
||||
app.done = 0;
|
||||
projectM::Settings settings;
|
||||
|
||||
int width = 784,
|
||||
height = 784;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
||||
|
||||
app.win = SDL_CreateWindow("projectM", 0, 0, width, height, 0);
|
||||
app.rend = SDL_CreateRenderer(app.win, 0, SDL_RENDERER_ACCELERATED);
|
||||
if (! app.rend) {
|
||||
fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError());
|
||||
return PROJECTM_ERROR;
|
||||
}
|
||||
SDL_SetWindowTitle(app.win, "projectM Visualizer");
|
||||
printf("SDL init version 2\n");
|
||||
|
||||
#ifdef PANTS
|
||||
if ( fsaa ) {
|
||||
SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS, &value );
|
||||
printf( "SDL_GL_MULTISAMPLEBUFFERS: requested 1, got %d\n", value );
|
||||
SDL_GL_GetAttribute( SDL_GL_MULTISAMPLESAMPLES, &value );
|
||||
printf( "SDL_GL_MULTISAMPLESAMPLES: requested %d, got %d\n", fsaa, value );
|
||||
}
|
||||
#endif
|
||||
|
||||
app.settings.meshX = 1;
|
||||
app.settings.meshY = 1;
|
||||
app.settings.fps = FPS;
|
||||
app.settings.textureSize = 2048; // idk?
|
||||
app.settings.windowWidth = width;
|
||||
app.settings.windowHeight = height;
|
||||
app.settings.smoothPresetDuration = 3; // seconds
|
||||
app.settings.presetDuration = 5; // seconds
|
||||
app.settings.beatSensitivity = 0.8;
|
||||
app.settings.aspectCorrection = 1;
|
||||
app.settings.easterEgg = 0; // ???
|
||||
app.settings.shuffleEnabled = 1;
|
||||
app.settings.softCutRatingsEnabled = 1; // ???
|
||||
app.settings.presetURL = "presets/presets_tryptonaut";
|
||||
app.settings.menuFontURL = "fonts/Vera.ttf";
|
||||
app.settings.titleFontURL = "fonts/Vera.ttf";
|
||||
settings.windowWidth = 1024;
|
||||
settings.windowHeight = 768;
|
||||
settings.meshX = 1;
|
||||
settings.meshY = 1;
|
||||
settings.fps = FPS;
|
||||
settings.textureSize = 2048; // idk?
|
||||
settings.smoothPresetDuration = 3; // seconds
|
||||
settings.presetDuration = 5; // seconds
|
||||
settings.beatSensitivity = 0.8;
|
||||
settings.aspectCorrection = 1;
|
||||
settings.easterEgg = 0; // ???
|
||||
settings.shuffleEnabled = 1;
|
||||
settings.softCutRatingsEnabled = 1; // ???
|
||||
settings.presetURL = "presets/presets_tryptonaut";
|
||||
settings.menuFontURL = "fonts/Vera.ttf";
|
||||
settings.titleFontURL = "fonts/Vera.ttf";
|
||||
|
||||
// init projectM
|
||||
app.pm = new projectM(app.settings);
|
||||
app.pm->selectRandom(true);
|
||||
app.pm->projectM_resetGL(width, height);
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
||||
auto win = SDL_CreateWindow("projectM", 0, 0, settings.windowWidth, settings.windowHeight, SDL_WINDOW_RESIZABLE);
|
||||
auto rend = SDL_CreateRenderer(win, 0, SDL_RENDERER_ACCELERATED);
|
||||
if (! rend) {
|
||||
fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
}
|
||||
SDL_SetWindowTitle(win, "projectM Visualizer");
|
||||
|
||||
auto *app = new projectMSDL(settings, 0);
|
||||
|
||||
app->init(win, rend);
|
||||
|
||||
// get an audio input device
|
||||
openAudioInput(&app);
|
||||
beginAudioCapture(&app);
|
||||
|
||||
app->openAudioInput();
|
||||
app->beginAudioCapture();
|
||||
|
||||
// standard main loop
|
||||
const Uint32 frame_delay = 1000/FPS;
|
||||
Uint32 last_time = SDL_GetTicks();
|
||||
while (! app.done) {
|
||||
renderFrame(&app);
|
||||
while (! app->done) {
|
||||
app->renderFrame();
|
||||
app->pollEvent();
|
||||
Uint32 elapsed = SDL_GetTicks() - last_time;
|
||||
if (elapsed < frame_delay)
|
||||
SDL_Delay(frame_delay - elapsed);
|
||||
last_time = SDL_GetTicks();
|
||||
}
|
||||
|
||||
|
||||
return PROJECTM_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user