diff --git a/CMakeLists.txt b/CMakeLists.txt index e3e5658c..1063715a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,8 +49,9 @@ set(headers include/al/app/al_App.hpp include/al/app/al_AudioApp.hpp include/al/app/al_DistributedApp.hpp + include/al/app/al_NodeConfiguration.hpp include/al/app/al_FPS.hpp/ - include/al/app/al_WindowApp.hpp +# include/al/app/al_WindowApp.hpp include/al/app/al_AudioDomain.hpp include/al/app/al_ComputationDomain.hpp @@ -108,7 +109,7 @@ set(headers include/al/scene/al_SequencerMIDI.hpp include/al/scene/al_SynthSequencer.hpp include/al/sound/al_Ambisonics.hpp - include/al/sound/al_AudioScene.hpp +# include/al/sound/al_AudioScene.hpp include/al/sound/al_Biquad.hpp include/al/sound/al_Crossover.hpp include/al/sound/al_Dbap.hpp @@ -121,6 +122,8 @@ set(headers include/al/sound/al_SoundFile.hpp include/al/spatial/al_HashSpace.hpp include/al/spatial/al_Pose.hpp + include/al/sphere/al_SphereUtils.hpp + include/al/sphere/al_PerProjection.hpp include/al/system/al_PeriodicThread.hpp include/al/system/al_Printing.hpp include/al/system/al_Thread.hpp @@ -144,6 +147,7 @@ set(headers include/al/ui/al_Gnomon.hpp include/al/ui/al_Pickable.hpp include/al/ui/al_SequenceRecorder.hpp + include/al/ui/al_SequenceServer.hpp include/al/ui/al_HtmlInterfaceServer.hpp include/al/ui/al_PickableManager.hpp ) @@ -152,7 +156,7 @@ set(sources src/app/al_App.cpp src/app/al_AudioApp.cpp src/app/al_FPS.cpp - src/app/al_WindowApp.cpp +# src/app/al_WindowApp.cpp src/app/al_DistributedApp.cpp src/app/al_AudioDomain.cpp @@ -208,16 +212,18 @@ set(sources src/scene/al_PolySynth.cpp src/scene/al_SynthSequencer.cpp src/sound/al_Ambisonics.cpp - src/sound/al_AudioScene.cpp +# src/sound/al_AudioScene.cpp src/sound/al_Biquad.cpp src/sound/al_Dbap.cpp src/sound/al_Lbap.cpp src/sound/al_Vbap.cpp src/sound/al_Spatializer.cpp + src/sound/al_Speaker.cpp src/sound/al_StereoPanner.cpp src/sound/al_SoundFile.cpp src/spatial/al_HashSpace.cpp src/spatial/al_Pose.cpp + src/sphere/al_AlloSphereSpeakerLayout.cpp src/sphere/al_SphereUtils.cpp src/sphere/al_PerProjection.cpp src/system/al_PeriodicThread.cpp @@ -235,6 +241,7 @@ set(sources src/ui/al_FileSelector.cpp src/ui/al_ParameterServer.cpp src/ui/al_SequenceRecorder.cpp + src/ui/al_SequenceServer.cpp src/ui/al_HtmlInterfaceServer.cpp src/ui/al_PresetHandler.cpp src/ui/al_PresetServer.cpp @@ -253,6 +260,7 @@ set_target_properties(al PROPERTIES if (AL_WINDOWS) target_compile_options(al PRIVATE "") + target_compile_definitions(al PUBLIC NOMINMAX) else () target_compile_options(al PRIVATE "-Wall") endif (AL_WINDOWS) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8927566c..f9415ba6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,35 +10,8 @@ MACRO(SUBDIRLIST result curdir) SET(${result} ${dirlist}) ENDMACRO() -# Ignore examples that wont work in CI -set(EXAMPLES_TO_IGNORE - one-line-of-c/main.cpp - sound/multiFilePlayer.cpp - sound/sound_file.cpp - sound/ambiPlayer.cpp - graphics/textureImage.cpp - graphics/asset.cpp -) - -#if(AL_WINDOWS EQUAL 1 AND APPVEYOR_BUILD EQUAL 1) -# Don't build files with dynamic dependencies on Appveyor CI build -#set(EXAMPLES_TO_IGNORE -# examples/sound/multiFilePlayer.cpp -# examples/sound/sound_file.cpp -# examples/sound/ambiPlayer.cpp -# examples/graphics/textureImage.cpp -# examples/graphics/font.cpp -# examples/graphics/asset.cpp -# examples/user_flags/main.cpp -# examples/openvr/openvr.cpp -# examples/openvr/openvr-app.cpp -# examples/one-line-of-c/main.cpp -#) -#endif() - macro(BuildExample example_src dir) get_filename_component(EXAMPLE_NAME ${example_src} NAME_WE) # Get name w/o extension - #get_filename_component(EXAMPLE_DIRECTORY ${example_src} DIRECTORY) # Get name w/o extension set(example_directory ${CMAKE_CURRENT_LIST_DIR}) if ("${dir}" STREQUAL ".") @@ -57,15 +30,8 @@ macro(BuildExample example_src dir) RUNTIME_OUTPUT_DIRECTORY_DEBUG ${example_directory}/${dir}/bin RUNTIME_OUTPUT_DIRECTORY_RELEASE ${example_directory}/${dir}/bin - #RUNTIME_OUTPUT_DIRECTORY_DEBUG ${EXAMPLE_DIRECTORY}/bin - #RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXAMPLE_DIRECTORY}/bin ) -# message("Adding target for example: ${example_src}") -#include_directories(${ALLOCORE_INCLUDE_DIR} ${GAMMA_INCLUDE_DIRS}}) - # message("Gamma : ${GAMMA_INCLUDE_DIRs}") - #add_dependencies(${EXAMPLE_TARGET} al Gamma) - #target_link_libraries(${EXAMPLE_TARGET} al Gamma ${OPENGL_gl_LIBRARY} ${ADDITIONAL_LIBRARIES} ${EXTERNAL_LIBRARIES}) target_link_libraries(${EXAMPLE_TARGET} al) #get_target_property(DLLS_TO_COPY al AL_DLL_LIBRARIES) @@ -79,21 +45,7 @@ foreach(dir ${EXAMPLE_DIRS}) file(GLOB EXAMPLE_FILES RELATIVE ${CMAKE_CURRENT_LIST_DIR} ${dir}/*.cpp) foreach(example_src ${EXAMPLE_FILES}) - list (FIND EXAMPLES_TO_IGNORE "${example_src}" _index) - if (${_index} EQUAL -1) BuildExample("${example_src}" "${dir}") - else() - message("Ignoring example ${EXAMPLE_NAME}") - endif (${_index} EQUAL -1) endforeach(example_src) endforeach(dir) - -#foreach(example_src ${EXTENSIONS_EXAMPLES}) -## message("Building extension example: ${example_src}") -# get_filename_component(EXAMPLE_DIR "${example_src}" DIRECTORY) -# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${EXAMPLE_DIR}/bin") -# BuildExample("${example_src}" "extensions" ) -#endforeach() - - diff --git a/examples/distributed/omnirendering.cpp b/examples/distributed/omnirendering.cpp index 797529fb..da7a77c8 100644 --- a/examples/distributed/omnirendering.cpp +++ b/examples/distributed/omnirendering.cpp @@ -1,17 +1,15 @@ #include + #include "al/app/al_DistributedApp.hpp" -#include "al/app/al_OmniRendererDomain.hpp" +//#include "al/app/al_OmniRendererDomain.hpp" using namespace std; using namespace al; struct MyOmniRendererApp : DistributedApp { VAOMesh mesh; - - // Nav mNav; - // Viewpoint mView {mNav.transformed()}; - // NavInputControl mNavControl {mNav}; + ParameterPose currentPose{"currentPose"}; bool DO_BLENDING = false; float alpha = 0.9f; @@ -20,12 +18,16 @@ struct MyOmniRendererApp : DistributedApp { // append(mNavControl); addIcosahedron(mesh); mesh.update(); + parameterServer() << currentPose; } - // void onAnimate(double dt) override { - // mNav.step(); - //// pose(mView.pose()); // should not be in onDraw - // } + void onAnimate(double dt) override { + if (isPrimary()) { + currentPose = pose(); + } else { + pose().set(currentPose.get()); + } + } void onDraw(Graphics& g) override { g.clear(0, 0, 1); diff --git a/examples/graphics/textureImage.cpp b/examples/graphics/textureImage.cpp index 1f0ebd93..ae457ffa 100644 --- a/examples/graphics/textureImage.cpp +++ b/examples/graphics/textureImage.cpp @@ -7,14 +7,14 @@ smooth. This is because interpolation is done on the GPU. Karl Yerkes and Matt Wright (2011/10/10) */ -#include "al/core.hpp" -#include "module/img/loadImage.hpp" - #include #include #include #include +#include "al/app/al_App.hpp" +#include "al/graphics/al_Image.hpp" + using namespace al; using namespace std; @@ -27,15 +27,16 @@ class MyApp : public App { // const char* filename = "data/hubble.jpg"; - auto imageData = imgModule::loadImage(filename); - if (imageData.data.size() == 0) { + auto imageData = Image(filename); + + if (imageData.array().size() == 0) { cout << "failed to load image" << endl; } - cout << "loaded image size: " << imageData.width << ", " << imageData.height - << endl; + cout << "loaded image size: " << imageData.width() << ", " + << imageData.height() << endl; - texture.create2D(imageData.width, imageData.height); - texture.submit(imageData.data.data(), GL_RGBA, GL_UNSIGNED_BYTE); + texture.create2D(imageData.width(), imageData.height()); + texture.submit(imageData.array().data(), GL_RGBA, GL_UNSIGNED_BYTE); texture.filter(Texture::LINEAR); } diff --git a/examples/imgui/imgui_example.cpp b/examples/imgui/imgui_example.cpp index 9d3fda4b..a154925e 100644 --- a/examples/imgui/imgui_example.cpp +++ b/examples/imgui/imgui_example.cpp @@ -29,7 +29,7 @@ struct my_app : public App { // so need to flag active/inactive here navControl().active(!isImguiUsingInput()); - // call beginIMGUI before everything, so info about gui status can be used + // call imguiBeginFrame before everything, so info about gui status can be used // to update app state imguiBeginFrame(); diff --git a/examples/io/midiIn.cpp b/examples/io/midiIn.cpp index b135f5a8..d8a3dd9a 100644 --- a/examples/io/midiIn.cpp +++ b/examples/io/midiIn.cpp @@ -10,6 +10,7 @@ Lance Putnam, 12/2012 */ #include + #include "al/io/al_MIDI.hpp" using namespace al; @@ -69,11 +70,11 @@ void midiCallback(double deltaTime, std::vector *msg, } int main() { - MIDIIn midiIn; + RtMidiIn RtMidiIn; // Check available ports vs. specified unsigned portToOpen = 0; - unsigned numPorts = midiIn.getPortCount(); + unsigned numPorts = RtMidiIn.getPortCount(); if (portToOpen >= numPorts) { printf("Invalid port specifier!\n"); @@ -82,12 +83,12 @@ int main() { try { // Print out names of available input ports for (unsigned i = 0; i < numPorts; ++i) { - printf("Port %u: %s\n", i, midiIn.getPortName(i).c_str()); + printf("Port %u: %s\n", i, RtMidiIn.getPortName(i).c_str()); } // Open the port specified above - midiIn.openPort(portToOpen); - } catch (MIDIError &error) { + RtMidiIn.openPort(portToOpen); + } catch (RtMidiError &error) { error.printMessage(); return 1; } @@ -95,10 +96,10 @@ int main() { // Set our callback function. This should be done immediately after // opening the port to avoid having incoming messages written to the // queue instead of sent to the callback function. - midiIn.setCallback(&midiCallback); + RtMidiIn.setCallback(&midiCallback); // Don't ignore sysex, timing, or active sensing messages. - midiIn.ignoreTypes(false, false, false); + RtMidiIn.ignoreTypes(false, false, false); printf("\nReading MIDI input ... press to quit.\n"); getchar(); diff --git a/examples/io/midiInApp.cpp b/examples/io/midiInApp.cpp index 84323c88..4232c4f6 100644 --- a/examples/io/midiInApp.cpp +++ b/examples/io/midiInApp.cpp @@ -10,22 +10,24 @@ Lance Putnam, 9/2013 */ #include + #include "al/app/al_App.hpp" #include "al/io/al_MIDI.hpp" using namespace al; class MyApp : public App, public MIDIMessageHandler { public: - MIDIIn midiIn; + RtMidiIn midiIn; - void onCreate() { + void onInit() { // Check for connected MIDI devices if (midiIn.getPortCount() > 0) { - // Bind ourself to the MIDIIn + // Bind ourself to the RtMidiIn object, to have the onMidiMessage() + // callback called whenever a MIDI message is received MIDIMessageHandler::bindTo(midiIn); // Open the last device found - int port = midiIn.getPortCount() - 1; + unsigned int port = midiIn.getPortCount() - 1; midiIn.openPort(port); printf("Opened port to %s\n", midiIn.getPortName(port).c_str()); } else { @@ -83,4 +85,7 @@ class MyApp : public App, public MIDIMessageHandler { } }; -int main() { MyApp().start(); } +int main() { + MyApp().start(); + return 0; +} diff --git a/examples/io/windowResizeCb.cpp b/examples/io/windowResizeCb.cpp new file mode 100644 index 00000000..b4c5a4ff --- /dev/null +++ b/examples/io/windowResizeCb.cpp @@ -0,0 +1,20 @@ +#include + +#include "al/app/al_App.hpp" +#include "al/graphics/al_Shapes.hpp" + +using namespace al; + +struct MyApp : App { + void onDraw(Graphics& g) override { g.clear(0, 0, 0); } + + void onResize(int w, int h) override { + std::cout << "Resized to: " << w << "," << h << std::endl; + } +}; + +int main() { + MyApp app; + app.dimensions(600, 400); + app.start(); +} diff --git a/examples/scene/distributed_scene.cpp b/examples/scene/distributed_scene.cpp index e9491ddd..e349666e 100644 --- a/examples/scene/distributed_scene.cpp +++ b/examples/scene/distributed_scene.cpp @@ -146,7 +146,7 @@ struct MyApp : public DistributedApp { return true; } - DistributedScene scene{PolySynth::TIME_MASTER_CPU}; + DistributedScene scene{TimeMasterMode::TIME_MASTER_CPU}; }; int main() { diff --git a/examples/sound/ambiPlayer.cpp b/examples/sound/ambiPlayer.cpp index 13e0ce2e..8d37991f 100644 --- a/examples/sound/ambiPlayer.cpp +++ b/examples/sound/ambiPlayer.cpp @@ -10,81 +10,75 @@ and a sphere follow mode. stereographic rendering */ -#include #include #include + #include "al/app/al_App.hpp" #include "al/sound/al_Ambisonics.hpp" +#include "al/sound/al_SoundFile.hpp" +#include "al/sphere/al_AlloSphereSpeakerLayout.hpp" using namespace al; #define AUDIO_BLOCK_SIZE 512 -typedef struct { - float *values; - int counter; - int numblocks; -} meters_t; - -typedef struct { - AmbisonicsSpatializer *spatializer; - gam::SoundFile sf; +struct MyApp : public App { + AmbisonicsSpatializer spatializer; + SoundFile sf; + SoundFilePlayer sfPlayer; float read_buffer[AUDIO_BLOCK_SIZE * 4]; float gain; int done; - meters_t meters; -} userdata_t; + Speakers speakerLayout; + + void onInit() override { + // Create spatializer + // spatializer = new AmbisonicsSpatializer(speakerLayout, 2, 1); + spatializer.configure(2, 1, 1); + spatializer.setSpeakerLayout(speakerLayout); + spatializer.numFrames(AUDIO_BLOCK_SIZE); + + gain = 1; + done = 0; + + std::string path = "/Users/create/code/spatial_andres/"; + std::string filename = "Ambisonics/Bees/T08.WAV"; + + if (!sf.open((path + filename).c_str())) { + std::cout << " Can't open file: " << filename << std::endl; + quit(); + } else { + std::cout << "Playing file: " << filename << std::endl; + } -void audioCB(AudioIOData &io) { - userdata_t *ud = (userdata_t *)io.user(); - int numFrames = io.framesPerBuffer(); + if (sf.channels != 4) { + std::cout << "ERROR: Wrong number of channels in file. Expected 4." + << std::endl; + quit(); + } + sfPlayer.soundFile = &sf; + } - assert(AUDIO_BLOCK_SIZE == numFrames); + void onSound(AudioIOData &io) override { + int numFrames = io.framesPerBuffer(); - AmbisonicsSpatializer *spatializer = ud->spatializer; - spatializer->prepare(io); + assert(AUDIO_BLOCK_SIZE == numFrames); - float *ambiChans = spatializer->ambiChans(); + spatializer.prepare(io); - int framesRead = ud->sf.read(ud->read_buffer, AUDIO_BLOCK_SIZE); + float *ambiChans = spatializer.ambiChans(); - if (framesRead == 0) { - ud->done = 1; - } + sfPlayer.getFrames(AUDIO_BLOCK_SIZE, read_buffer, AUDIO_BLOCK_SIZE); - for (int i = 0; i < 1; i++) { - for (int j = 0; j < framesRead; j++) { - //*ambiChans++ = (ud->read_buffer[j*4 + i] * ud->gain); - int chan = 0; - ambiChans[j + chan * AUDIO_BLOCK_SIZE] = - (ud->read_buffer[j * 4 + chan] * ud->gain); - // io.out(i, j) = (ud->read_buffer[j*4 + i] * ud->gain); - } - } - spatializer->finalize(io); - float *curvalue = ud->meters.values; - for (int i = 0; i < io.channelsOut(); i++) { - for (int j = 0; j < numFrames; j++) { - if (*curvalue < io.out(i, j)) { - *curvalue = io.out(i, j); + for (int i = 0; i < 1; i++) { + for (int j = 0; j < AUDIO_BLOCK_SIZE; j++) { + int chan = 0; + ambiChans[j + chan * AUDIO_BLOCK_SIZE] = (read_buffer[j * 4 + chan]); } } - curvalue++; + spatializer.finalize(io); } - if (++(ud->meters.counter) == ud->meters.numblocks) { - for (int i = 0; i < io.channelsOut(); i++) { - int db = (int)(20.0 * log10(ud->meters.values[i])); - - if (db < -120) { - std::cout << "-- "; - } else { - std::cout << db << " "; - } - } - std::cout << std::endl; - ud->meters.counter = 0; - } -} +}; // #define STEREO @@ -92,135 +86,15 @@ int main(int argc, char *argv[]) { // Set speaker layout AudioDevice::printAll(); -#ifdef STEREO - const int numSpeakers = 2; - Speaker speakers[] = { - Speaker(0, 45, 0), - Speaker(1, -45, 0), - }; -#else - const int numSpeakers = 22; - Speaker speakers[numSpeakers] = { - Speaker(1 - 1, 77.660913, 41.000000), - // Speaker(2-1, 45.088015, 41.000000), - Speaker(3 - 1, 14.797289, 41.000000), - // Speaker(4-1, -14.797289, 41.000000), - Speaker(5 - 1, -45.088015, 41.000000), - // Speaker(6-1, -77.660913, 41.000000), - Speaker(7 - 1, -102.339087, 41.000000), - // Speaker(8-1, -134.911985, 41.000000), - Speaker(9 - 1, -165.202711, 41.000000), - // Speaker(10-1, 165.202711, 41.000000), - Speaker(11 - 1, 134.911985, 41.000000), - // Speaker(12-1, 102.339087, 41.000000), - Speaker(17 - 1, 77.660913, 0.000000), - // Speaker(18-1, 65.647587, 0.000000), - // Speaker(19-1, 54.081600, 0.000000), - Speaker(20 - 1, 42.869831, 0.000000), - // Speaker(21-1, 31.928167, 0.000000), - // Speaker(22-1, 21.181024, 0.000000), - Speaker(23 - 1, 10.559657, 0.000000), - // Speaker(24-1, 0.000000, 0.000000), - // Speaker(25-1, -10.559657, 0.000000), - Speaker(26 - 1, -21.181024, 0.000000), - // Speaker(27-1, -31.928167, 0.000000), - // Speaker(28-1, -42.869831, 0.000000), - Speaker(29 - 1, -54.081600, 0.000000), - // Speaker(30-1, -65.647587, 0.000000), - // Speaker(31-1, -77.660913, 0.000000), - Speaker(32 - 1, -102.339087, 0.000000), - // Speaker(33-1, -114.352413, 0.000000), - // Speaker(34-1, -125.918400, 0.000000), - Speaker(35 - 1, -137.130169, 0.000000), - // Speaker(36-1, -148.071833, 0.000000), - // Speaker(37-1, -158.818976, 0.000000), - Speaker(38 - 1, -169.440343, 0.000000), - // Speaker(39-1, -180.000000, 0.000000), - // Speaker(40-1, 169.440343, 0.000000), - Speaker(41 - 1, 158.818976, 0.000000), - // Speaker(42-1, 148.071833, 0.000000), - // Speaker(43-1, 137.130169, 0.000000), - Speaker(44 - 1, 125.918400, 0.000000), - // Speaker(45-1, 114.352413, 0.000000), - // Speaker(46-1, 102.339087, 0.000000), - Speaker(49 - 1, 77.660913, -32.500000), - // Speaker(50-1, 45.088015, -32.500000), - Speaker(51 - 1, 14.797289, -32.500000), - // Speaker(52-1, -14.797289, -32.500000), - Speaker(53 - 1, -45.088015, -32.500000), - // Speaker(54-1, -77.660913, -32.500000), - Speaker(55 - 1, -102.339087, -32.500000), - // Speaker(56-1, -134.911985, -32.500000), - Speaker(57 - 1, -165.202711, -32.500000), - // Speaker(58-1, 165.202711, -32.500000), - Speaker(59 - 1, 134.911985, -32.500000), - // Speaker(60-1, 102.339087, -32.500000), - }; -#endif - - SpeakerLayout speakerLayout; - for (int i = 0; i < numSpeakers; i++) { - speakerLayout.addSpeaker(speakers[i]); - } - float sr = 48000; - userdata_t ud; - // Create spatializer - ud.spatializer = new AmbisonicsSpatializer(speakerLayout, 2, 1); - ud.spatializer->numSpeakers(numSpeakers); - ud.spatializer->numFrames(AUDIO_BLOCK_SIZE); - ud.gain = 1; - ud.done = 0; - ud.meters.counter = 0; - ud.meters.numblocks = 1.0 * sr / AUDIO_BLOCK_SIZE; - ud.meters.values = (float *)calloc(60, sizeof(float)); - - std::string path = "/Users/create/code/spatial_andres/"; - // std::string path = "/home/andres/Music/"; - - // std::string filename = "Ambisonics/Ambisonia/AF_Orfeo_Barocco_REC2.amb"; - // std::string filename = - // "Ambisonics/Ambisonia/PWH_Purcell-Your_hay_it_is_mowed.amb"; std::string - // filename = - // "Ambisonics/Ambisonia/Royer-Werni-Töpp_Improvisation_#3-CoreSound_TetraMic-24bit-48k.amb"; - // std::string filename = - // "Ambisonics/Ambisonia/cv_Badlands_Day_Insects.amb"; - std::string filename = "Ambisonics/Bees/T08.WAV"; - // std::string filename = - //"Ambisonics/Ambisonia/The_Kites-Conference_of_the_Birds-CoreSound_TetraMic-24bit-48k.amb"; - // std::string filename = - //"Ambisonics/Ambisonia/The_Kites-Conference_of_the_Birds-Soundfield_ST450-24bit-48k.amb"; - - // std::cout << ud.spatializer->numChannels() << std::endl; - ud.sf.path(path + filename); - if (!ud.sf.openRead()) { - std::cout << " Can't open file: " << filename << std::endl; - return -1; - } else { - std::cout << "Playing file: " << filename << std::endl; - } + MyApp app; - if (!ud.sf.channels() == 4) { - std::cout << "Wrong number of channels" << std::endl; - return -1; - } - // Dbap *dbap; - // dbap = new Dbap(); - - // Create listener to render audio - // listener = scene.createListener(speakerLayout, ambisonics); - // listener = scene.createListener(speakerLayout, dbap); + app.speakerLayout = AlloSphereSpeakerLayout(); - AudioIO audioIO; + app.audioDomain()->configure(sr, 256, 60); - audioIO.initWithDefaults(audioCB, &ud, true, true, 256); - audioIO.channelsOut(60); - audioIO.start(); - - while (!ud.done) { - al_sleep(1); - } + app.start(); return 0; } diff --git a/examples/sound/multiFilePlayer.cpp b/examples/sound/multiFilePlayer.cpp index f2960342..a5b9c9b9 100644 --- a/examples/sound/multiFilePlayer.cpp +++ b/examples/sound/multiFilePlayer.cpp @@ -11,10 +11,11 @@ and a sphere follow mode. stereographic rendering */ -#include #include #include -#include "al/core.hpp" + +#include "al/app/al_App.hpp" +#include "al/sound/al_SoundFile.hpp" #include "al/sound/al_Speaker.hpp" using namespace al; @@ -28,186 +29,58 @@ typedef struct { int numblocks; } meters_t; -typedef struct { - gam::SoundFile sfs[4]; - float read_buffer[AUDIO_BLOCK_SIZE]; - vector outputMap; - meters_t meters; - float gain; - int done; -} userdata_t; - -void audioCB(AudioIOData &io) { - userdata_t *ud = (userdata_t *)io.user(); - int numFrames = io.framesPerBuffer(); +struct MyApp : public App { + SoundFile sfs[4]; + uint64_t frameCounter = 0; + vector outputMap; - int framesRead; - for (int i = 0; i < 4; i++) { - framesRead = ud->sfs[i].read(ud->read_buffer, numFrames); - for (int j = 0; j < framesRead; j++) { - io.out(ud->outputMap[i], j) += (ud->read_buffer[j] * ud->gain); + void onSound(AudioIOData &io) override { + uint64_t numFrames = io.framesPerBuffer(); + if (frameCounter + numFrames > sfs[0].frameCount) { + numFrames = sfs[0].frameCount - frameCounter; } - } - float *curvalue = ud->meters.values; - for (int i = 0; i < io.channelsOut(); i++) { - for (int j = 0; j < numFrames; j++) { - if (*curvalue < io.out(i, j)) { - *curvalue = io.out(i, j); - } + std::cout << frameCounter << std::endl; + for (uint16_t i = 0; i < 4; i++) { + float *frames = sfs[i].getFrame(frameCounter); + memcpy(io.outBuffer(i), frames, numFrames * sizeof(float)); } - curvalue++; - } - if (++(ud->meters.counter) == ud->meters.numblocks) { - for (int i = 0; i < io.channelsOut(); i++) { - int db = (int)(20.0 * log10(ud->meters.values[i])); - - if (db < -120) { - std::cout << "-- "; - } else { - std::cout << db << " "; - } + frameCounter += io.framesPerBuffer(); + if (frameCounter >= sfs[0].frameCount) { + frameCounter = 0; } - std::cout << std::endl; - ud->meters.counter = 0; - } - if (framesRead == 0) { - ud->done = 1; - } -} - -//#define STEREO - -int main(int argc, char *argv[]) { - // Set speaker layout - -#ifdef STEREO - const int numSpeakers = 2; - Speaker speakers[] = { - Speaker(0, 45, 0), - Speaker(1, -45, 0), - }; -#else - const int numSpeakers = 54; - Speaker speakers[] = { - Speaker(1 - 1, 77.660913, 41.000000), - Speaker(2 - 1, 45.088015, 41.000000), - Speaker(3 - 1, 14.797289, 41.000000), - Speaker(4 - 1, -14.797289, 41.000000), - Speaker(5 - 1, -45.088015, 41.000000), - Speaker(6 - 1, -77.660913, 41.000000), - Speaker(7 - 1, -102.339087, 41.000000), - Speaker(8 - 1, -134.911985, 41.000000), - Speaker(9 - 1, -165.202711, 41.000000), - Speaker(10 - 1, 165.202711, 41.000000), - Speaker(11 - 1, 134.911985, 41.000000), - Speaker(12 - 1, 102.339087, 41.000000), - Speaker(17 - 1, 77.660913, 0.000000), - Speaker(18 - 1, 65.647587, 0.000000), - Speaker(19 - 1, 54.081600, 0.000000), - Speaker(20 - 1, 42.869831, 0.000000), - Speaker(21 - 1, 31.928167, 0.000000), - Speaker(22 - 1, 21.181024, 0.000000), - Speaker(23 - 1, 10.559657, 0.000000), - Speaker(24 - 1, 0.000000, 0.000000), - Speaker(25 - 1, -10.559657, 0.000000), - Speaker(26 - 1, -21.181024, 0.000000), - Speaker(27 - 1, -31.928167, 0.000000), - Speaker(28 - 1, -42.869831, 0.000000), - Speaker(29 - 1, -54.081600, 0.000000), - Speaker(30 - 1, -65.647587, 0.000000), - Speaker(31 - 1, -77.660913, 0.000000), - Speaker(32 - 1, -102.339087, 0.000000), - Speaker(33 - 1, -114.352413, 0.000000), - Speaker(34 - 1, -125.918400, 0.000000), - Speaker(35 - 1, -137.130169, 0.000000), - Speaker(36 - 1, -148.071833, 0.000000), - Speaker(37 - 1, -158.818976, 0.000000), - Speaker(38 - 1, -169.440343, 0.000000), - Speaker(39 - 1, -180.000000, 0.000000), - Speaker(40 - 1, 169.440343, 0.000000), - Speaker(41 - 1, 158.818976, 0.000000), - Speaker(42 - 1, 148.071833, 0.000000), - Speaker(43 - 1, 137.130169, 0.000000), - Speaker(44 - 1, 125.918400, 0.000000), - Speaker(45 - 1, 114.352413, 0.000000), - Speaker(46 - 1, 102.339087, 0.000000), - Speaker(49 - 1, 77.660913, -32.500000), - Speaker(50 - 1, 45.088015, -32.500000), - Speaker(51 - 1, 14.797289, -32.500000), - Speaker(52 - 1, -14.797289, -32.500000), - Speaker(53 - 1, -45.088015, -32.500000), - Speaker(54 - 1, -77.660913, -32.500000), - Speaker(55 - 1, -102.339087, -32.500000), - Speaker(56 - 1, -134.911985, -32.500000), - Speaker(57 - 1, -165.202711, -32.500000), - Speaker(58 - 1, 165.202711, -32.500000), - Speaker(59 - 1, 134.911985, -32.500000), - Speaker(60 - 1, 102.339087, -32.500000), - }; -#endif - - SpeakerLayout speakerLayout; - for (int i = 0; i < numSpeakers; i++) { - speakerLayout.addSpeaker(speakers[i]); } +}; - float sr = 44100; - - userdata_t ud; - ud.gain = 0.5; - ud.done = 0; - ud.meters.counter = 0; - ud.meters.numblocks = 1.0 * sr / AUDIO_BLOCK_SIZE; - ud.meters.values = (float *)calloc(60, sizeof(float)); - - std::string path = "/Users/create/code/spatial_andres/"; - // std::string path = "/home/andres/Music/"; - +int main() { std::vector filenames; - filenames.push_back(path + "Chowning/Turenas-MM/Turenas-MM-LF.aif"); - filenames.push_back(path + "Chowning/Turenas-MM/Turenas-MM-RF.aif"); - filenames.push_back(path + "Chowning/Turenas-MM/Turenas-MM-LR.aif"); - filenames.push_back(path + "Chowning/Turenas-MM/Turenas-MM-RR.aif"); - // filenames.push_back(path + "Chowning/Stria/Stria-FL.aiff"); - // filenames.push_back(path + "Chowning/Stria/Stria-FR.aiff"); - // filenames.push_back(path + "Chowning/Stria/Stria-RL.aiff"); - // filenames.push_back(path + "Chowning/Stria/Stria-RR.aiff"); + filenames.push_back("data/count.wav"); + filenames.push_back("data/count.wav"); + filenames.push_back("data/count.wav"); + filenames.push_back("data/count.wav"); + + MyApp app; - ud.outputMap.resize(filenames.size()); - // ud.outputMap[0] = 2 -1; - // ud.outputMap[1] = 53 -1; - // ud.outputMap[2] = 59 -1; - // ud.outputMap[3] = 8 -1; - // ud.outputMap[0] = 21 -1; - // ud.outputMap[1] = 27 -1; - // ud.outputMap[2] = 42 -1; - // ud.outputMap[3] = 36 -1; - ud.outputMap[0] = 2 - 1; - ud.outputMap[1] = 53 - 1; - ud.outputMap[2] = 59 - 1; - ud.outputMap[3] = 8 - 1; + app.outputMap = {0, 1, 2, 3}; + // app.outputMap[0] = 2 - 1; + // app.outputMap[1] = 53 - 1; + // app.outputMap[2] = 59 - 1; + // app.outputMap[3] = 8 - 1; - for (int i = 0; i < filenames.size(); i++) { - ud.sfs[i].path(filenames[i]); - if (!ud.sfs[i].openRead()) { + for (size_t i = 0; i < filenames.size(); i++) { + if (!app.sfs[i].open(filenames[i].c_str())) { std::cout << " Can't open file: " << filenames[i] << std::endl; return -1; } else { - std::cout << "Playing file: " << filenames[i] << std::endl; + std::cout << "Playing file " << i << " : " << filenames[i] << std::endl; + std::cout << " -- sr: " << app.sfs[i].sampleRate + << " total frames: " << app.sfs[i].frameCount + << " channels: " << app.sfs[i].channels << std::endl; } - // TODO check sampling rate; } - // Dbap *dbap; - // dbap = new Dbap(); - - AudioIO audioIO; - audioIO.initWithDefaults(audioCB, &ud, true, true, 256); - audioIO.channelsOut(60); - audioIO.start(); - - while (!ud.done) { - al_sleep(0); - } + float sr = app.sfs[0].sampleRate; + app.audioDomain()->audioIO().gain(0.5); // Global output gain. + app.audioDomain()->configure(sr, AUDIO_BLOCK_SIZE, 4); + app.start(); return 0; } diff --git a/examples/sound/soundfile_selector.cpp b/examples/sound/soundfile_selector.cpp new file mode 100644 index 00000000..af3793e0 --- /dev/null +++ b/examples/sound/soundfile_selector.cpp @@ -0,0 +1,102 @@ +#include +#include +#include + +#include "al/app/al_App.hpp" +#include "al/io/al_Imgui.hpp" +#include "al/sound/al_SoundFile.hpp" +#include "al/ui/al_FileSelector.hpp" + +using namespace al; + +// Sound file reading + +struct MyApp : App { + SoundFilePlayerTS playerTS; + std::vector buffer; + bool loop = true; + FileSelector selector{"", [](std::string name) { + if (name.substr(name.size() - 4) == ".wav") + return true; + else + return false; + }}; + + void onCreate() override { imguiInit(); } + + void onDraw(Graphics& g) override { + imguiBeginFrame(); + + ImGui::Begin("control window"); + if (ImGui::Button("Select File")) { + selector.start(File::currentPath() + "\\data"); + } + if (ImGui::Button("play")) { + playerTS.setPlay(); + } + if (ImGui::Button("pause")) { + playerTS.setPause(); + } + ImGui::End(); + + if (selector.isActive()) { + ImGui::SetNextWindowSize({200, 200}, ImGuiCond_FirstUseEver); + ImGui::Begin("File selector"); + if (selector.drawFileSelector()) { + auto items = selector.getSelection(); + audioIO().stop(); + if (items.count() > 0) { + auto filepath = items[0].filepath(); + if (playerTS.open(filepath.c_str())) { + std::cout << "sampleRate: " << playerTS.soundFile.sampleRate + << std::endl; + std::cout << "channels: " << playerTS.soundFile.channels + << std::endl; + std::cout << "frameCount: " << playerTS.soundFile.frameCount + << std::endl; + playerTS.setLoop(); + playerTS.setPlay(); + + audioIO().framesPerSecond(playerTS.soundFile.sampleRate); + audioIO().start(); + } else { + std::cerr << "Error opening file: " << filepath << std::endl; + } + } + } + ImGui::End(); + } + + imguiEndFrame(); + g.clear(0, 0, 0); + imguiDraw(); + } + + void onSound(AudioIOData& io) override { + int frames = (int)io.framesPerBuffer(); + if (playerTS.soundFile.data.size() == 0) { + // Ignore callback if no audio file + return; + } + int channels = playerTS.soundFile.channels; + int bufferLength = frames * channels; + if ((int)buffer.size() < bufferLength) { + buffer.resize(bufferLength); + } + playerTS.getFrames(frames, buffer.data(), (int)buffer.size()); + int second = (channels < 2) ? 0 : 1; + while (io()) { + int frame = (int)io.frame(); + int idx = frame * channels; + io.out(0) = buffer[idx]; + io.out(1) = buffer[idx + second]; + } + } + + void onExit() override { imguiShutdown(); } +}; + +int main() { + MyApp app; + app.start(); +} diff --git a/examples/sound/soundfilestreaming.cpp b/examples/sound/soundfilestreaming.cpp new file mode 100644 index 00000000..e4152331 --- /dev/null +++ b/examples/sound/soundfilestreaming.cpp @@ -0,0 +1,55 @@ +#include +#include + +#include "al/app/al_App.hpp" +#include "al/sound/al_SoundFile.hpp" + +using namespace al; + +// Sound file reading streaming from disk. + +struct MyApp : App { + SoundFileStreaming player; + std::vector buffer; + bool loop = true; + + void onInit() override { + const char name[] = "data/count.wav"; + if (!player.open(name)) { + std::cerr << "File not found: " << name << std::endl; + quit(); + } + std::cout << "sampleRate: " << player.sampleRate() << std::endl; + std::cout << "channels: " << player.numChannels() << std::endl; + std::cout << "frameCount: " << player.totalFrames() << std::endl; + + int channels = player.numChannels(); + int frames = (int)audioIO().framesPerBuffer(); + int bufferLength = frames * channels; + // Prepare buffer to write samples from file to. + if ((int)buffer.size() < bufferLength) { + buffer.resize(bufferLength); + } + } + + void onSound(AudioIOData& io) override { + int channels = player.numChannels(); + int second = (channels < 2) ? 0 : 1; // buffer stride + + // Read interleaved frames into buffer + player.getFrames(io.framesPerBuffer(), buffer.data()); + while (io()) { + int frame = (int)io.frame(); + uint64_t idx = frame * channels; + io.out(0) = buffer[idx]; + io.out(1) = buffer[idx + second]; + } + } +}; + +int main() { + MyApp app; + app.configureAudio(44100, 512, 2, 0); + app.start(); + return 0; +} diff --git a/examples/sound/spatialization.cpp b/examples/sound/spatialization.cpp index 6f363105..d2a9213f 100644 --- a/examples/sound/spatialization.cpp +++ b/examples/sound/spatialization.cpp @@ -1,3 +1,6 @@ +#include +#include + #include "al/app/al_App.hpp" #include "al/graphics/al_Shapes.hpp" #include "al/math/al_Random.hpp" @@ -10,9 +13,6 @@ #include "al/sphere/al_AlloSphereSpeakerLayout.hpp" #include "al/ui/al_Parameter.hpp" -#include -#include - using namespace al; using namespace std; @@ -25,29 +25,18 @@ struct MyApp : public App { Spatializer *spatializer{nullptr}; Mesh mMarker; - float speedMult = 0.04f; + double speedMult = 0.04f; double mElapsedTime = 0.0; ParameterVec3 srcpos{"srcPos", "", {0.0, 0.0, 0.0}}; atomic *mPeaks{nullptr}; - SpeakerLayout speakerLayout; + Speakers speakerLayout; int speakerType = 0; int spatializerType = 0; unsigned long counter = 0; // overall sample counter - MyApp() { - audioIO().channelsBus(1); - initSpeakers(0); - initSpatializer(1); - } - - ~MyApp() override { - if (mPeaks) { - free(mPeaks); - } - } void initSpeakers(int type = -1) { if (type < 0) { type = (speakerType + 1) % 3; @@ -63,9 +52,8 @@ struct MyApp : public App { if (mPeaks) { free(mPeaks); } - mPeaks = - new atomic[speakerLayout.speakers().size()]; // Not being freed - // in this example + mPeaks = new atomic[speakerLayout.size()]; // Not being freed + // in this example } void initSpatializer(int type) { @@ -90,6 +78,7 @@ struct MyApp : public App { } void onInit() override { + audioIO().channelsBus(1); addDodecahedron(mMarker); initSpeakers(0); initSpatializer(1); @@ -116,11 +105,10 @@ struct MyApp : public App { g.blending(true); g.blendModeTrans(); // Draw the speakers - Speakers sp = speakerLayout.speakers(); - for (size_t i = 0; i < sp.size(); ++i) { + for (size_t i = 0; i < speakerLayout.size(); ++i) { g.pushMatrix(); float xyz[3]; - sp[i].posCart(xyz); + speakerLayout[i].posCart(xyz); g.translate(-xyz[1], xyz[2], -xyz[0]); float peak = mPeaks[i].load(); g.scale(0.02f + fabs(peak) * 5); @@ -169,11 +157,10 @@ struct MyApp : public App { spatializer->finalize(io); // Now compute RMS to display the signal level for each speaker - Speakers &speakers = speakerLayout.speakers(); - for (size_t speaker = 0; speaker < speakers.size(); speaker++) { + for (size_t speaker = 0; speaker < speakerLayout.size(); speaker++) { float rms = 0; for (unsigned int i = 0; i < io.framesPerBuffer(); i++) { - unsigned int deviceChannel = speakers[speaker].deviceChannel; + unsigned int deviceChannel = speakerLayout[speaker].deviceChannel; float sample = io.out(deviceChannel, i); rms += sample * sample; } @@ -205,6 +192,12 @@ struct MyApp : public App { audioIO().start(); return true; } + + void onExit() override { + if (mPeaks) { + free(mPeaks); + } + } }; int main() { diff --git a/examples/sound/spatializationScene.cpp b/examples/sound/spatializationScene.cpp index f2d1b4fd..c68d8bf1 100644 --- a/examples/sound/spatializationScene.cpp +++ b/examples/sound/spatializationScene.cpp @@ -1,3 +1,6 @@ +#include +#include + #include "al/app/al_App.hpp" #include "al/graphics/al_Shapes.hpp" #include "al/math/al_Random.hpp" @@ -10,9 +13,6 @@ #include "al/sphere/al_AlloSphereSpeakerLayout.hpp" #include "al/ui/al_Parameter.hpp" -#include -#include - using namespace al; using namespace std; @@ -61,7 +61,7 @@ struct MyApp : public App { Source *mSource; - SpeakerLayout speakerLayout; + Speakers speakerLayout; int speakerType = 0; int spatializerType = 0; @@ -88,9 +88,8 @@ struct MyApp : public App { if (mPeaks) { free(mPeaks); } - mPeaks = - new atomic[speakerLayout.speakers().size()]; // Not being freed - // in this example + mPeaks = new atomic[speakerLayout.size()]; // Not being freed + // in this example } void initSpatializer(int type = -1) { @@ -150,11 +149,10 @@ struct MyApp : public App { g.blendOn(); g.blendModeTrans(); // Draw the speakers - Speakers sp = speakerLayout.speakers(); - for (int i = 0; i < (int)sp.size(); ++i) { + for (int i = 0; i < (int)speakerLayout.size(); ++i) { g.pushMatrix(); float xyz[3]; - sp[i].posCart(xyz); + speakerLayout[i].posCart(xyz); g.translate(-xyz[1], xyz[2], -xyz[0]); float peak = mPeaks[i].load(); g.scale(0.02 + fabs(peak) * 5); @@ -187,11 +185,10 @@ struct MyApp : public App { mScene.render(io); // Now compute RMS to display the signal level for each speaker - Speakers &speakers = speakerLayout.speakers(); - for (int speaker = 0; speaker < (int)speakers.size(); speaker++) { + for (size_t speaker = 0; speaker < speakerLayout.size(); speaker++) { float rms = 0; - for (int i = 0; i < (int)io.framesPerBuffer(); i++) { - int deviceChannel = speakers[speaker].deviceChannel; + for (uint64_t i = 0; i < io.framesPerBuffer(); i++) { + auto deviceChannel = speakerLayout[speaker].deviceChannel; float sample = io.out(deviceChannel, i); rms += sample * sample; } diff --git a/examples/sphere/common.hpp b/examples/sphere/common.hpp deleted file mode 100644 index 5bba554c..00000000 --- a/examples/sphere/common.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __COMMON__ -#define __COMMON__ - -#include "al/app/al_App.hpp" - -struct State { - al::Color backgroundColor {1.0f, 1.0f, 1.0f, 1.0f}; - al::Pose pose; -}; - -#endif \ No newline at end of file diff --git a/examples/sphere/renderer.cpp b/examples/sphere/renderer.cpp deleted file mode 100644 index de7a36d9..00000000 --- a/examples/sphere/renderer.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef AL_USE_CUTTLEBONE -#include "Cuttlebone/Cuttlebone.hpp" -#include "al/app/al_App.hpp" -#include "al/sphere/al_OmniRenderer.hpp" - -#include "common.hpp" - -using namespace std; -using namespace al; - -struct MyRendererApp : OmniRenderer { - cuttlebone::Taker taker; - unique_ptr state; - - Mesh mesh; - - void onCreate() override { - state = make_unique(); - taker.start(); - addIcosahedron(mesh); - } - - void onAnimate(double dt) override { - int popCount = taker.get(*state); - pose(state->pose); - } - - void onDraw(Graphics& g) override { - g.clear(state->backgroundColor); - g.color(0.5); - g.draw(mesh); - } - - void onExit() override { taker.stop(); } -}; - -int main() { - MyRendererApp app; - app.start(); -} - -#else -#include - -int main() { std::cout << "Cuttlebone not available!!" << std::endl; } - -#endif \ No newline at end of file diff --git a/examples/sphere/simulator.cpp b/examples/sphere/simulator.cpp deleted file mode 100644 index 12a777da..00000000 --- a/examples/sphere/simulator.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifdef AL_USE_CUTTLEBONE -#include "Cuttlebone/Cuttlebone.hpp" -#include "al/app/al_App.hpp" -#include "al/sphere/al_SphereUtils.hpp" - -#include "common.hpp" - -using namespace std; -using namespace al; - -const char* defaultBroadcastIP() { - if (sphere::is_in_sphere()) - return "192.168.10.255"; - else - return "127.0.0.1"; -} - -struct MySimulatorApp : App { - cuttlebone::Maker maker{defaultBroadcastIP()}; - unique_ptr state; - Mesh mesh; - - void onCreate() override { - state = make_unique(); - maker.start(); - nav().pos(0, 0, 5); - nav().faceToward({0, 0, 0}, {0, 1, 0}); - addIcosahedron(mesh); - } - - void onAnimate(double dt) override { - state->backgroundColor.r = float(mouse().x()) / width(); - state->backgroundColor.g = float(mouse().y()) / height(); - state->pose = pose(); - maker.set(*state); - } - - void onDraw(Graphics& g) override { - g.clear(state->backgroundColor); - g.color(0.5); - g.draw(mesh); - } - - void onExit() override { maker.stop(); } -}; - -int main() { - MySimulatorApp app; - app.start(); -} - -#else - -#include - -int main() { - std::cerr << " **** ==== **** Cuttlebone not available!!" << std::endl; -} - -#endif \ No newline at end of file diff --git a/examples/ui/fileselector.cpp b/examples/ui/fileselector.cpp new file mode 100644 index 00000000..9a94c875 --- /dev/null +++ b/examples/ui/fileselector.cpp @@ -0,0 +1,56 @@ +#include +#include +#include + +#include "al/app/al_App.hpp" +#include "al/io/al_Imgui.hpp" +#include "al/sound/al_SoundFile.hpp" +#include "al/ui/al_FileSelector.hpp" + +using namespace al; + +// This example shows how to use the file selector class + +struct MyApp : App { + FileSelector selector; + // You can specify a root path (a path you can't go down beyond) and a + // filtering function in the constructor of the selector + // FileSelector selector{"/Volumes/Data", [](std::string name) { return + // name.substr(name.size() - 4) == ".wav";}}; + std::string currentFile = "No file selected"; + + void onCreate() override { imguiInit(); } + + void onDraw(Graphics& g) override { + imguiBeginFrame(); + + ImGui::Begin("File Selector"); + + ImGui::Text("%s", currentFile.c_str()); + if (ImGui::Button("Select File")) { + // When the select file button is clicked, the file selector is shown + selector.start(""); + } + // The file selector knows internally whether it should be drawn or not, + // so you should always draw it. Check the return value of the draw function + // to know if the user has selected a file through the file selector + if (selector.drawFileSelector()) { + auto selection = selector.getSelection(); + if (selection.count() > 0) { + currentFile = selection[0].filepath(); + } + } + ImGui::End(); + imguiEndFrame(); + g.clear(0, 0, 0); + imguiDraw(); + } + + void onExit() override { imguiShutdown(); } +}; + +int main() { + MyApp app; + app.start(); + return 0; +} diff --git a/examples/ui/parameter_async_callback.cpp b/examples/ui/parameter_async_callback.cpp new file mode 100644 index 00000000..bc5af5ee --- /dev/null +++ b/examples/ui/parameter_async_callback.cpp @@ -0,0 +1,78 @@ + +#include +#include + +#include "al/app/al_App.hpp" +#include "al/graphics/al_Shapes.hpp" +#include "al/ui/al_ParameterGUI.hpp" + +using namespace al; + +struct MyApp : public App { + Parameter x{"X", "", 0, "", -2.0, 2.0}; + float processedValue{0.0f}; + bool mRunning{true}; + + std::unique_ptr valueCallbackThread; + + void onInit() override { + // Register a callback for 'x' Parameter + // For this simple example, just copy the parameter's value to the internal + // variable + x.registerChangeCallback([&](float value) { processedValue = value; }); + x.setSynchronousCallbacks(false); + + // Start a thread to run the parameters callbacks fpr 'x' + // The parameter callbacks for 'x' will be called at most once per second + valueCallbackThread = std::make_unique([&]() { + while (mRunning) { + x.processChange(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + std::cout << "Thread done." << std::endl; + }); + } + + void onCreate() override { + imguiInit(); + // Disable mouse nav to avoid naving while changing gui controls. + navControl().useMouse(false); + } + + void onAnimate(double /*dt*/) override { + imguiBeginFrame(); + + // The ParameterGUI class provides static functions to assist drawing + // parameters and GUIs + // Each beginPanel()/endPanel() pair creates a separate "window" + ParameterGUI::beginPanel("Position Control"); + // The ParameterGUI::drawParameter() can take any parameter and will + // draw and appropriate GUI + ParameterGUI::drawParameter(&x); + + ImGui::Text("current: %f", processedValue); + ImGui::Separator(); + ParameterGUI::endPanel(); + imguiEndFrame(); + } + + virtual void onDraw(Graphics &g) override { + g.clear(0); + + // draw the GUI + imguiDraw(); + } + + void onExit() override { + imguiShutdown(); + mRunning = false; + valueCallbackThread->join(); + } +}; + +int main() { + MyApp app; + app.title("Parameter GUI"); + app.start(); + return 0; +} diff --git a/examples/ui/parameterbundle_presets.cpp b/examples/ui/parameterbundle_presets.cpp index 9ad7560f..8bd16e34 100644 --- a/examples/ui/parameterbundle_presets.cpp +++ b/examples/ui/parameterbundle_presets.cpp @@ -1,14 +1,15 @@ #include "al/app/al_App.hpp" #include "al/graphics/al_Shapes.hpp" - #include "al/ui/al_ControlGUI.hpp" #include "al/ui/al_Parameter.hpp" #include "al/ui/al_ParameterBundle.hpp" using namespace al; +// This example shows usage of presets applied to parameter bundles +// For this to work you only need to register each bundle instance with the gui -// Create an 'agent' with parameters. For all agents have +// Create an 'agent' with parameters. All agents have // the same parameters, but each one can have a different // value for them struct Thing { @@ -50,6 +51,7 @@ struct MyApp : public App { gui << things[i].bundle; presetHandler << things[i].bundle; } + gui << presetHandler; // Draw Preset Handler in gui navControl().disable(); gui.init(); // presetHandler.verbose(); @@ -63,15 +65,6 @@ struct MyApp : public App { gui.draw(g); } - bool onKeyDown(Keyboard const &k) override { - if (k.key() == '1') { - presetHandler.storePreset("bundles"); - } else if (k.key() == '2') { - presetHandler.recallPreset("bundles"); - } - return true; - } - private: Thing things[3]; ControlGUI gui; diff --git a/examples/ui/parametergui.cpp b/examples/ui/parametergui.cpp index acfe7e42..7389d723 100644 --- a/examples/ui/parametergui.cpp +++ b/examples/ui/parametergui.cpp @@ -25,6 +25,10 @@ struct MyApp : public App { addSphere(mMesh, 0.1); mMesh.primitive(Mesh::LINES); + // You can set the display name for the parameter to be different to its + // name + z.displayName("Pos(Z)"); + nav() = Vec3d(0, 0, 2); // We must initialize ImGUI ourselves: imguiInit(); diff --git a/examples/ui/parametersequencer.cpp b/examples/ui/parametersequencer.cpp index 196011a2..947eaa68 100644 --- a/examples/ui/parametersequencer.cpp +++ b/examples/ui/parametersequencer.cpp @@ -1,6 +1,5 @@ #include "al/app/al_App.hpp" #include "al/graphics/al_Shapes.hpp" - #include "al/ui/al_ControlGUI.hpp" #include "al/ui/al_Parameter.hpp" #include "al/ui/al_PresetSequencer.hpp" @@ -9,15 +8,17 @@ using namespace al; using namespace std; +// This file shows how you can use the PresetSequencer class to store and +// recall individual parameter changes (without having them be part of a +// preset) + struct MyApp : App { Mesh m; Parameter X{"x", "", 0.0, "", -2, 2}; Parameter Y{"y", "", 0.0, "", -2, 2}; - Parameter Z{"z", "", 0.0, "", -2, 2}; ControlGUI gui; - PresetHandler presetHandler; PresetSequencer sequencer; SequenceRecorder recorder; @@ -27,21 +28,21 @@ struct MyApp : App { navControl().disable(); sequencer.setDirectory("presets"); + recorder.setDirectory("presets"); + // Register parameters with sequencer + sequencer << X << Y; + // Register parameters with recorder + recorder << X << Y; - sequencer << presetHandler - << Z; // Register preset handler and parameters with sequencer - recorder << presetHandler - << Z; // Register preset handler and parameters with recorder - presetHandler << X << Y; - + // GUI gui.init(); - gui << X << Y << Z; - gui << presetHandler; + gui << X << Y; + gui << sequencer << recorder; } void onDraw(Graphics &g) override { g.clear(0); - g.translate(X, Y, Z); + g.translate(X, Y, 0.0); if (recorder.recording()) { g.color(1.0, 0.0, 0.0); } else if (sequencer.running()) { @@ -52,36 +53,23 @@ struct MyApp : App { g.draw(m); gui.draw(g); } - - bool onKeyDown(const Keyboard &k) override { - if (k.key() == ' ') { - if (recorder.recording()) { - recorder.stopRecord(); - std::cout << "End recording" << std::endl; - auto steps = sequencer.loadSequence("seq"); - while (!steps.empty()) { - std::cout << steps.front().waitTime << " " << steps.front().presetName - << std::endl; - steps.pop(); - } - } else { - recorder.startRecord("seq", true); - std::cout << "Start recording" << std::endl; - } - } else if (k.key() == Keyboard::ENTER) { - if (sequencer.running()) { - sequencer.stopSequence(); - cout << "Sequencer stopped" << endl; - } else { - sequencer.playSequence("seq"); - cout << "Sequencer started" << endl; - } - } - return true; - } }; +// Function to write needed files for this example. +void writeExampleFile() { + string sequence = R"(+0.0:/x:-0.6 ++1.0:/x:0.3 ++0.5:/x:0.6 ++0.5:/x:-0.2 +:: +)"; + ofstream fseq("presets/paramseq.sequence"); + fseq << sequence; + fseq.close(); +} + int main() { + writeExampleFile(); MyApp().start(); return 0; } diff --git a/examples/ui/presets_sync_morph.cpp b/examples/ui/presets_sync_morph.cpp new file mode 100644 index 00000000..47796848 --- /dev/null +++ b/examples/ui/presets_sync_morph.cpp @@ -0,0 +1,69 @@ + + +#include "al/app/al_App.hpp" +#include "al/graphics/al_Shapes.hpp" +#include "al/ui/al_ControlGUI.hpp" +#include "al/ui/al_HtmlInterfaceServer.hpp" +#include "al/ui/al_ParameterMIDI.hpp" + +using namespace al; + +struct MyApp : public App { + Parameter Size{"Size", "", 0.3, "", 0.1, 2.0}; + ParameterBool RunPresets{"Run Presets", "", 1.0}; + + // Set time master mode to Async + PresetHandler presets{TimeMasterMode::TIME_MASTER_FREE, "presetsGUI"}; + + ControlGUI gui; + Mesh mMeshCone; + + void onInit() override { + // Add parameters to preset handling + presets << Size; + + // Add to GUI + gui << Size << RunPresets; + + // Adding a PresetHandler to a ControlGUI creates a multi-button interface + // to control the presets. + gui << presets; + + addCone(mMeshCone); + } + + virtual void onCreate() override { + nav().pos(Vec3d(0, 0, 8)); + // Disable mouse nav to avoid naving while changing gui controls. + navControl().useMouse(false); + + // Set morph step size. Since we will be stepping the morphing function + // from the draw callback, we can compute step size from fps. + presets.setMorphStepTime(1.0 / graphicsDomain()->fps()); + gui.init(); + } + + virtual void onAnimate(double dt) { + // If you uncheck Run Presets, selecting presets will have no effect + if (RunPresets.get() == 1.0) { + // Set parameter values to next step in morph (if morphing) + presets.stepMorphing(); + } + } + + virtual void onDraw(Graphics &g) override { + g.clear(0); + g.scale(Size.get()); + g.draw(mMeshCone); + gui.draw(g); + } +}; + +int main(int argc, char *argv[]) { + MyApp app; + app.dimensions(800, 600); + app.title("Presets GUI"); + app.fps(30); + app.start(); + return 0; +} diff --git a/examples/ui/presetsgui.cpp b/examples/ui/presetsgui.cpp index f0d2ae67..1cf9d12d 100644 --- a/examples/ui/presetsgui.cpp +++ b/examples/ui/presetsgui.cpp @@ -9,18 +9,35 @@ using namespace al; struct MyApp : public App { - MyApp() { - presets << Number << Size << Red << Green - << Blue; // Add parameters to preset handling + ParameterInt Number{"Number", "", 1, "", 0, 16}; + ParameterMenu Shape{"Shape"}; + Parameter Size{"Size", "", 0.3, "", 0.1, 2.0}; + Parameter Red{"Red", "Color", 0.5, "", 0.0, 1.0}; + Parameter Green{"Green", "Color", 1.0, "", 0.0, 1.0}; + Parameter Blue{"Blue", "Color", 0.5, "", 0.0, 1.0}; + + PresetHandler presets{"presetsGUI"}; + + ControlGUI gui; + + Light light; + Mesh mMeshCone; + Mesh mMeshCube; + + void onInit() override { + // Add parameters to preset handling + presets << Number << Size << Red << Green << Blue << Shape; + + Shape.setElements({"Cone", "Cube"}); // Now make control GUI // You can add Parameter objects using the streaming operator. // They will all be laid out vertically gui << Number << Size; - gui << Red << Green << Blue; + gui << Red << Green << Blue << Shape; // Expose parameters to network (You can send OSC message to them - parameterServer() << Number << Size << Red << Green << Blue; + parameterServer() << Number << Size << Red << Green << Blue << Shape; // Print server configuration parameterServer().print(); @@ -29,8 +46,10 @@ struct MyApp : public App { // to control the presets. gui << presets; - addCone(mMesh); - mMesh.generateNormals(); + addCone(mMeshCone); + mMeshCone.generateNormals(); + addCube(mMeshCube); + mMeshCube.generateNormals(); } virtual void onCreate() override { @@ -49,25 +68,15 @@ struct MyApp : public App { g.translate((i % 4) - 2, (i / 4) - 2, -5); g.scale(Size.get()); g.color(Red.get(), Green.get(), Blue.get()); - g.draw(mMesh); + if (Shape.get() == 0) { + g.draw(mMeshCone); + } else { + g.draw(mMeshCube); + } g.popMatrix(); } gui.draw(g); } - - private: - ParameterInt Number{"Number", "", 1, "", 0, 16}; - Parameter Size{"Size", "", 0.3, "", 0.1, 2.0}; - Parameter Red{"Red", "Color", 0.5, "", 0.0, 1.0}; - Parameter Green{"Green", "Color", 1.0, "", 0.0, 1.0}; - Parameter Blue{"Blue", "Color", 0.5, "", 0.0, 1.0}; - - PresetHandler presets{"presetsGUI"}; - - ControlGUI gui; - - Light light; - Mesh mMesh; }; int main(int argc, char *argv[]) { diff --git a/examples/ui/sequencer.cpp b/examples/ui/sequencer.cpp index 3ab85d7e..99e8494a 100644 --- a/examples/ui/sequencer.cpp +++ b/examples/ui/sequencer.cpp @@ -1,12 +1,12 @@ +#include + #include "al/app/al_App.hpp" #include "al/graphics/al_Shapes.hpp" - #include "al/ui/al_ControlGUI.hpp" #include "al/ui/al_Parameter.hpp" #include "al/ui/al_PresetHandler.hpp" #include "al/ui/al_PresetSequencer.hpp" - -#include +#include "al/ui/al_SequenceServer.hpp" using namespace al; using namespace std; diff --git a/examples/ui/sequencergui.cpp b/examples/ui/sequencergui.cpp index de89cc4a..1d0d2057 100644 --- a/examples/ui/sequencergui.cpp +++ b/examples/ui/sequencergui.cpp @@ -1,14 +1,12 @@ -#include "al/app/al_App.hpp" +#include +#include "al/app/al_App.hpp" #include "al/graphics/al_Shapes.hpp" - #include "al/ui/al_ControlGUI.hpp" #include "al/ui/al_Parameter.hpp" #include "al/ui/al_PresetHandler.hpp" #include "al/ui/al_PresetSequencer.hpp" -#include - using namespace al; using namespace std; @@ -18,7 +16,7 @@ struct MyApp : App { Parameter X{"x", "", 0.0, "", -2, 2}; Parameter Y{"y", "", 0.0, "", -2, 2}; - PresetHandler presetHandler{"sequencerDir", true}; + PresetHandler presetHandler{"sequencerDir", false}; PresetSequencer sequencer; ControlGUI gui; @@ -31,28 +29,51 @@ struct MyApp : App { presetHandler << X << Y; // Register parameters with preset handler sequencer << presetHandler; // Register preset handler with sequencer - gui << sequencer; + gui << X << Y << sequencer; gui.init(); + + // Sequencer timing will run in a separate thread by default. + // You can set the sequencer "granularity", i.e. the minimum time + // between sequencer steps. + // A smaller number means less jitter in the sequencer at the expense + // of greater CPU usage. The default value is 0.05 (50 ms). Using 0.01 + // Results is smoother visual movement. + sequencer.setSequencerStepTime(0.01); + // The begin callback is called whenever the sequence starts + sequencer.registerBeginCallback([&](PresetSequencer *) { + std::cout << "**** Started Sequence" << std::endl; + }); + // The end callback is called when the sequence ends or is stopped + // The 'finished' argument is true if the sequence finished by + // itself. It will be false if the sequence was stopped by the user. + sequencer.registerEndCallback([&](bool finished, PresetSequencer *) { + if (finished) { + std::cout << "**** Sequence FINSIHED ***" << std::endl; + } else { + std::cout << "**** Sequence Stopped" << std::endl; + } + }); } void onDraw(Graphics &g) override { g.clear(0); if (sequencer.running()) { - g.translate(X.get(), Y.get(), 0); g.color(0.0, 1.0, 0.0); } else { g.color(0.0, 0.0, 1.0); } + g.translate(X.get(), Y.get(), 0); g.draw(m); gui.draw(g); } }; +// Function to write needed files for this example. void writeExamplePresets() { string sequence = R"(preset1:0.0:0.5 preset2:3.0:1.0 preset3:1.0:0.0 -preset2:1.5:2.0 +preset1:1.5:2.0 :: )"; ofstream fseq("sequencerDir/seq.sequence"); @@ -60,7 +81,7 @@ preset2:1.5:2.0 fseq.close(); string preset1 = R"(::preset1 -/x f 0.4 +/x f -0.4 /y f 0.2 :: )"; diff --git a/examples/ui/sequencergui_free.cpp b/examples/ui/sequencergui_free.cpp new file mode 100644 index 00000000..83d5373f --- /dev/null +++ b/examples/ui/sequencergui_free.cpp @@ -0,0 +1,117 @@ +#include + +#include "al/app/al_App.hpp" +#include "al/graphics/al_Shapes.hpp" +#include "al/ui/al_ControlGUI.hpp" +#include "al/ui/al_Parameter.hpp" +#include "al/ui/al_PresetHandler.hpp" +#include "al/ui/al_PresetSequencer.hpp" + +using namespace al; +using namespace std; + +// This example shows usage of the preset handler and preset sequencer in +// TIME_MASTER_FREE mode. This means that instead of running the sequencer +// and preset morphing in a separate CPU thread, the user is responsible +// of calling the step functions. This also means that the user must call +// setMorphStepTime() for the preset handler. + +// Using the TIME_MASTER_FREE mode usually results in smoother graphics display +// But might incur in some jitter if you are also reading the parameters in +// other contexts (e.g. the audio context). + +// See sequencergui.cpp example for details on PresetHandler and PresetSequencer + +struct MyApp : App { + Mesh m; + + Parameter X{"x", "", 0.0, "", -2, 2}; + Parameter Y{"y", "", 0.0, "", -2, 2}; + + PresetHandler presetHandler{TimeMasterMode::TIME_MASTER_FREE, "sequencerDir", + true}; + PresetSequencer sequencer{TimeMasterMode::TIME_MASTER_FREE}; + + ControlGUI gui; + + void onCreate() override { + addSphere(m, 0.2); + nav().pullBack(4); + + navControl().disable(); + + presetHandler << X << Y; // Register parameters with preset handler + sequencer << presetHandler; // Register preset handler with sequencer + gui << sequencer; + gui.init(); + + // Currently the preset handler requires setting this manually. + // In the future it will get picked up through the value passed + // in stepMorphing() + presetHandler.setMorphStepTime(1.0f / graphicsDomain()->fps()); + } + + void onAnimate(double dt) override { + // As both the sequencer and the preset handler are in ASYNC mode, their + // step functions must be called + sequencer.stepSequencer(dt); + presetHandler.stepMorphing(dt); + } + + void onDraw(Graphics &g) override { + g.clear(0); + if (sequencer.running()) { + g.color(0.0, 1.0, 0.0); + } else { + g.color(0.0, 0.0, 1.0); + } + g.translate(X.get(), Y.get(), 0); + g.draw(m); + gui.draw(g); + } +}; + +void writeExamplePresets() { + string sequence = R"(preset1:0.0:0.5 +preset2:3.0:1.0 +preset3:1.0:0.0 +preset1:1.5:2.0 +:: +)"; + ofstream fseq("sequencerDir/seq.sequence"); + fseq << sequence; + fseq.close(); + + string preset1 = R"(::preset1 +/x f -0.4 +/y f 0.2 +:: +)"; + ofstream f1("sequencerDir/preset1.preset"); + f1 << preset1; + f1.close(); + + string preset2 = R"(::preset2 +/x f 0.6 +/y f -0.9 +:: +)"; + ofstream f2("sequencerDir/preset2.preset"); + f2 << preset2; + f2.close(); + + string preset3 = R"(::preset3 +/x f -0.1 +/y f 1.0 +:: +)"; + ofstream f3("sequencerDir/preset3.preset"); + f3 << preset3; + f3.close(); +} + +int main() { + writeExamplePresets(); + MyApp().start(); + return 0; +} diff --git a/include/al/app/al_App.hpp b/include/al/app/al_App.hpp index 2790965c..98f15e92 100644 --- a/include/al/app/al_App.hpp +++ b/include/al/app/al_App.hpp @@ -14,9 +14,7 @@ #include "al/app/al_OSCDomain.hpp" #include "al/app/al_OpenGLGraphicsDomain.hpp" #include "al/app/al_SimulationDomain.hpp" -//#include "al_OpenVRDomain.hpp" - -#include "al/app/al_WindowApp.hpp" +#include "al/graphics/al_Graphics.hpp" /** @defgroup App Application building tools * @@ -52,14 +50,14 @@ class App { virtual void onMessage(osc::Message &m) { (void)m; } virtual void onExit() {} - virtual bool onKeyDown(Keyboard const &k) { return true; } - virtual bool onKeyUp(Keyboard const &k) { return true; } - virtual bool onMouseDown(Mouse const &m) { return true; } - virtual bool onMouseUp(Mouse const &m) { return true; } - virtual bool onMouseDrag(Mouse const &m) { return true; } - virtual bool onMouseMove(Mouse const &m) { return true; } - virtual bool onMouseScroll(Mouse const &m) { return true; } - // virtual void onResize(int w, int h) {} + virtual bool onKeyDown(Keyboard const & /*k*/) { return true; } + virtual bool onKeyUp(Keyboard const & /*k*/) { return true; } + virtual bool onMouseDown(Mouse const & /*m*/) { return true; } + virtual bool onMouseUp(Mouse const & /*m*/) { return true; } + virtual bool onMouseDrag(Mouse const & /*m*/) { return true; } + virtual bool onMouseMove(Mouse const & /*m*/) { return true; } + virtual bool onMouseScroll(Mouse const & /*m*/) { return true; } + virtual void onResize(int /*w*/, int /*h*/) {} // virtual void onVisibility(bool v) {} void quit(); ///< Requests domain to quit. @@ -69,10 +67,10 @@ class App { virtual Window &defaultWindow(); virtual Graphics &graphics(); - Viewpoint &view(); - Pose &pose(); - Lens &lens(); - Nav &nav(); + virtual Viewpoint &view(); + virtual Pose &pose(); + virtual Lens &lens(); + virtual Nav &nav(); NavInputControl &navControl(); void fps(double f); @@ -139,7 +137,7 @@ class App { WindowEventHandler &handler); // Access to audio domain - [[deprecated("Use call from domain directly")]] AudioIO &audioIO(); + AudioIO &audioIO(); void configureAudio(double audioRate = 44100, int audioBlockSize = 512, int audioOutputs = -1, int audioInputs = -1); @@ -197,9 +195,7 @@ class App { }; StandardWindowAppKeyControls stdControls; - protected: - void initializeDomains(); - + // Modify these with care std::shared_ptr mDefaultWindowDomain; std::shared_ptr mOSCDomain; @@ -207,19 +203,13 @@ class App { std::shared_ptr mOpenGLGraphicsDomain; std::shared_ptr mSimulationDomain; + protected: + void initializeDomains(); + std::vector> mDomainList; std::stack> mRunningDomains; }; -class AudioControl { - public: - void registerAudioIO(AudioIO &io) { - gain.registerChangeCallback([&io](float value) { io.gain(value); }); - } - - Parameter gain{"gain", "sound", 1.0, "alloapp", 0.0, 2.0}; -}; - } // namespace al #endif // AL_APP_H diff --git a/include/al/app/al_AudioDomain.hpp b/include/al/app/al_AudioDomain.hpp index 837b2e2e..3f030845 100644 --- a/include/al/app/al_AudioDomain.hpp +++ b/include/al/app/al_AudioDomain.hpp @@ -8,10 +8,9 @@ #include #include -#include "al_ComputationDomain.hpp" - #include "Gamma/Domain.h" #include "al/io/al_AudioIO.hpp" +#include "al_ComputationDomain.hpp" namespace al { @@ -21,6 +20,7 @@ namespace al { */ class AudioDomain : public AsynchronousDomain { public: + AudioDomain(); virtual ~AudioDomain() {} // Domain management functions @@ -48,6 +48,7 @@ class AudioDomain : public AsynchronousDomain { } private: + Parameter mGainParameter{"gain", "", 1.0, "", 0.0, 2.0}; AudioIO mAudioIO; }; diff --git a/include/al/app/al_ComputationDomain.hpp b/include/al/app/al_ComputationDomain.hpp index b9a28376..3cc721aa 100644 --- a/include/al/app/al_ComputationDomain.hpp +++ b/include/al/app/al_ComputationDomain.hpp @@ -9,6 +9,8 @@ #include #include +#include "al/ui/al_Parameter.hpp" + namespace al { class SynchronousDomain; @@ -79,6 +81,19 @@ class ComputationDomain { mCleanupCallbacks.push_back(callback); } + /** + * @brief Return a list of parameters that control this domain + * @return list of parameters + * + * The parameters provided here provide runtime "continuous" parameters, + * for example like audio gain or eye separation. There should be a clear + * distinction between values that need to be set on domain intialization + * that must remain immutable during domain operation and parameters + * provided here that provide continuous adjustment to the domain's + * operation. + */ + std::vector parameters() { return mParameters; } + protected: /** * @brief initializeSubdomains should be called within the domain's @@ -132,13 +147,15 @@ class ComputationDomain { } } - protected: std::mutex mSubdomainLock; // It is the domain's responsibility to lock and // unlock while processing. double mTimeDrift{0.0}; std::vector, bool>> mSubDomainList; + // Add parameters for domain control here + std::vector mParameters; + private: std::vector> mInitializeCallbacks; std::vector> mCleanupCallbacks; diff --git a/include/al/app/al_DistributedApp.hpp b/include/al/app/al_DistributedApp.hpp index dd939bfc..554bc5f9 100644 --- a/include/al/app/al_DistributedApp.hpp +++ b/include/al/app/al_DistributedApp.hpp @@ -5,16 +5,17 @@ * Andres Cabrera, 2018, 2019, mantaraya36@gmail.com */ +#include +#include + #include "al/app/al_App.hpp" +#include "al/app/al_NodeConfiguration.hpp" #include "al/app/al_OmniRendererDomain.hpp" #include "al/io/al_Socket.hpp" #include "al/io/al_Toml.hpp" #include "al/scene/al_DistributedScene.hpp" #include "al/scene/al_DynamicScene.hpp" -#include -#include - /* * MPI and cuttlebone are optional. */ @@ -29,27 +30,21 @@ namespace al { -// TODO flow parameters +class AudioControl { + public: + void registerAudioIO(AudioIO &io) { + gain.registerChangeCallback([&io](float value) { io.gain(value); }); + } + + Parameter gain{"gain", "sound", 1.0, "alloapp", 0.0, 2.0}; +}; /** * @brief DistributedApp class * @ingroup App */ -struct DistributedApp : public App { +class DistributedApp : public App, public NodeConfiguration { public: - typedef enum { - CAP_NONE = 0, - CAP_SIMULATOR = 1 << 1, - CAP_RENDERING = 1 << 2, - CAP_OMNIRENDERING = 1 << 3, - CAP_AUDIO_IO = 1 << 4, - CAP_OSC = 1 << 5, - CAP_CONSOLE_IO = 1 << 6, - CAP_USER = - 1 - << 7 // User defined capabilities can add from here through bitshifting - } Capability; - DistributedApp(); void start() override; @@ -58,38 +53,22 @@ struct DistributedApp : public App { void initialize(); - bool hasCapability(Capability cap) { return cap & mCapabilites; } - - bool isPrimary() { return rank == 0; } - void registerDynamicScene(DynamicScene &scene); - Graphics &graphics() override { - if (hasCapability(CAP_OMNIRENDERING)) { - return omniRendering->graphics(); - } else { - return mDefaultWindowDomain->graphics(); - } - } - - Window &defaultWindow() override { - if (hasCapability(CAP_OMNIRENDERING)) { - return omniRendering->window(); - } else { - return mDefaultWindowDomain->window(); - } - } + Graphics &graphics() override; + Window &defaultWindow() override; + Viewpoint &view() override; + Pose &pose() override; + Lens &lens() override; + Nav &nav() override; - uint16_t rank{0}; - uint16_t group{0}; std::shared_ptr omniRendering; + std::map additionalConfig; private: AudioControl mAudioControl; - std::string mGlobalDataRootPath; std::map mRoleMap; - Capability mCapabilites{CAP_NONE}; }; template @@ -104,19 +83,19 @@ class DistributedAppWithState : public DistributedApp { // Replace Simulation domain with state simulation domain mSimulationDomain = mOpenGLGraphicsDomain - ->newSubDomain>(true); + ->newSubDomain>(true); if (rank == 0) { std::cout << "Running primary" << std::endl; auto sender = - std::static_pointer_cast>( + std::static_pointer_cast>( mSimulationDomain) ->addStateSender("state"); sender->configure(10101); } else { std::cout << "Running REPLICA" << std::endl; auto receiver = - std::static_pointer_cast>( + std::static_pointer_cast>( mSimulationDomain) ->addStateReceiver("state"); receiver->configure(10101); @@ -126,7 +105,7 @@ class DistributedAppWithState : public DistributedApp { } TSharedState &state() { - return std::static_pointer_cast>( + return std::static_pointer_cast>( mSimulationDomain) ->state(); } diff --git a/include/al/app/al_NodeConfiguration.hpp b/include/al/app/al_NodeConfiguration.hpp new file mode 100644 index 00000000..279da53f --- /dev/null +++ b/include/al/app/al_NodeConfiguration.hpp @@ -0,0 +1,60 @@ +#ifndef INCLUDE_AL_NODECONFIGURATION +#define INCLUDE_AL_NODECONFIGURATION + +#include +#include + +namespace al { + +// TODO flow parameters +typedef enum { + CAP_NONE = 0, + CAP_SIMULATOR = 1 << 1, + CAP_RENDERING = 1 << 2, + CAP_OMNIRENDERING = 1 << 3, + CAP_AUDIO_IO = 1 << 4, + CAP_OSC = 1 << 5, + CAP_CONSOLE_IO = 1 << 6, + CAP_2DGUI = 1 << 7, + CAP_USER = 1 << 10 + // User defined capabilities can add from here through bitshifting +} Capability; + +struct NodeConfiguration { + uint16_t rank{0}; + uint16_t group{0}; + + std::string dataRoot; + + Capability mCapabilites{CAP_NONE}; + + bool hasCapability(Capability cap) { return cap & mCapabilites; } + bool isPrimary() { return rank == 0; } + + void setRole(std::string role) { + if (role == "desktop") { + mCapabilites = (Capability)(CAP_SIMULATOR | CAP_RENDERING | CAP_AUDIO_IO | + CAP_OSC | CAP_2DGUI); + } else if (role == "renderer") { + mCapabilites = (Capability)(CAP_SIMULATOR | CAP_OMNIRENDERING | CAP_OSC); + } else if (role == "audio") { + mCapabilites = + (Capability)(CAP_SIMULATOR | CAP_AUDIO_IO | CAP_CONSOLE_IO | CAP_OSC); + } else if (role == "simulator") { + mCapabilites = (Capability)(CAP_SIMULATOR | CAP_CONSOLE_IO | CAP_OSC); + } else if (role == "replica") { + mCapabilites = (Capability)(CAP_SIMULATOR | CAP_OMNIRENDERING | + CAP_AUDIO_IO | CAP_OSC); + } else if (role == "control") { + mCapabilites = (Capability)(CAP_RENDERING | CAP_OSC | CAP_2DGUI); + } else { + std::cerr << "WARNING: Setting no capabilities for this app from " + "config file" + << std::endl; + } + } +}; + +} // namespace al + +#endif // INCLUDE_AL_NODECONFIGURATION diff --git a/include/al/app/al_OmniRendererDomain.hpp b/include/al/app/al_OmniRendererDomain.hpp index f41ac24b..f982b9ad 100644 --- a/include/al/app/al_OmniRendererDomain.hpp +++ b/include/al/app/al_OmniRendererDomain.hpp @@ -1,14 +1,12 @@ #ifndef INCLUDE_OMNIRENDER_HPP #define INCLUDE_OMNIRENDER_HPP -#include "al/sphere/al_Perprojection.hpp" -#include "al/sphere/al_SphereUtils.hpp" +#include #include "al/app/al_OpenGLGraphicsDomain.hpp" -//#include "al/graphics/al_GLFW.hpp" #include "al/graphics/al_Graphics.hpp" - -#include +#include "al/sphere/al_PerProjection.hpp" +#include "al/sphere/al_SphereUtils.hpp" namespace al { @@ -57,8 +55,19 @@ class GLFWOpenGLOmniRendererDomain : public SynchronousDomain { Graphics &graphics() { return *mGraphics; } // omni-stereo related functions - void stereo(bool b) { render_stereo = b; } - void toggleStereo() { render_stereo = !render_stereo; } + void stereo(bool b) { + if (mWindow && mWindow->enabled(Window::DisplayMode::STEREO_BUF) != b) { + if (b) { + mWindow->displayMode(Window::DisplayMode::DEFAULT_BUF | + Window::DisplayMode::STEREO_BUF); + } else { + mWindow->displayMode(Window::DisplayMode::DEFAULT_BUF); + } + } + render_stereo = b; + } + + void toggleStereo() { stereo(!render_stereo); } void omniResolution(int res) { pp_render.update_resolution(res); } int omniResolution() { return pp_render.res_; } diff --git a/include/al/app/al_OpenGLGraphicsDomain.hpp b/include/al/app/al_OpenGLGraphicsDomain.hpp index d433e8aa..846383f8 100644 --- a/include/al/app/al_OpenGLGraphicsDomain.hpp +++ b/include/al/app/al_OpenGLGraphicsDomain.hpp @@ -8,10 +8,11 @@ #include #include -#include "al_ComputationDomain.hpp" - -#include "al/app/al_WindowApp.hpp" +#include "al/app/al_ComputationDomain.hpp" +#include "al/app/al_FPS.hpp" +#include "al/graphics/al_Graphics.hpp" #include "al/io/al_ControlNav.hpp" +#include "al/io/al_Window.hpp" namespace al { @@ -45,6 +46,8 @@ class OpenGLGraphicsDomain : public AsynchronousDomain, public FPS { void quit() { mShouldQuitApp = true; } bool shouldQuit() { return mShouldQuitApp || mSubDomainList.size() == 0; } + bool running() { return mRunning; } + std::shared_ptr newWindow(); void closeWindow(std::shared_ptr windowDomain) { diff --git a/include/al/app/al_SimulationDomain.hpp b/include/al/app/al_SimulationDomain.hpp index 01bf6155..22412804 100644 --- a/include/al/app/al_SimulationDomain.hpp +++ b/include/al/app/al_SimulationDomain.hpp @@ -9,9 +9,8 @@ #include #include -#include "Gamma/Domain.h" -#include "al_ComputationDomain.hpp" -#include "al_StateDistributionDomain.hpp" +#include "al/app/al_ComputationDomain.hpp" +#include "al/app/al_StateDistributionDomain.hpp" namespace al { @@ -21,16 +20,9 @@ namespace al { */ class SimulationDomain : public SynchronousDomain { public: - virtual bool tick() override { - bool ret = tickSubdomains(true); - if (mUseCallback) { - simulationFunction(timeDelta()); - } - ret &= tickSubdomains(false); - return true; - } + virtual bool tick() override; - void disableProcessingCallback() { mUseCallback = false; } + void disableProcessingCallback(); std::function simulationFunction = [](double) { }; // function to be called in onAnimate() @@ -38,38 +30,34 @@ class SimulationDomain : public SynchronousDomain { bool mUseCallback{true}; }; +// ------------- + template class StateSimulationDomain : public SimulationDomain { public: - StateSimulationDomain() { mState = std::make_shared(); } - - virtual bool initialize(ComputationDomain *parent = nullptr) { - SimulationDomain::initialize(parent); - return true; - } - TSharedState &state() { return *mState; } std::shared_ptr statePtr() { return mState; } - std::shared_ptr> addStateSender( - std::string id = "") { - auto newDomain = newSubDomain>(false); - newDomain->setId(id); - newDomain->setStatePointer(statePtr()); - return newDomain; - } + // virtual std::shared_ptr> addStateSender( + // std::string id = "") { + // auto newDomain = newSubDomain>(false); + // newDomain->setId(id); + // newDomain->setStatePointer(statePtr()); + // return newDomain; + // } - std::shared_ptr> addStateReceiver( - std::string id = "") { - auto newDomain = newSubDomain>(true); - newDomain->setId(id); - newDomain->setStatePointer(statePtr()); - return newDomain; - } + // virtual std::shared_ptr> + // addStateReceiver( + // std::string id = "") { + // auto newDomain = newSubDomain>(true); + // newDomain->setId(id); + // newDomain->setStatePointer(statePtr()); + // return newDomain; + // } - private: - std::shared_ptr mState; + protected: + std::shared_ptr mState{new TSharedState}; }; } // namespace al diff --git a/include/al/app/al_StateDistributionDomain.hpp b/include/al/app/al_StateDistributionDomain.hpp index d15dea18..25a50c5e 100644 --- a/include/al/app/al_StateDistributionDomain.hpp +++ b/include/al/app/al_StateDistributionDomain.hpp @@ -10,45 +10,43 @@ #include #include +#include "al/app/al_SimulationDomain.hpp" #include "al/protocol/al_OSC.hpp" #include "al/spatial/al_Pose.hpp" -#include "Gamma/Domain.h" -#include "al/app/al_ComputationDomain.hpp" - -//#undef AL_USE_CUTTLEBONE - -#ifdef AL_USE_CUTTLEBONE -#include "Cuttlebone/Cuttlebone.hpp" -#else -#include "al/protocol/al_OSC.hpp" -#endif - namespace al { struct DefaultState { Pose pose; }; + template class StateReceiveDomain; template class StateSendDomain; +template +class StateSimulationDomain; + /** * @brief StateDistributionDomain class * @ingroup App */ -class StateDistributionDomain : public SynchronousDomain { +template +class StateDistributionDomain : public StateSimulationDomain { public: - template std::shared_ptr> addStateSender( std::string id = "", std::shared_ptr statePtr = nullptr); - template std::shared_ptr> addStateReceiver( std::string id = "", std::shared_ptr statePtr = nullptr); + bool isSender() { return mIsSender; } + + protected: + bool mIsSender{false}; + private: }; @@ -61,11 +59,6 @@ class StateReceiveDomain : public SynchronousDomain { tickSubdomains(true); assert(mState); // State must have been set at this point -#ifdef AL_USE_CUTTLEBONE - assert(mTaker); - mQueuedStates = mTaker->get(mState); - return true; -#else mRecvLock.lock(); if (newMessages > 0) { mQueuedStates = newMessages; @@ -75,24 +68,16 @@ class StateReceiveDomain : public SynchronousDomain { mRecvLock.unlock(); tickSubdomains(false); return true; -#endif } bool cleanup(ComputationDomain *parent = nullptr) override { cleanupSubdomains(true); -#ifdef AL_USE_CUTTLEBONE - mTaker->stop(); - mTaker = nullptr; - cleanupSubdomains(false); - return true; -#else mRecv = nullptr; mState = nullptr; // std::cerr << "Not using Cuttlebone. Ignoring" << std::endl; cleanupSubdomains(false); return true; -#endif } void configure(uint16_t port = 10100, std::string id = "state", @@ -116,18 +101,16 @@ class StateReceiveDomain : public SynchronousDomain { void setId(const std::string &id) { mId = id; } - private: + protected: std::shared_ptr mState; int mQueuedStates{1}; - std::string mAddress{"localhost"}; uint16_t mPort = 10100; uint16_t mPacketSize = 1400; + + private: std::string mId; -#ifdef AL_USE_CUTTLEBONE - std::unique_ptr> mTaker; -#else class Handler : public osc::PacketHandler { public: StateReceiveDomain *mOscDomain; @@ -157,7 +140,6 @@ class StateReceiveDomain : public SynchronousDomain { uint16_t newMessages = 0; std::mutex mRecvLock; std::unique_ptr mRecv; -#endif }; template @@ -165,13 +147,6 @@ bool StateReceiveDomain::initialize(ComputationDomain *parent) { initializeSubdomains(true); assert(parent != nullptr); -#ifdef AL_USE_CUTTLEBONE - mTaker = - std::make_unique>(); - mTaker->start(); - initializeSubdomains(false); - return true; -#else buf = std::make_unique(sizeof(TSharedState)); mRecv = std::make_unique(); if (!mRecv || !mRecv->open(mPort, mAddress.c_str())) { @@ -188,7 +163,6 @@ bool StateReceiveDomain::initialize(ComputationDomain *parent) { std::cout << "Opened " << mAddress << ":" << mPort << std::endl; initializeSubdomains(false); return true; -#endif } template @@ -197,34 +171,14 @@ class StateSendDomain : public SynchronousDomain { bool initialize(ComputationDomain *parent = nullptr) override { initializeSubdomains(true); -#ifdef AL_USE_CUTTLEBONE - mMaker = - std::make_unique>(); - mMaker->start(); - initializeSubdomains(false); - return true; -#else - // mSend = std::make_unique(mPort, mAddress.c_str()); - //// std::cout << "StateSendDomain not using Cuttlebone" << std::endl; - // if (!mSend) { - // std::cerr << "Can't create sender for StateSendDomain address:" << - // mAddress << " port:" << mPort <set(mState); - tickSubdomains(false); - return true; -#else // assert(mSend); // osc::Blob b(&mState, sizeof(mState)); @@ -240,23 +194,14 @@ class StateSendDomain : public SynchronousDomain { tickSubdomains(false); return true; -#endif } bool cleanup(ComputationDomain *parent = nullptr) override { cleanupSubdomains(true); -#ifdef AL_USE_CUTTLEBONE - if (mMaker) { - mMaker->stop(); - } - cleanupSubdomains(false); - return true; -#else mState = nullptr; // std::cerr << "Not using Cuttlebone. Ignoring" << std::endl; cleanupSubdomains(false); return true; -#endif } void configure(uint16_t port, std::string id = "state", @@ -285,48 +230,38 @@ class StateSendDomain : public SynchronousDomain { void setId(const std::string &id) { mId = id; } - private: -#ifdef AL_USE_CUTTLEBONE - std::unique_ptr> mMaker; -#else - std::unique_ptr mSend; -#endif - -#ifdef AL_USE_CUTTLEBONE - if (role() & ROLE_SIMULATOR) { - std::string broadcastAddress = configLoader.gets("broadcastAddress"); - mMaker = std::make_unique>( - broadcastAddress.c_str()); - mMaker->start(); - } else if (role() & ROLE_RENDERER) { - } + void setAddress(std::string address) { mAddress = address; }; -#endif - - std::string mId = ""; + protected: + std::shared_ptr mState; + std::mutex mStateLock; + int mQueuedStates{0}; uint16_t mPort = 10100; std::string mAddress{"localhost"}; uint16_t mPacketSize = 1400; - std::shared_ptr mState; - std::mutex mStateLock; - int mQueuedStates{0}; + private: + std::unique_ptr mSend; + + std::string mId = ""; }; template std::shared_ptr> -StateDistributionDomain::addStateSender( +StateDistributionDomain::addStateSender( std::string id, std::shared_ptr statePtr) { - auto newDomain = newSubDomain>(false); + auto newDomain = + this->template newSubDomain>(false); newDomain->setId(id); return newDomain; } template std::shared_ptr> -StateDistributionDomain::addStateReceiver( +StateDistributionDomain::addStateReceiver( std::string id, std::shared_ptr statePtr) { - auto newDomain = newSubDomain>(true); + auto newDomain = + this->template newSubDomain>(true); newDomain->setId(id); return newDomain; diff --git a/include/al/app/al_WindowApp.hpp b/include/al/app/al_WindowApp.hpp deleted file mode 100644 index c94830de..00000000 --- a/include/al/app/al_WindowApp.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef INCLUDE_AL_WINDOWAPP_HPP -#define INCLUDE_AL_WINDOWAPP_HPP - -// SINGLE-WINDOW app - -// makes window to be also an event handler -// by appending itself to the list of handlers - -// also adds standard window controls, such as ctrl + q to quit - -// Keehong Youn, 2016 -// younkeehong@gmail.com - -#include "al/app/al_FPS.hpp" -#include "al/graphics/al_Graphics.hpp" -#include "al/io/al_Window.hpp" - -#include -#include - -namespace al { - -/** - * @brief WindowApp class - * @ingroup App - */ -class[[deprecated("Use domains instead")]] WindowApp - : public Window, - public WindowEventHandler, - public FPS { - public: - // basic window app keyboard actions: fullscreen, quit, ... - struct StandardWindowAppKeyControls : WindowEventHandler { - bool keyDown(const Keyboard& k); - }; - StandardWindowAppKeyControls stdControls; - - Graphics mGraphics; - std::atomic mShouldQuitApp{false}; - bool is_verbose = false; - void verbose(bool b = true) { is_verbose = b; } - - WindowApp(); - - // start app loop - virtual void start(); - - void quit() { mShouldQuitApp = true; } - bool shouldQuit() { return mShouldQuitApp || Window::shouldClose(); } - - // user will override these - virtual void onInit() {} - virtual void onCreate() {} - virtual void onAnimate(double dt) {} - virtual void onDraw(Graphics & g) {} - virtual void onExit() {} - - virtual void onResize(int w, int h) {} - virtual void onVisibility(bool v) {} - - bool resize(int dw, int dh) final; - bool visibility(bool v) final; -}; - -} // namespace al - -#endif diff --git a/include/al/graphics/al_Lens.hpp b/include/al/graphics/al_Lens.hpp index 5219247b..1e6d1001 100644 --- a/include/al/graphics/al_Lens.hpp +++ b/include/al/graphics/al_Lens.hpp @@ -48,6 +48,11 @@ #include "al/math/al_Vec.hpp" #include "al/spatial/al_Pose.hpp" +#ifdef AL_WINDOWS +#undef near +#undef far +#endif + namespace al { /// Stores optics settings important for rendering diff --git a/include/al/graphics/al_OpenGL.hpp b/include/al/graphics/al_OpenGL.hpp index 4f5a0ee5..fb6cf19e 100644 --- a/include/al/graphics/al_OpenGL.hpp +++ b/include/al/graphics/al_OpenGL.hpp @@ -1,7 +1,6 @@ #ifndef INCLUDE_AL_OPENGL_HPP #define INCLUDE_AL_OPENGL_HPP -// #include "GL/glew.h" #include "glad/glad.h" namespace al { @@ -11,19 +10,19 @@ namespace gl { bool load(); bool loaded(); -const char* versionString(); +const char *versionString(); /// Get current GPU error string /// \returns the error string or an empty string if no error /// -const char* errorString(bool verbose = false); +const char *errorString(bool verbose = false); /// Print current GPU error state /// @param[in] msg Custom error message /// @param[in] ID Graphics object ID (-1 for none) /// \returns whether there was an error -bool error(const char* msg = "", int ID = -1); +bool error(const char *msg = "", int ID = -1); /// Returns number of bytes for given data type int numBytes(GLenum v); diff --git a/include/al/graphics/al_Shader.hpp b/include/al/graphics/al_Shader.hpp index e8bbeaf5..5c9f3430 100644 --- a/include/al/graphics/al_Shader.hpp +++ b/include/al/graphics/al_Shader.hpp @@ -65,14 +65,14 @@ class ShaderBase : public GPUObject { virtual ~ShaderBase() {} /// Returns info log or 0 if none - const char* log() const; + const char *log() const; /// Prints info log, if any void printLog() const; protected: - virtual void get(int pname, void* params) const = 0; - virtual void getLog(char* buf) const = 0; + virtual void get(int pname, void *params) const = 0; + virtual void getLog(char *buf) const = 0; }; /// Shader object @@ -84,15 +84,15 @@ class Shader : public ShaderBase { public: enum Type { VERTEX, GEOMETRY, FRAGMENT }; - Shader(const std::string& source = "", Shader::Type type = FRAGMENT); + Shader(const std::string &source = "", Shader::Type type = FRAGMENT); /// This will automatically delete the shader object when it is no longer /// attached to any program object. virtual ~Shader() { destroy(); } - Shader& source(const std::string& v); - Shader& source(const std::string& v, Shader::Type type); - Shader& compile(); + Shader &source(const std::string &v); + Shader &source(const std::string &v, Shader::Type type); + Shader &compile(); bool compiled() const; Shader::Type type() const { return mType; } @@ -101,8 +101,8 @@ class Shader : public ShaderBase { std::string mSource; Shader::Type mType; - virtual void get(int pname, void* params) const; - virtual void getLog(char* buf) const; + virtual void get(int pname, void *params) const; + virtual void getLog(char *buf) const; virtual void onCreate(); virtual void onDestroy(); @@ -162,10 +162,10 @@ class ShaderProgram : public ShaderBase { /// The input shader will be compiled if necessary. /// - ShaderProgram& attach(Shader& s); + ShaderProgram &attach(Shader &s); /// Detach shader from program - const ShaderProgram& detach(const Shader& s) const; + const ShaderProgram &detach(const Shader &s) const; /// Link attached shaders @@ -173,13 +173,13 @@ class ShaderProgram : public ShaderBase { /// You might not want to do this if you need to set uniforms before /// validating, e.g., when using different texture sampler types in the /// same shader. - const ShaderProgram& link(bool doValidate = true) const; + const ShaderProgram &link(bool doValidate = true) const; /// Compile and link shader sources - bool compile(const std::string& vertSource, const std::string& fragSource, - const std::string& geomSource = ""); + bool compile(const std::string &vertSource, const std::string &fragSource, + const std::string &geomSource = ""); - const ShaderProgram& use(); + const ShaderProgram &use(); /// Begin use of shader program void begin(); @@ -197,149 +197,149 @@ class ShaderProgram : public ShaderBase { void listParams() const; /// Get location of uniform - int getUniformLocation(const char* name) const; - int getUniformLocation(const std::string& s) const { + int getUniformLocation(const char *name) const; + int getUniformLocation(const std::string &s) const { return getUniformLocation(s.c_str()); } /// Get location of attribute - int attribute(const char* name) const; + int attribute(const char *name) const; - const ShaderProgram& uniform(const char* name, const Color& c) const { + const ShaderProgram &uniform(const char *name, const Color &c) const { return uniform4(name, c.components); } void uniform4f(int loc, float v0, float v1, float v2, float v3) const; - void uniformMat4f(int loc, float* data) const; + void uniformMat4f(int loc, float *data) const; - const ShaderProgram& uniform(int loc, int v) const; - const ShaderProgram& uniform(int loc, float v) const; - const ShaderProgram& uniform(int loc, double v) const { + const ShaderProgram &uniform(int loc, int v) const; + const ShaderProgram &uniform(int loc, float v) const; + const ShaderProgram &uniform(int loc, double v) const { return uniform(loc, float(v)); } - const ShaderProgram& uniform(int loc, float v0, float v1) const; - const ShaderProgram& uniform(int loc, float v0, float v1, float v2) const; - const ShaderProgram& uniform(int loc, float v0, float v1, float v2, + const ShaderProgram &uniform(int loc, float v0, float v1) const; + const ShaderProgram &uniform(int loc, float v0, float v1, float v2) const; + const ShaderProgram &uniform(int loc, float v0, float v1, float v2, float v3) const; - const ShaderProgram& uniform4v(int loc, const float* v, int count = 1) const; + const ShaderProgram &uniform4v(int loc, const float *v, int count = 1) const; template - const ShaderProgram& uniform(int loc, const Vec<2, T>& v) const { + const ShaderProgram &uniform(int loc, const Vec<2, T> &v) const { return uniform(loc, v.x, v.y); } template - const ShaderProgram& uniform(int loc, const Vec<3, T>& v) const { + const ShaderProgram &uniform(int loc, const Vec<3, T> &v) const { return uniform(loc, v.x, v.y, v.z); } template - const ShaderProgram& uniform(int loc, const Vec<4, T>& v) const { + const ShaderProgram &uniform(int loc, const Vec<4, T> &v) const { return uniform(loc, v.x, v.y, v.z, v.w); } - const ShaderProgram& uniformMatrix3(int loc, const float* v, + const ShaderProgram &uniformMatrix3(int loc, const float *v, bool transpose = false) const; - const ShaderProgram& uniformMatrix4(int loc, const float* v, + const ShaderProgram &uniformMatrix4(int loc, const float *v, bool transpose = false) const; - const ShaderProgram& uniform(int loc, const Mat<4, float>& m) const { + const ShaderProgram &uniform(int loc, const Mat<4, float> &m) const { return uniformMatrix4(loc, m.elems()); } template - const ShaderProgram& uniform(int loc, const Mat<4, T>& m) const { + const ShaderProgram &uniform(int loc, const Mat<4, T> &m) const { return uniform(loc, Mat4f(m)); } - const ShaderProgram& uniform(const char* name, int v) const; - const ShaderProgram& uniform(const char* name, float v) const; - const ShaderProgram& uniform(const char* name, double v) const { + const ShaderProgram &uniform(const char *name, int v) const; + const ShaderProgram &uniform(const char *name, float v) const; + const ShaderProgram &uniform(const char *name, double v) const { return uniform(name, float(v)); } - const ShaderProgram& uniform(const char* name, float v0, float v1) const; - const ShaderProgram& uniform(const char* name, float v0, float v1, + const ShaderProgram &uniform(const char *name, float v0, float v1) const; + const ShaderProgram &uniform(const char *name, float v0, float v1, float v2) const; - const ShaderProgram& uniform(const char* name, float v0, float v1, float v2, + const ShaderProgram &uniform(const char *name, float v0, float v1, float v2, float v3) const; template - const ShaderProgram& uniform(const char* name, const Vec<2, T>& v) const { + const ShaderProgram &uniform(const char *name, const Vec<2, T> &v) const { return uniform(name, v.x, v.y); } template - const ShaderProgram& uniform(const char* name, const Vec<3, T>& v) const { + const ShaderProgram &uniform(const char *name, const Vec<3, T> &v) const { return uniform(name, v.x, v.y, v.z); } template - const ShaderProgram& uniform(const char* name, const Vec<4, T>& v) const { + const ShaderProgram &uniform(const char *name, const Vec<4, T> &v) const { return uniform(name, v.x, v.y, v.z, v.w); } - const ShaderProgram& uniform(const char* name, const Mat<4, float>& m, + const ShaderProgram &uniform(const char *name, const Mat<4, float> &m, bool transpose = false) const { return uniformMatrix4(name, m.elems(), transpose); } template - const ShaderProgram& uniform(const char* name, const Mat<4, T>& m, + const ShaderProgram &uniform(const char *name, const Mat<4, T> &m, bool transpose = false) const { return uniform(name, Mat4f(m), transpose); } template - const ShaderProgram& uniform(const char* name, const Quat& q) const { + const ShaderProgram &uniform(const char *name, const Quat &q) const { // note wxyz => xyzw for GLSL vec4: return uniform(name, q.x, q.y, q.z, q.w); } - const ShaderProgram& uniform1(const char* name, const float* v, + const ShaderProgram &uniform1(const char *name, const float *v, int count = 1) const; - const ShaderProgram& uniform2(const char* name, const float* v, + const ShaderProgram &uniform2(const char *name, const float *v, int count = 1) const; - const ShaderProgram& uniform3(const char* name, const float* v, + const ShaderProgram &uniform3(const char *name, const float *v, int count = 1) const; - const ShaderProgram& uniform4(const char* name, const float* v, + const ShaderProgram &uniform4(const char *name, const float *v, int count = 1) const; - const ShaderProgram& uniformMatrix3(const char* name, const float* v, + const ShaderProgram &uniformMatrix3(const char *name, const float *v, bool transpose = false) const; - const ShaderProgram& uniformMatrix4(const char* name, const float* v, + const ShaderProgram &uniformMatrix4(const char *name, const float *v, bool transpose = false) const; - const ShaderProgram& attribute(int loc, float v) const; - const ShaderProgram& attribute(int loc, float v0, float v1) const; - const ShaderProgram& attribute(int loc, float v0, float v1, float v2) const; - const ShaderProgram& attribute(int loc, float v0, float v1, float v2, + const ShaderProgram &attribute(int loc, float v) const; + const ShaderProgram &attribute(int loc, float v0, float v1) const; + const ShaderProgram &attribute(int loc, float v0, float v1, float v2) const; + const ShaderProgram &attribute(int loc, float v0, float v1, float v2, float v3) const; - const ShaderProgram& attribute(const char* name, float v) const; - const ShaderProgram& attribute(const char* name, float v0, float v1) const; - const ShaderProgram& attribute(const char* name, float v0, float v1, + const ShaderProgram &attribute(const char *name, float v) const; + const ShaderProgram &attribute(const char *name, float v0, float v1) const; + const ShaderProgram &attribute(const char *name, float v0, float v1, float v2) const; - const ShaderProgram& attribute(const char* name, float v0, float v1, float v2, + const ShaderProgram &attribute(const char *name, float v0, float v1, float v2, float v3) const; - const ShaderProgram& attribute1(const char* name, const float* v) const; - const ShaderProgram& attribute2(const char* name, const float* v) const; - const ShaderProgram& attribute3(const char* name, const float* v) const; - const ShaderProgram& attribute4(const char* name, const float* v) const; - const ShaderProgram& attribute1(int loc, const double* v) const; - const ShaderProgram& attribute2(int loc, const double* v) const; - const ShaderProgram& attribute3(int loc, const double* v) const; - const ShaderProgram& attribute4(int loc, const double* v) const; + const ShaderProgram &attribute1(const char *name, const float *v) const; + const ShaderProgram &attribute2(const char *name, const float *v) const; + const ShaderProgram &attribute3(const char *name, const float *v) const; + const ShaderProgram &attribute4(const char *name, const float *v) const; + const ShaderProgram &attribute1(int loc, const double *v) const; + const ShaderProgram &attribute2(int loc, const double *v) const; + const ShaderProgram &attribute3(int loc, const double *v) const; + const ShaderProgram &attribute4(int loc, const double *v) const; template - const ShaderProgram& attribute(int loc, const Vec<2, T>& v) const { + const ShaderProgram &attribute(int loc, const Vec<2, T> &v) const { return attribute(loc, v.x, v.y); } template - const ShaderProgram& attribute(int loc, const Vec<3, T>& v) const { + const ShaderProgram &attribute(int loc, const Vec<3, T> &v) const { return attribute(loc, v.x, v.y, v.z); } template - const ShaderProgram& attribute(int loc, const Vec<4, T>& v) const { + const ShaderProgram &attribute(int loc, const Vec<4, T> &v) const { return attribute(loc, v.x, v.y, v.z, v.w); } template - const ShaderProgram& attribute(int loc, const Quat& q) const { + const ShaderProgram &attribute(int loc, const Quat &q) const { // note wxyz => xyzw for GLSL vec4: return attribute(loc, q.x, q.y, q.z, q.w); } @@ -353,8 +353,8 @@ class ShaderProgram : public ShaderBase { mutable std::unordered_map mUniformLocs, mAttribLocs; // bool mActive; - virtual void get(int pname, void* params) const; - virtual void getLog(char* buf) const; + virtual void get(int pname, void *params) const; + virtual void getLog(char *buf) const; virtual void onCreate(); virtual void onDestroy(); diff --git a/include/al/io/al_AudioIO.hpp b/include/al/io/al_AudioIO.hpp index ed01ba6b..be2e0ba3 100644 --- a/include/al/io/al_AudioIO.hpp +++ b/include/al/io/al_AudioIO.hpp @@ -88,7 +88,7 @@ class AudioBackend { double time(); - bool open(int framesPerSecond, int framesPerBuffer, void *userdata); + bool open(int framesPerSecond, unsigned int framesPerBuffer, void *userdata); bool close(); bool start(int framesPerSecond, int framesPerBuffer, void *userdata); diff --git a/include/al/io/al_AudioIOData.hpp b/include/al/io/al_AudioIOData.hpp index 37d167e3..08f1e19d 100644 --- a/include/al/io/al_AudioIOData.hpp +++ b/include/al/io/al_AudioIOData.hpp @@ -46,7 +46,9 @@ */ #include +#include #include +#include namespace al { @@ -63,13 +65,13 @@ static int resize(T*& buf, int n) { return n; } -// Utility function to efficiently clear buffer (set all to 0) +/// Utility function to efficiently clear buffer (set all to 0) template -static void zero(T* buf, int n) { +static void zero(T* buf, unsigned int n) { memset(buf, 0, n * sizeof(T)); } -// Utility function to deinterleave samples +/// Utility function to deinterleave samples template static void deinterleave(T* dst, const T* src, int numFrames, int numChannels) { int numSamples = numFrames * numChannels; @@ -226,9 +228,8 @@ class AudioIOData { unsigned int channelsOut() const; ///< Get effective number of output channels unsigned int channelsBus() const; ///< Get number of allocated bus channels - unsigned int framesPerBuffer() - const; ///< Get frames/buffer of audio I/O stream - double framesPerSecond() const; ///< Get frames/second of audio I/O streams + uint64_t framesPerBuffer() const; ///< Get frames/buffer of audio I/O stream + double framesPerSecond() const; ///< Get frames/second of audio I/O streams double fps() const { return framesPerSecond(); } double secondsPerBuffer() const; ///< Get seconds/buffer of audio I/O stream diff --git a/include/al/io/al_File.hpp b/include/al/io/al_File.hpp index d2cf390b..dc96031f 100644 --- a/include/al/io/al_File.hpp +++ b/include/al/io/al_File.hpp @@ -47,6 +47,7 @@ */ #include + #include #include #include @@ -287,6 +288,11 @@ class File { /// Convert relative paths to absolute paths static std::string absolutePath(const std::string& path); + /// Returns current path in filesystem + /// This function should be replaced when moved to C++17 to + /// std::filesystem::current_path() + static std::string currentPath(); + /// Returns the base name of path. /// The base name is everything following the last slash. @@ -323,9 +329,9 @@ class File { /// Search for file or directory back from current directory /// @param[in,out] rootPath The input should contain the path to search - /// relative to. If the input is empty, then "./" - ///is assumed. If a match is made, then the output is a string that can be - ///prefixed to 'matchPath' to get the actual location of the match. + /// relative to. If the input is empty, then "./" is assumed. If a match is + /// made, then the output is a string that can be prefixed to 'matchPath' to + /// get the actual location of the match. /// @param[in] matchPath File or directory to search for /// @param[in] maxDepth Maximum number of directories to search back /// \returns whether the file or directory was found @@ -333,18 +339,14 @@ class File { int maxDepth = 6); /// Search for file or directory back from current directory - /// @param[in,out] path Input is a file or directory to search - /// for. - /// If the file is found, the output contains a series - ///of - /// "../" prefixed to the input. Otherwise, the - ///input path is not modified. + /// for. If the file is found, the output contains a series of "../" prefixed + /// to the input. Otherwise, the input path is not modified. /// @param[in] maxDepth Maximum number of directories to search back /// \returns whether the file or directory was found static bool searchBack(std::string& path, int maxDepth = 6); - // TODO: why have these? + // TODO: Implement these. // static al_sec modified(const std::string& path){ return // File(path).modified(); } static al_sec accessed(const std::string& path){ // return File(path).accessed(); } static al_sec created (const std::string& diff --git a/include/al/io/al_MIDI.hpp b/include/al/io/al_MIDI.hpp index 358616e5..20ff9e5a 100644 --- a/include/al/io/al_MIDI.hpp +++ b/include/al/io/al_MIDI.hpp @@ -12,7 +12,7 @@ \brief An abstract base class for realtime MIDI input/output. This class implements some common functionality for the realtime - MIDI input/output subclasses RtMidiIn and RtMidiOut. + MIDI input/output subclasses RtRtMidiIn and RtMidiOut. RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ */ @@ -25,12 +25,13 @@ namespace al { // Internal classes to maintain backward compatibility -class[[depreacated("User RtMidiIn directly")]] MIDIIn : public RtMidiIn{}; +// class[[depreacated("User RtRtMidiIn directly")]] RtMidiIn : public +// RtMidiIn{}; -class[[depreacated("User RtMidiOut directly")]] MIDIOut : public RtMidiOut{}; +// class[[depreacated("User RtMidiOut directly")]] MIDIOut : public RtMidiOut{}; -class[[depreacated("User RtMidiError directly")]] MIDIError - : public RtMidiError{}; +// class[[depreacated("User RtMidiError directly")]] MIDIError +// : public RtMidiError{}; /// Convert note number to Hz value /// @@ -201,7 +202,7 @@ class MIDIMessageHandler { virtual void onMIDIMessage(const MIDIMessage& m) = 0; /// Bind handler to a MIDI input - void bindTo(RtMidiIn& midiIn, unsigned port = 0); + void bindTo(RtMidiIn& RtMidiIn, unsigned port = 0); void clearBindings() { for (auto& binding : mBindings) { diff --git a/include/al/io/al_Window.hpp b/include/al/io/al_Window.hpp index bd5f9d36..ddaea037 100644 --- a/include/al/io/al_Window.hpp +++ b/include/al/io/al_Window.hpp @@ -46,6 +46,7 @@ */ #include + #include #include #include @@ -53,7 +54,9 @@ #include #include +#ifdef AL_WINDOWS #undef DELETE +#endif namespace al { @@ -123,7 +126,7 @@ class Keyboard { void meta(bool state); ///< Set meta key state void shift(bool state); ///< Set shift key state - void print(std::ostream& stream) const; ///< Print keyboard state + void print(std::ostream &stream) const; ///< Print keyboard state protected: friend class WindowImpl; @@ -194,25 +197,25 @@ class WindowEventHandler { virtual ~WindowEventHandler(); /// Called when a keyboard key is pressed - virtual bool keyDown(const Keyboard& k) { return true; } + virtual bool keyDown(const Keyboard &k) { return true; } /// Called when a keyboard key is released - virtual bool keyUp(const Keyboard& k) { return true; } + virtual bool keyUp(const Keyboard &k) { return true; } /// Called when a mouse button is pressed - virtual bool mouseDown(const Mouse& m) { return true; } + virtual bool mouseDown(const Mouse &m) { return true; } /// Called when the mouse moves while a button is down - virtual bool mouseDrag(const Mouse& m) { return true; } + virtual bool mouseDrag(const Mouse &m) { return true; } /// Called when the mouse moves - virtual bool mouseMove(const Mouse& m) { return true; } + virtual bool mouseMove(const Mouse &m) { return true; } /// Called when a mouse button is released - virtual bool mouseUp(const Mouse& m) { return true; } + virtual bool mouseUp(const Mouse &m) { return true; } /// Called when mouse scrolled - virtual bool mouseScroll(const Mouse& m) { return true; } + virtual bool mouseScroll(const Mouse &m) { return true; } /// Called whenever window dimensions change virtual bool resize(int dw, int dh) { return true; } @@ -221,17 +224,17 @@ class WindowEventHandler { virtual bool visibility(bool v) { return true; } /// Return self - WindowEventHandler& windowEventHandler() { return *this; } + WindowEventHandler &windowEventHandler() { return *this; } bool attached() const { return NULL != mWindow; } - Window& window() { return *mWindow; } - const Window& window() const { return *mWindow; } + Window &window() { return *mWindow; } + const Window &window() const { return *mWindow; } - Window* mWindow; + Window *mWindow; private: friend class Window; - WindowEventHandler& window(Window* v) { + WindowEventHandler &window(Window *v) { mWindow = v; return *this; } @@ -247,7 +250,7 @@ class WindowEventHandler { // class Window : public InputEventHandler, public WindowEventHandler { class Window { public: - typedef std::vector WindowEventHandlers; + typedef std::vector WindowEventHandlers; /// Window display mode bit flags enum DisplayMode : unsigned int { @@ -306,10 +309,10 @@ class Window { /// Destroy current window and its associated graphics context void destroy(); - const Keyboard& keyboard() const { + const Keyboard &keyboard() const { return mKeyboard; } ///< Get current keyboard state - const Mouse& mouse() const { return mMouse; } ///< Get current mouse state + const Mouse &mouse() const { return mMouse; } ///< Get current mouse state double aspect() const; ///< Get aspect ratio (width divided by height) bool created() const; ///< Whether window has been created providing a valid @@ -320,7 +323,7 @@ class Window { DisplayMode displayMode() const; ///< Get current display mode bool enabled(DisplayMode v) const; ///< Get whether display mode flag is set bool fullScreen() const; ///< Get whether window is in fullscreen - const std::string& title() const; ///< Get title of window + const std::string &title() const; ///< Get title of window bool visible() const; ///< Get whether window is visible bool vsync() const; ///< Get whether v-sync is enabled @@ -340,7 +343,7 @@ class Window { void cursor(Cursor v); ///< Set cursor type void cursorHide(bool v); ///< Set cursor hiding void cursorHideToggle(); ///< Toggle cursor hiding - void dimensions(const Dim& v); ///< Set dimensions + void dimensions(const Dim &v); ///< Set dimensions void dimensions(int w, int h); ///< Set dimensions void dimensions(int x, int y, int w, int h); ///< Set dimensions void displayMode(DisplayMode v); ///< Set display mode; will recreate window @@ -348,12 +351,12 @@ class Window { /// This will make the window go fullscreen without borders and, /// if posssible, without changing the display resolution. - void fullScreen(bool on); + void fullScreen(bool on, int monitorIndex = 0); void fullScreenToggle(); ///< Toggle fullscreen void hide(); ///< Hide window (if showing) void iconify(); ///< Iconify window // void show(); ///< Show window (if hidden) - void title(const std::string& v); ///< Set title + void title(const std::string &v); ///< Set title void vsync(bool v); ///< Set whether to sync the frame rate to the monitor's ///< refresh rate void decorated(bool b); @@ -362,40 +365,41 @@ class Window { // return false is the event has been consumed and should not propagate // further. - std::function onKeyDown = [](Keyboard const&) { + std::function onKeyDown = [](Keyboard const &) { return true; }; - std::function onKeyUp = [](Keyboard const&) { + std::function onKeyUp = [](Keyboard const &) { return true; }; - std::function onMouseDown = [](Mouse const&) { + std::function onMouseDown = [](Mouse const &) { return true; }; - std::function onMouseUp = [](Mouse const&) { + std::function onMouseUp = [](Mouse const &) { return true; }; - std::function onMouseDrag = [](Mouse const&) { + std::function onMouseDrag = [](Mouse const &) { return true; }; - std::function onMouseMove = [](Mouse const&) { + std::function onMouseMove = [](Mouse const &) { return true; }; - std::function onMouseScroll = [](Mouse const&) { + std::function onMouseScroll = [](Mouse const &) { return true; }; + std::function onResize = [](int, int) {}; - WindowEventHandlers& windowEventHandlers() { return mWindowEventHandlers; } + WindowEventHandlers &windowEventHandlers() { return mWindowEventHandlers; } /// Append handler to window event handler list /// The order of handlers in the list matches their calling order. - Window& append(WindowEventHandler& v); + Window &append(WindowEventHandler &v); /// Prepend handler to input event handler list /// The order of handlers in the list matches their calling order. - Window& prepend(WindowEventHandler& v); + Window &prepend(WindowEventHandler &v); /// Remove all window event handlers matching argument - Window& remove(WindowEventHandler& v); + Window &remove(WindowEventHandler &v); /// Destroy all created windows static void destroyAll(); @@ -434,7 +438,7 @@ class Window { void implSetCursor(); void implSetCursorHide(); void implSetDimensions(); - void implSetFullScreen(); + void implSetFullScreen(int monitorIndex = 0); void implSetTitle(); void implSetVSync(); void implHide(); @@ -443,7 +447,7 @@ class Window { bool implShouldClose(); void implClose(); - Window& insert(WindowEventHandler& v, int i); + Window &insert(WindowEventHandler &v, int i); #define CALL(e) \ { \ @@ -479,26 +483,28 @@ class Window { CALL(keyUp(mKeyboard)); onKeyUp(mKeyboard); } - void callHandlersResize(int w, int h) { CALL(resize(w, h)); } + void callHandlersResize(int w, int h) { + CALL(resize(w, h)); + onResize(w, h); + } void callHandlersVisibility(bool v) { CALL(visibility(v)); } #undef CALL public: - [[deprecated]] Window& add(WindowEventHandler* v) { - return append(*v); - }[[deprecated]] Window& prepend(WindowEventHandler* v) { - return prepend(*v); - } - [[deprecated]] Window& remove(WindowEventHandler* v) { return remove(*v); } + // [[deprecated]] Window& add(WindowEventHandler* v) { return append(*v); } + // [[deprecated]] Window& prepend(WindowEventHandler* v) { return + // prepend(*v); } + // [[deprecated]] Window& remove(WindowEventHandler* v) { return remove(*v); + // } }; -inline Window::DisplayMode operator|(const Window::DisplayMode& a, - const Window::DisplayMode& b) { +inline Window::DisplayMode operator|(const Window::DisplayMode &a, + const Window::DisplayMode &b) { return Window::DisplayMode(+a | +b); } -inline Window::DisplayMode operator&(const Window::DisplayMode& a, - const Window::DisplayMode& b) { +inline Window::DisplayMode operator&(const Window::DisplayMode &a, + const Window::DisplayMode &b) { return Window::DisplayMode(+a & +b); } diff --git a/include/al/protocol/al_OSC.hpp b/include/al/protocol/al_OSC.hpp index d5657ef1..b40a3030 100644 --- a/include/al/protocol/al_OSC.hpp +++ b/include/al/protocol/al_OSC.hpp @@ -46,13 +46,13 @@ Keehong Youn, 2017, younkeehong@gmail.com */ -#include "al/system/al_Thread.hpp" -#include "al/system/al_Time.hpp" - #include #include #include +#include "al/system/al_Thread.hpp" +#include "al/system/al_Time.hpp" + namespace al { /// Open Sound Control (OSC) utilities @@ -89,7 +89,7 @@ class Packet { /// @param[in] contents buffer to copy packet data from /// @param[in] size size, in bytes, of the packet buffer - Packet(const char* contents, int size); + Packet(const char* contents, size_t size); ~Packet(); @@ -101,7 +101,7 @@ class Packet { void printRaw() const; /// Get number of bytes of current packet data - int size() const; + size_t size() const; /// Begin a new bundle Packet& beginBundle(TimeTag timeTag = 1); @@ -303,8 +303,8 @@ class Send : public Packet { /// @param[in] port Port number (valid range is 0-65535) /// @param[in] address IP address - /// @param[in] timeout < 0: block forever; = 0: no blocking; > 0 block with - /// timeout + /// @param[in] timeout < 0: block forever; = 0: no blocking; > 0 block + /// with timeout /// @param[in] size Packet buffer size Send(uint16_t port, const char* address = "localhost", al_sec timeout = 0, int size = 1024); @@ -317,66 +317,66 @@ class Send : public Packet { uint16_t port() const { return mPort; } /// Send and clear current packet contents - int send(); + size_t send(); /// Send a packet - int send(const Packet& p); + size_t send(const Packet& p); /// Send zero argument message immediately - int send(const std::string& addr) { + size_t send(const std::string& addr) { addMessage(addr); return send(); } /// Send one argument message immediately template - int send(const std::string& addr, const A& a) { + size_t send(const std::string& addr, const A& a) { addMessage(addr, a); return send(); } /// Send two argument message immediately template - int send(const std::string& addr, const A& a, const B& b) { + size_t send(const std::string& addr, const A& a, const B& b) { addMessage(addr, a, b); return send(); } /// Send three argument message immediately template - int send(const std::string& addr, const A& a, const B& b, const C& c) { + size_t send(const std::string& addr, const A& a, const B& b, const C& c) { addMessage(addr, a, b, c); return send(); } /// Send four argument message immediately template - int send(const std::string& addr, const A& a, const B& b, const C& c, - const D& d) { + size_t send(const std::string& addr, const A& a, const B& b, const C& c, + const D& d) { addMessage(addr, a, b, c, d); return send(); } /// Send five argument message immediately template - int send(const std::string& addr, const A& a, const B& b, const C& c, - const D& d, const E& e) { + size_t send(const std::string& addr, const A& a, const B& b, const C& c, + const D& d, const E& e) { addMessage(addr, a, b, c, d, e); return send(); } /// Send six argument message immediately template - int send(const std::string& addr, const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f) { + size_t send(const std::string& addr, const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f) { addMessage(addr, a, b, c, d, e, f); return send(); } /// Send seven argument message immediately template - int send(const std::string& addr, const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f, const G& g) { + size_t send(const std::string& addr, const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f, const G& g) { addMessage(addr, a, b, c, d, e, f, g); return send(); } @@ -395,10 +395,10 @@ class Recv { Recv(); /// @param[in] port Port number (valid range is 0-65535) - /// @param[in] address IP address. If empty, will bind all network interfaces - /// to socket. - /// @param[in] timeout < 0: block forever; = 0: no blocking; > 0 block with - /// timeout + /// @param[in] address IP address. If empty, will bind all network + /// interfaces to socket. + /// @param[in] timeout < 0: block forever; = 0: no blocking; > 0 block + /// with timeout Recv(uint16_t port, const char* address = "", al_sec timeout = 0); virtual ~Recv(); diff --git a/include/al/scene/al_DistributedScene.hpp b/include/al/scene/al_DistributedScene.hpp index 6c305891..7a245657 100644 --- a/include/al/scene/al_DistributedScene.hpp +++ b/include/al/scene/al_DistributedScene.hpp @@ -13,7 +13,7 @@ namespace al { class DistributedScene : public DynamicScene, public osc::MessageConsumer { public: DistributedScene(std::string name = "scene", int threadPoolSize = 0, - TimeMasterMode masterMode = TIME_MASTER_CPU); + TimeMasterMode masterMode = TimeMasterMode::TIME_MASTER_CPU); DistributedScene(TimeMasterMode masterMode) : DistributedScene("scene", 0, masterMode) {} diff --git a/include/al/scene/al_DynamicScene.hpp b/include/al/scene/al_DynamicScene.hpp index d7905ee0..35ab5244 100644 --- a/include/al/scene/al_DynamicScene.hpp +++ b/include/al/scene/al_DynamicScene.hpp @@ -52,11 +52,9 @@ #include "al/math/al_Vec.hpp" #include "al/scene/al_SynthSequencer.hpp" #include "al/sound/al_Speaker.hpp" -#include "al/spatial/al_DistAtten.hpp" -#include "al/spatial/al_Pose.hpp" - #include "al/sound/al_StereoPanner.hpp" #include "al/spatial/al_DistAtten.hpp" +#include "al/spatial/al_Pose.hpp" namespace al { /** @@ -98,7 +96,7 @@ class PositionedVoice : public SynthVoice { * @brief Override this function to apply transformations after the internal * transformations of the voice has been applied */ - virtual void preProcess(Graphics &g) {} + virtual void preProcess(Graphics & /*g*/) {} /** * @brief For PositionedVoice, the pose (7 floats) and the size are appended @@ -180,7 +178,7 @@ class ThreadPool { void waitForProcessingDone(); ~ThreadPool(); - unsigned int size() { return workers.size(); } + size_t size() { return workers.size(); } void stopThreads(); @@ -213,7 +211,7 @@ void ThreadPool::enqueue(F &&f, UpdateThreadFuncData &data) { class DynamicScene : public PolySynth { public: DynamicScene(int threadPoolSize = 0, - TimeMasterMode masterMode = TIME_MASTER_AUDIO); + TimeMasterMode masterMode = TimeMasterMode::TIME_MASTER_AUDIO); virtual ~DynamicScene(); @@ -225,7 +223,7 @@ class DynamicScene : public PolySynth { * If not called, the default is stereo panning over two speakers. */ template - std::shared_ptr setSpatializer(SpeakerLayout &sl) { + std::shared_ptr setSpatializer(Speakers &sl) { mSpatializer = std::make_shared(sl); mSpatializer->compile(); return std::static_pointer_cast(mSpatializer); diff --git a/include/al/scene/al_PolySynth.hpp b/include/al/scene/al_PolySynth.hpp index 0847ebf0..f7c941e8 100644 --- a/include/al/scene/al_PolySynth.hpp +++ b/include/al/scene/al_PolySynth.hpp @@ -133,92 +133,7 @@ the other * @return true if able to set the fields */ virtual bool setTriggerParams(std::vector pFields, - bool noCalls = false) { - if (pFields.size() < mTriggerParams.size()) { - // std::cout << "pField count mismatch. Ignoring." << std::endl; - return false; - } - auto it = pFields.begin(); - // Trigger parameters should not trigger callbacks when set through - // this function as these values are initial "construction" values - // If you need the callbacks to propagate, set the parameter values - // directly instead of through these functions. - if (noCalls) { - for (auto ¶m : mTriggerParams) { - if (it->type() == ParameterField::FLOAT) { - if (strcmp(typeid(*param).name(), typeid(Parameter).name()) == 0) { - static_cast(param)->setNoCalls(it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterInt).name()) == 0) { - static_cast(param)->setNoCalls(it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterMenu).name()) == 0) { - static_cast(param)->setNoCalls(it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterString).name()) == 0) { - static_cast(param)->setNoCalls( - std::to_string(it->get())); - } else { - std::cerr << "ERROR: p-field string not setting parameter. Invalid " - "parameter type for parameter " - << param->getFullAddress() << std::endl; - } - } else if (it->type() == ParameterField::STRING) { - if (strcmp(typeid(*param).name(), typeid(ParameterString).name()) == - 0) { - static_cast(param)->setNoCalls( - it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterMenu).name()) == 0) { - static_cast(param)->setCurrent( - it->get(), noCalls); - } else { - std::cerr << "ERROR: p-field string not setting parameter. Invalid " - "parameter type for parameter " - << param->getFullAddress() << std::endl; - } - } - it++; - } - } else { - for (auto ¶m : mTriggerParams) { - if (it->type() == ParameterField::FLOAT) { - if (strcmp(typeid(*param).name(), typeid(Parameter).name()) == 0) { - static_cast(param)->set(it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterInt).name()) == 0) { - static_cast(param)->set(it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterMenu).name()) == 0) { - static_cast(param)->set(it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterString).name()) == 0) { - static_cast(param)->set( - std::to_string(it->get())); - } else { - std::cerr << "ERROR: p-field string not setting parameter. Invalid " - "parameter type for parameter " - << param->getFullAddress() << std::endl; - } - } else if (it->type() == ParameterField::STRING) { - if (strcmp(typeid(*param).name(), typeid(ParameterString).name()) == - 0) { - static_cast(param)->set(it->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterMenu).name()) == 0) { - static_cast(param)->setCurrent( - it->get(), noCalls); - } else { - std::cerr << "ERROR: p-field string not setting parameter. Invalid " - "parameter type for parameter " - << param->getFullAddress() << std::endl; - } - } - it++; - } - } - return true; - } + bool noCalls = false); /** * @brief Get this instance's parameter fields @@ -232,26 +147,7 @@ the other * Copy the values from the internal parameters that have been * registered using registerParameterAsField or the << operator. */ - int getTriggerParams(float *pFields, int maxParams = -1) { - std::vector pFieldsVector = getTriggerParams(); - if (maxParams == -1) { - assert(pFieldsVector.size() < INT_MAX); - maxParams = int(pFieldsVector.size()); - } - int count = 0; - for (auto param : pFieldsVector) { - if (count == maxParams) { - break; - } - if (param.type() == ParameterField::FLOAT) { - *pFields++ = param.get(); - } else { - *pFields++ = 0.0f; // Ignore strings... - } - count++; - } - return count; - } + int getTriggerParams(float *pFields, int maxParams = -1); /** * @brief Get this instance's parameter fields @@ -263,24 +159,7 @@ the other * operator. Override this function in your voice if you need a different * behavior. */ - virtual std::vector getTriggerParams() { - std::vector pFields; - pFields.reserve(mTriggerParams.size()); - for (auto param : mTriggerParams) { - if (param) { - if (strcmp(typeid(*param).name(), typeid(ParameterString).name()) == - 0) { - pFields.push_back(static_cast(param)->get()); - } else if (strcmp(typeid(*param).name(), - typeid(ParameterMenu).name()) == 0) { - pFields.push_back(static_cast(param)->getCurrent()); - } else { - pFields.push_back(param->toFloat()); - } - } - } - return pFields; - } + virtual std::vector getTriggerParams(); /** * @brief Override this function to define audio processing. @@ -290,12 +169,12 @@ the other * free() function when envelopes or processing is done. You should * call free() from one of the render() functions. */ - virtual void onProcess(AudioIOData &io) {} + virtual void onProcess(AudioIOData & /*io*/) {} /** * @brief Override this function to define graphics for this synth */ - virtual void onProcess(Graphics &g) {} + virtual void onProcess(Graphics & /*g*/) {} /** * @brief Override this function to update internal state, e.g. from an @@ -303,7 +182,7 @@ the other * * dt is the delta time elapsed since last update */ - virtual void update(double dt = 0) {} + virtual void update(double dt = 0) { (void)dt; } /** * @brief Override this function to initialize internal data. @@ -345,11 +224,7 @@ the other * This function can be called to programatically trigger a voice. * It is used for example in PolySynth to trigger a voice. */ - void triggerOn(int offsetFrames = 0) { - mOnOffsetFrames = offsetFrames; - mActive = true; - onTriggerOn(); - } + void triggerOn(int offsetFrames = 0); /** * @brief Call the voice's onTriggerOff() function to begin note's @@ -359,12 +234,7 @@ the other * This function can be called to programatically trigger the release of a * voice */ - void triggerOff(int offsetFrames = 0) { - mOffOffsetFrames = - offsetFrames; // TODO implement offset frames for trigger off. - // Currently ignoring and turning off at start of buffer - onTriggerOff(); - } + void triggerOff(int offsetFrames = 0); /** * @brief Set the id for this voice @@ -402,43 +272,44 @@ the other */ unsigned int numOutChannels() { return mNumOutChannels; } + /** + * @brief A convenience function for quick creation of a managed parameter + * @return a shared pointer to the created Parameter + * + * This creates a float type Parameter. The parameters instantiated + * through this function can be queried and set quickly through + * getInternalParameter(), getInternalParameterValue() and + * setInternalParameterValue(). These functions provide a quick way to add + * trigger parameters to the synth. These parameters are registered + * automatically, so they will be used in automatic GUIs and with the + * SynthSequencer. + * + */ std::shared_ptr createInternalTriggerParameter( std::string name, float defaultValue = 0.0, float minValue = -9999.0, - float maxValue = 9999.0) { - mInternalParameters.push_back( - std::make_shared(name, defaultValue, minValue, maxValue)); - registerTriggerParameter(*mInternalParameters.back().get()); - return mInternalParameters.back(); - } + float maxValue = 9999.0); - Parameter &getInternalParameter(std::string name) { - for (auto param : mInternalParameters) { - if (param->getName() == name && - strcmp(typeid(*param).name(), typeid(Parameter).name()) == 0) { - return *param; - } - } - std::cerr << "Parameter not found! Aborting: " << name << std::endl; - throw "Invalid parameter name"; - } + /** + * @brief Get reference to internal Parameter + * @param name parameter name + * @return reference to the parameter + * + * Internal parameters are those registered through + * createInternalTriggerParameter() + */ + Parameter &getInternalParameter(std::string name); - float getInternalParameterValue(std::string name) { - for (auto param : mInternalParameters) { - if (param->getName() == name) { - return param->get(); - } - } - return 0.0; - } + /** + * @brief Get value for internal trigger parameter + * @param name name of the parameter + * @return current float value of the parameter + */ + float getInternalParameterValue(std::string name); - void setInternalParameterValue(std::string name, float value) { - for (auto param : mInternalParameters) { - if (param->getName() == name) { - param->set(value); - // return; - } - } - } + /** + * @brief Set value for internal trigger parameter + */ + void setInternalParameterValue(std::string name, float value); /** * @brief Register a parameter as a "trigger" parameter @@ -504,6 +375,10 @@ the other return *this; } + /** + * @brief Get the Voice's continuous (i.e. not "trigger") parameters + * @return vector with pointers to parameters + */ std::vector parameters() { return mContinuousParameters; } /** @@ -555,16 +430,9 @@ the other */ class PolySynth { public: - typedef enum { - TIME_MASTER_AUDIO, - TIME_MASTER_GRAPHICS, - TIME_MASTER_ASYNC, - TIME_MASTER_CPU - } TimeMasterMode; - friend class SynthSequencer; - PolySynth(TimeMasterMode masterMode = TIME_MASTER_AUDIO); + PolySynth(TimeMasterMode masterMode = TimeMasterMode::TIME_MASTER_AUDIO); virtual ~PolySynth(); @@ -791,39 +659,26 @@ class PolySynth { std::function cb, - void *userData = nullptr) { - TriggerOnCallback cbNode(cb, userData); - mTriggerOnCallbacks.push_back(cbNode); - } + void *userData = nullptr); /** * @brief register a callback to be notified of a trigger off event */ void registerTriggerOffCallback( - std::function cb, - void *userData = nullptr) { - TriggerOffCallback cbNode(cb, userData); - mTriggerOffCallbacks.push_back(cbNode); - } + std::function cb, void *userData = nullptr); /** * @brief register a callback to be notified of a note is moved to the free * pool from the active list */ void registerFreeCallback(std::function cb, - void *userData = nullptr) { - FreeCallback cbNode(cb, userData); - mFreeCallbacks.push_back(cbNode); - } + void *userData = nullptr); /** * @brief register a callback to be notified of allocation of a voice. */ void registerAllocateCallback( std::function cb, - void *userData = nullptr) { - AllocationCallback cbNode(cb, userData); - mAllocationCallbacks.push_back(cbNode); - } + void *userData = nullptr); /** * Register a SynthVoice class to allow instantiating it by name @@ -894,7 +749,7 @@ class PolySynth { * Always call prepare() after calling this function. The changes are only * applied by prepare(). */ - void setVoiceMaxOutputChannels(unsigned int channels) { + void setVoiceMaxOutputChannels(uint16_t channels) { mVoiceMaxOutputChannels = channels; } @@ -905,9 +760,7 @@ class PolySynth { * Always call prepare() after calling this function. The changes are only * applied by prepare(). */ - void setVoiceBusChannels(unsigned int channels) { - mVoiceBusChannels = channels; - } + void setVoiceBusChannels(uint16_t channels) { mVoiceBusChannels = channels; } typedef const std::function @@ -925,6 +778,12 @@ class PolySynth { mBusRoutingCallback = std::make_shared(cb); } + /** + * @brief Set the time in seconds to wait between sequencer updates when time + * master is CPU. + * + * This has no effect if time master mode is not TIME_MASTER_CPU + */ void setCpuClockGranularity(double timeSecs) { mCpuGranularitySec = timeSecs; } @@ -1059,7 +918,7 @@ class PolySynth { if (mAudioGain != 1.0f) { for (unsigned int i = 0; i < io.channelsOut(); i++) { float *buffer = io.outBuffer(i); - unsigned int samps = io.framesPerBuffer(); + uint64_t samps = io.framesPerBuffer(); while (samps--) { *buffer++ *= mAudioGain; } @@ -1067,43 +926,26 @@ class PolySynth { } } - virtual void prepare(AudioIOData &io) { - internalAudioIO.framesPerBuffer(io.framesPerBuffer()); - internalAudioIO.channelsIn(mVoiceMaxInputChannels); - internalAudioIO.channelsOut(mVoiceMaxOutputChannels); - internalAudioIO.channelsBus(mVoiceBusChannels); - if ((int)io.channelsBus() < mVoiceBusChannels) { - std::cout << "WARNING: You don't have enough buses in AudioIO object. " - "This is likely to crash." - << std::endl; - } - // mThreadedAudioData.resize(mAudioThreads.size()); - // for (auto &threadio: mThreadedAudioData) { - // threadio.framesPerBuffer(io.framesPerBuffer()); - // threadio.channelsIn(mVoiceMaxInputChannels); - // threadio.channelsOut(mVoiceMaxOutputChannels); - // threadio.channelsBus(mVoiceBusChannels); - // } - m_internalAudioConfigured = true; - } + virtual void prepare(AudioIOData &io); - // Internal voices are allocated in PolySynth and shared with the outside. - SynthVoice *mVoicesToInsert{ - nullptr}; // Voices to be inserted in the realtime context - SynthVoice *mFreeVoices{nullptr}; // Allocated voices available for reuse - SynthVoice *mActiveVoices{ - nullptr}; // Dynamic voices that are currently active. Only modified - // within the master domain (set by mMasterMode) + /// Voices to be inserted in the realtime context. Internal voices are + /// allocated in PolySynth and shared with the outside. + SynthVoice *mVoicesToInsert{nullptr}; + /// Allocated voices available for reuse + SynthVoice *mFreeVoices{nullptr}; + /// Dynamic voices that are currently active. Only modified + /// within the master domain (set by mMasterMode) + SynthVoice *mActiveVoices{nullptr}; std::mutex mVoiceToInsertLock; std::mutex mFreeVoiceLock; std::mutex mGraphicsLock; bool m_useInternalAudioIO = true; bool m_internalAudioConfigured = false; - // Internal AudioIOData characteristics. Set these - int mVoiceMaxOutputChannels = 2; - int mVoiceMaxInputChannels = 0; - int mVoiceBusChannels = 0; + + uint16_t mVoiceMaxOutputChannels = 2; + uint16_t mVoiceMaxInputChannels = 0; + uint16_t mVoiceBusChannels = 0; std::shared_ptr mBusRoutingCallback; AudioIOData internalAudioIO; @@ -1135,8 +977,8 @@ class PolySynth { int mIdCounter{1000}; - bool mAllNotesOff{ - false}; // Flag used to notify processing to turn off all voices + // Flag used to notify processing to turn off all voices + bool mAllNotesOff{false}; typedef std::function VoiceCreatorFunc; typedef std::map Creators; @@ -1144,9 +986,8 @@ class PolySynth { void *mDefaultUserData{nullptr}; Creators mCreators; - std::vector - mNoAllocationList; // Disallow auto allocation for class name. Set in - // allocateVoice() + // Disallow auto allocation for class name. Set in allocateVoice() + std::vector mNoAllocationList; bool mRunCPUClock{true}; double mCpuGranularitySec = 0.001; // 1ms diff --git a/include/al/scene/al_SequencerMIDI.hpp b/include/al/scene/al_SequencerMIDI.hpp index 7a52018c..4d6b8f35 100644 --- a/include/al/scene/al_SequencerMIDI.hpp +++ b/include/al/scene/al_SequencerMIDI.hpp @@ -62,12 +62,12 @@ class SequencerMIDI : public MIDIMessageHandler { SequencerMIDI() {} SequencerMIDI(int deviceIndex) : mSynth(nullptr) { - MIDIMessageHandler::bindTo(mMidiIn); + MIDIMessageHandler::bindTo(mRtMidiIn); try { - mMidiIn.openPort(deviceIndex); + mRtMidiIn.openPort(deviceIndex); printf("PresetMIDI: Opened port to %s\n", - mMidiIn.getPortName(deviceIndex).c_str()); - } catch (al::MIDIError error) { + mRtMidiIn.getPortName(deviceIndex).c_str()); + } catch (RtMidiError error) { std::cout << "PresetMIDI Warning: Could not open MIDI port " << deviceIndex << std::endl; } @@ -86,32 +86,32 @@ class SequencerMIDI : public MIDIMessageHandler { } void open(int deviceIndex) { - MIDIMessageHandler::bindTo(mMidiIn); + MIDIMessageHandler::bindTo(mRtMidiIn); - if (mMidiIn.isPortOpen()) { - mMidiIn.closePort(); + if (mRtMidiIn.isPortOpen()) { + mRtMidiIn.closePort(); } try { - if (deviceIndex >= 0 && deviceIndex < (int)mMidiIn.getPortCount()) { - mMidiIn.openPort(deviceIndex); + if (deviceIndex >= 0 && deviceIndex < (int)mRtMidiIn.getPortCount()) { + mRtMidiIn.openPort(deviceIndex); printf("PresetMIDI: Opened port to %s\n", - mMidiIn.getPortName(deviceIndex).c_str()); + mRtMidiIn.getPortName(deviceIndex).c_str()); } else { std::cerr << "PresetMIDI Warning: Could not open MIDI port " << deviceIndex << std::endl; } - } catch (al::MIDIError error) { + } catch (RtMidiError error) { std::cerr << "PresetMIDI Warning: Could not open MIDI port " << deviceIndex << std::endl; } } void close() { - mMidiIn.closePort(); + mRtMidiIn.closePort(); MIDIMessageHandler::clearBindings(); } - bool isOpen() { return mMidiIn.isPortOpen(); } + bool isOpen() { return mRtMidiIn.isPortOpen(); } void setSynthSequencer(PolySynth &synth) { mSynth = &synth; } @@ -166,7 +166,7 @@ class SequencerMIDI : public MIDIMessageHandler { private: PolySynth *mSynth; - MIDIIn mMidiIn; + RtMidiIn mRtMidiIn; std::vector> mNoteOnFunctions; std::vector> mNoteOffFunctions; }; diff --git a/include/al/scene/al_SynthSequencer.hpp b/include/al/scene/al_SynthSequencer.hpp index e87e52fb..11caeadf 100644 --- a/include/al/scene/al_SynthSequencer.hpp +++ b/include/al/scene/al_SynthSequencer.hpp @@ -122,7 +122,7 @@ struct SynthEvent { * the audio callback (e.g. onSound() ) or the graphics callback (e.g. onDraw()) * * A time master can be selected in the constructor to define where the - * sequencer runs. TIME_MASTER_AUDIO is more precise in time, but you might want + * sequencer runs. TimeMasterMode::TIME_MASTER_AUDIO is more precise in time, but you might want * to use TIME_MASTER_GRAPHICS if your "note" produces no audio. * * Sequences can also be read from text files with the extension @@ -169,7 +169,7 @@ struct SynthEvent { class SynthSequencer { public: SynthSequencer( - PolySynth::TimeMasterMode masterMode = PolySynth::TIME_MASTER_CPU) { + TimeMasterMode masterMode = TimeMasterMode::TIME_MASTER_CPU) { mInternalSynth = std::make_unique(masterMode); registerSynth(*mInternalSynth.get()); } @@ -295,7 +295,7 @@ class SynthSequencer { std::mutex mLoadingLock; bool mPlaying{false}; - PolySynth::TimeMasterMode mMasterMode{PolySynth::TIME_MASTER_AUDIO}; + TimeMasterMode mMasterMode{TimeMasterMode::TIME_MASTER_AUDIO}; double mMasterTime{0.0}; double mPlaybackStartTime{0.0}; diff --git a/include/al/sound/al_Ambisonics.hpp b/include/al/sound/al_Ambisonics.hpp index 6102e70e..653bdf76 100644 --- a/include/al/sound/al_Ambisonics.hpp +++ b/include/al/sound/al_Ambisonics.hpp @@ -50,6 +50,7 @@ */ #include + #include #include "al/math/al_Vec.hpp" @@ -219,17 +220,17 @@ class AmbiDecode : public AmbiBase { void setSpeaker(int index, int deviceChannel, float azimuth, float elevation = 0, float amp = 1.f); - // void zero(); ///< Zeroes out internal ambisonic - // frame. + // void zero(); ///< Zeroes out internal + // ambisonic frame. void setSpeakers(Speakers* spkrs); void setSpeakers(Speakers& spkrs); - // float * azimuths(); ///< Returns pointer to speaker - //azimuths. + // float * azimuths(); ///< Returns pointer to + // speaker azimuths. // float * elevations(); ///< Returns pointer to speaker - //elevations. float * frame() const; ///< Returns pointer to - //ambisonic channel frame used by decode(int) + // elevations. float * frame() const; ///< Returns + // pointer to ambisonic channel frame used by decode(int) /// Get speaker Speaker& speaker(int num) { return mSpeakers[num]; } @@ -317,7 +318,8 @@ class AmbiEncode : public AmbiBase { /// @ingroup Sound class AmbisonicsSpatializer : public Spatializer { public: - AmbisonicsSpatializer(SpeakerLayout& sl, int dim = 2, int order = 1, + AmbisonicsSpatializer(); + AmbisonicsSpatializer(Speakers& sl, int dim = 2, int order = 1, int flavor = 1); void zeroAmbi(); @@ -431,12 +433,12 @@ inline float AmbiDecode::decode(float* encFrame, int encNumChannels, // AmbiEncode // inline void AmbiEncode::encode(const AmbiDecode &dec, float input){ // for(int c=0; c -#include -#include -#include -#include - -#include "al/io/al_AudioIO.hpp" -#include "al/math/al_Interpolation.hpp" -#include "al/math/al_Vec.hpp" -#include "al/sound/al_Biquad.hpp" -#include "al/sound/al_Reverb.hpp" -#include "al/sound/al_Spatializer.hpp" -#include "al/sound/al_Speaker.hpp" -#include "al/spatial/al_DistAtten.hpp" -#include "al/spatial/al_Pose.hpp" -#include "al/types/al_Buffer.hpp" - -namespace al { - -/*! - A note on coordinate conventions - - The cartesian coordinate system used for Ambisonics is: - +x is forward - +y is left - +z is up - - The polar coordinate system is as follows: - Azimuth is the angle between the xz-plane and the source. From - the listener's perspective, a positive azimuth is leftward (towards +y) and - negative is rightwards (towards -y). Elevation is the angle between the - xy-plane and the source. From the listener's perspective, a positive - elevation is upward (towards +z) and negative is downward (towards -z). - - The cartesian coordinate system used in Allocore's OpenGL is: - +x is right - +y is up - +z is backward - - The correct OpenGL to Ambisonics conversion is thus: - ambi_x = -gl_z; - ambi_y = -gl_x; - ambi_z = gl_y; -*/ - -class Listener; -class SoundSource; - -/// Base class for an object (listener or source) in an audio scene - -/// This contains a "pose" to represent the position and orientation of a -/// scene object and a buffer of previous positions that can be used to -/// generate smooth, "zipper"-free trajectories at audio rate. -/// -/// @ingroup Sound -class [[deprecated("use DynamicScene instead")]] AudioSceneObject { - public: - /// Set current pose - void pose(const Pose& p) { mPose.set(p); } - Pose& pose() { return mPose; } - - /// Set position - void pos(double x, double y, double z = 0.) { mPose.pos(x, y, z); } - - /// Get current pose - const Pose& pose() const { return mPose; } - - /// Get current position - const Vec3d& pos() const { return mPose.pos(); } - - /// Get history of positions - const ShiftBuffer<4, Vec3d>& posHistory() const { return mPosHistory; } - - void updateHistory(); - - protected: - Pose mPose; // current position/orientation - ShiftBuffer<4, Vec3d> mPosHistory; // previous positions -}; - -/// A listener in an audio space. Listener objects are not created by the user -/// but instantiated using the createListener() function from the AudioScene -/// class. -/// A Listener is tied to a spatialization technique when it is created. -/// -/// @ingroup Sound -class[[deprecated("use DynamicScene instead")]] Listener - : public AudioSceneObject { - friend class SoundSource; - - public: - /// Get history of orientations - const std::vector& quatHistory() const { return mQuatHistory; } - - void compile(); - - void updateHistory(int numFrames); - - protected: - friend class AudioScene; - - Listener(int numFrames, Spatializer* spatialier); - - void numFrames(unsigned v); - - Spatializer* mSpatializer; - std::vector mQuatHistory; // buffer of interpolated orientations - Quatd mQuatPrev; // orientation in previous block - bool mIsCompiled; -}; - -/// A sound source - -/// Doppler Types -enum DopplerType { - DOPPLER_NONE = 0, /**< Do not use Doppler Shift */ - DOPPLER_SYMMETRICAL, /**< Symmetrical Frequency Shift (not physically - accurate) */ - DOPPLER_PHYSICAL /**< Physically Accurate Doppler Shift. Requires per sample - processing for AudioScene and SoundSource. */ -}; - -/// The attenuation policy may be different per source, i.e., because a bee has -/// a different attenuation characteristic than an airplane. -/// -/// @ingroup Sound -class[[deprecated("use DynamicScene instead")]] SoundSource - : public AudioSceneObject, - public DistAtten { - public: - /// @param[in] nearClip Distance below which amplitude is clamped to - /// 1 - /// @param[in] farClip Distance above which amplitude reaches its - /// mimumum - /// @param[in] law Attenuation law - /// @param[in] farBias Amplitude at far clip (the minimum - /// amplitude) - /// @param[in] delaySize Size of internal delay line. This should be - /// large enough for the most distant - ///sound: samples = sampleRate * (near + range)/speedOfSound - SoundSource(double nearClip = 0.1, double farClip = 20, - AttenuationLaw law = ATTEN_INVERSE, - DopplerType dopplerType = DOPPLER_SYMMETRICAL, - double sampleRate = 44100, double farBias = 0, - int delaySize = 100000); - - virtual ~SoundSource() {} - - float getNextSample(Pose listeningPose) { - float s = 0.0f; - double dist = - listeningPose.vec().mag(); // Compute distance in world-space units - Vec3d relDirection = posHistory()[0] - listeningPose.vec(); - - Quatd srcRot = listeningPose.quat(); - relDirection = srcRot.rotate(relDirection); - double distanceToSample = 0; - if (dopplerType() == DOPPLER_SYMMETRICAL) { - distanceToSample = mSampleRate / mSpeedOfSound; - } else if (dopplerType() == DOPPLER_PHYSICAL) { - // FIXME AC Can we use the current pose here for distance or should we - // calculate from listener history? - double prevDistance = (posHistory()[1] - listeningPose.vec()).mag(); - double sourceVel = - (dist - prevDistance) * - mSampleRate; // positive when moving away, negative moving toward - // if(sourceVel == -mSpeedOfSound) sourceVel -= - //0.001; //prevent divide by 0 / inf freq - double sumSpeed = mSpeedOfSound + sourceVel; - if (sumSpeed < 0.001) { - sumSpeed = 0.001; - } - - distanceToSample = fabs(mSampleRate / sumSpeed); - } - updateHistory(); - - // Compute how many samples ago to read from buffer - // Start with time delay due to speed of sound - double samplesAgo = dist * distanceToSample; - - // // Add on time delay (in samples) - only needed if the source is - //rendered per buffer if(!usePerSampleProcessing()) - samplesAgo += mFrameCounter++; - - // Is our delay line big enough? - if (samplesAgo <= maxIndex()) { - double gain = attenuation(dist); - // std::cout << dist << ":" << gain << std::endl; - - // This seemed to get the same sample per block - // float s = src.readSample(samplesAgo) * gain; - - // reading samplesAgo-i causes a discontinuity - s = readSample(samplesAgo) * gain; - - // s = src.presenceFilter(s); //TODO: causing stopband ripple here, why? - } - return s; - } - - float getNextSample(Listener & l) { return getNextSample(l.pose()); } - - void getBuffer(Listener & l, float* buffer, const int size) { - frame(0); - for (int i = 0; i < size; i++) { - *buffer++ = getNextSample(l); - } - } - - void getBuffer(Pose listeningPose, float* buffer, const int size) { - frame(0); - for (int i = 0; i < size; i++) { - *buffer++ = getNextSample(listeningPose); - } - } - - void frame(int v) { mFrameCounter = v; } - - /// Read sample from delay-line using linear interpolation - /// The index specifies how many samples ago by which to read back from - /// the buffer. The index must be less than or equal to bufferSize()-2. - float readSample(double index) const { - int index0 = index; - float a = mSound.read(index0); - float b = mSound.read(index0 + 1); - float frac = index - index0; - // return ipl::linear(frac, a, b); - - float a0 = mSound.read(index0 - 1); - float b1 = mSound.read(index0 + 2); - return ipl::cubic(frac, a0, a, b, b1); - } - - /// Returns whether distance-based attenuation is enabled - bool useAttenuation() const { return mUseAtten; } - /// Enable/disable distance-based gain attenuation - void useAttenuation(bool enable) { mUseAtten = enable; } - - /// Returns Doppler Type - DopplerType dopplerType() const { return mDopplerType; } - /// Set Doppler Type - void dopplerType(DopplerType type) { mDopplerType = type; } - - /// Returns attentuation factor based on distance to listener - double attenuation(double distance) const { - return mUseAtten ? DistAtten::attenuation(distance) : 1.0; - } - - /// Write sample to internal delay-line - void writeSample(float v) { mSound.next() = v; } - - /// Returns whether the source is using per sample processing - /// (AudioScene::render will call this source's onProcessSample) - bool usePerSampleProcessing() const { return mUsePerSampleProcessing; } - - /// Enable per sample processing - void usePerSampleProcessing(bool shouldUsePerSampleProcessing) { - mUsePerSampleProcessing = shouldUsePerSampleProcessing; - } - - /// Get size of delay in samples - int delaySize() const { return mSound.size(); } - - /// Convert delay, in seconds, to an index - double delayToIndex(double delay, double sampleRate) const { - if (mDopplerType == DOPPLER_NONE) return 0; - return delay * sampleRate; - } - - /// Returns maximum number of seconds of delay - double maxDelay(double sampleRate) const { return delaySize() / sampleRate; } - - /// Returns maximum index that can be used for reading samples - int maxIndex() const { return delaySize() - 2; } - - // calculate the buffersize needed for given samplerate, speed of sound & - // distance traveled (e.g. nearClip+clipRange). probably want to add - // io.samplesPerBuffer() to this for safety. - static int bufferSize(double samplerate, double speedOfSound, - double distance); - - /// Set speed of sound for computation in m/s - void setSpeedOfSound(float speedOfSound) { mSpeedOfSound = speedOfSound; } - - void cachedIndex(unsigned int v) { - mCachedIndex = v; - } // FIXME: For VBAP. There should be a better place for this - unsigned int cachedIndex() { return mCachedIndex; } - - private: - RingBuffer mSound; // spherical wave around position - bool mUseAtten; - DopplerType mDopplerType; - bool mUsePerSampleProcessing; - unsigned int mCachedIndex; // for VBAP with multiple sources - float mSampleRate; - float mSpeedOfSound; - int mFrameCounter; - - BiQuadNX presenceFilter; // used for presence filtering and spatial - // modulation BW control -}; - -/// An audio scene consisting of Listeners and Sources. -/// -/// @ingroup Sound -class [[deprecated("use DynamicScene instead")]] AudioScene { - public: - /// A set of listeners - typedef std::vector Listeners; - - /// A set of sources - typedef std::list Sources; - - /// @param[in] numFrames block size of audio buffers - AudioScene(int numFrames); - - ~AudioScene(); - - /// Get listeners - Listeners& listeners() { return mListeners; } - const Listeners& listeners() const { return mListeners; } - - /// Get sources - Sources& sources() { return mSources; } - const Sources& sources() const { return mSources; } - - void numFrames(int v); - - /// Create a new listener for this scene using the given spatializer - - /// The returned Listener is allocated internally and will be deleted - /// in the AudioScene destructor. - Listener* createListener(Spatializer * spatializer); - - /// Add a sound source to scene - void addSource(SoundSource & src); - - /// Remove a sound source from scene - void removeSource(SoundSource & src); - - /// Perform rendering - void render(AudioIOData & io); - - /// Set per sample processing (false by default) - /// Per sample processing is useful for smoother doppler and gain - /// interpolation for high-speed sources, but uses much more CPU. - /// When set to true, smoothing of the position of sources - /// is performed within the audio buffer using a 4 point moving average - /// When false, the position of a source will be static within the audio - /// processing block. - /// Turn off to reduce CPU for large number of sources and/or Doppler shift - /// not required. - void usePerSampleProcessing(bool shouldUsePerSampleProcessing) { - mPerSampleProcessing = shouldUsePerSampleProcessing; - } - - protected: - Listeners mListeners; - Sources mSources; - int mNumFrames; // audio frames per block - std::vector mBuffer; // temporary frame buffer - bool mPerSampleProcessing; -}; - -} // namespace al -#endif diff --git a/include/al/sound/al_Dbap.hpp b/include/al/sound/al_Dbap.hpp index 0aecf052..4b2ea3e8 100644 --- a/include/al/sound/al_Dbap.hpp +++ b/include/al/sound/al_Dbap.hpp @@ -64,7 +64,7 @@ class Dbap : public Spatializer { public: /// @param[in] sl A speaker layout /// @param[in] focus Amplitude focus to nearby speakers - Dbap(const SpeakerLayout& sl, float focus = 1.f); + Dbap(const Speakers& sl, float focus = 1.f); virtual void renderSample(AudioIOData& io, const Pose& listeningPose, const float& sample, diff --git a/include/al/sound/al_Lbap.hpp b/include/al/sound/al_Lbap.hpp index 9763fce3..d08cb55d 100644 --- a/include/al/sound/al_Lbap.hpp +++ b/include/al/sound/al_Lbap.hpp @@ -59,13 +59,13 @@ namespace al { class LdapRing { public: - LdapRing(SpeakerLayout &sl) { + LdapRing(Speakers &sl) { vbap = std::make_shared(sl); elevation = 0; - for (auto speaker : sl.speakers()) { + for (auto speaker : sl) { elevation += speaker.elevation; } - elevation /= sl.numSpeakers(); // store average elevation + elevation /= sl.size(); // store average elevation vbap->compile(); } @@ -84,7 +84,7 @@ class Lbap : public Spatializer { } VbapOptions; /// @param[in] sl A speaker layout - Lbap(const SpeakerLayout &sl) : Spatializer(sl) {} + Lbap(const Speakers &sl) : Spatializer(sl) {} virtual ~Lbap() override { if (buffer) { diff --git a/include/al/sound/al_SoundFile.hpp b/include/al/sound/al_SoundFile.hpp index 2f9e6986..ecfc9b20 100644 --- a/include/al/sound/al_SoundFile.hpp +++ b/include/al/sound/al_SoundFile.hpp @@ -6,11 +6,13 @@ namespace al { -/// @brief Read sound file and store the data in float array (interleaved) -/// @ingroup Sound -/// Reading supports wav, flac -/// Implementation uses "dr libs" (https://github.com/mackron/dr_libs) - +/** + * @brief Read sound file and store the data in float array (interleaved) + * @ingroup Sound + * + * Reading supports wav, flac + * Implementation uses "dr libs" (https://github.com/mackron/dr_libs) + */ struct SoundFile { std::vector data; int sampleRate = 0; @@ -37,11 +39,6 @@ struct SoundFile { SoundFile getResampledSoundFile(SoundFile* toConvert, unsigned int newSampleRate); -// only supports wav -// TODO - implement -// void writeSoundFile (const char* filename, float* data, int frameCount, -// int sampleRate, int channels); - /// @brief Soundfile player class /// @ingroup Sound struct SoundFilePlayer { @@ -63,10 +60,42 @@ struct SoundFilePlayer { // // ~SoundFilePlayer() = default; - void getFrames(int numFrames, float* buffer, int bufferLength); + void getFrames(uint64_t numFrames, float* buffer, int bufferLength); }; -/// @brief Soundfile player class +/** + * @brief The SoundFileStreaming class provides reading soundifle directly from + * disk one buffer at a time. + * + * This is a simple reading class with few options, if you need more + * comprehensive support, use the soundfile module in al_ext + */ +class SoundFileStreaming { + public: + SoundFileStreaming(const char* path = nullptr); + ~SoundFileStreaming(); + + bool isOpen() { return mImpl != nullptr; } + + /// Sampling rate of file. Call after open has returned true. + uint32_t sampleRate(); + /// Total number of frames in file. Call after open has returned true. + uint64_t totalFrames(); + /// Number of channels in file. Call after open has returned true. + uint16_t numChannels(); + + /// Open file for reading. + bool open(const char* path); + /// Close file and cleanup + void close(); + /// Read interleaved frames into preallocated buffer; + uint64_t getFrames(uint64_t numFrames, float* buffer); + + private: + void* mImpl{nullptr}; +}; + +/// @brief Soundfile player class with thread-safe access to playback controls /// @ingroup Sound struct SoundFilePlayerTS { SoundFile soundFile; diff --git a/include/al/sound/al_Spatializer.hpp b/include/al/sound/al_Spatializer.hpp index 741380dc..991977f7 100644 --- a/include/al/sound/al_Spatializer.hpp +++ b/include/al/sound/al_Spatializer.hpp @@ -60,7 +60,7 @@ namespace al { class Spatializer { public: /// @param[in] sl A speaker layout to use - Spatializer(const SpeakerLayout& sl); + Spatializer(const Speakers& sl); virtual ~Spatializer() {} diff --git a/include/al/sound/al_Speaker.hpp b/include/al/sound/al_Speaker.hpp index 93c9030a..51795e8c 100644 --- a/include/al/sound/al_Speaker.hpp +++ b/include/al/sound/al_Speaker.hpp @@ -46,6 +46,7 @@ #include #include + #include "al/math/al_Constants.hpp" #include "al/math/al_Vec.hpp" @@ -69,13 +70,7 @@ class Speaker { /// @param[in] radius radius of speaker /// @param[in] gain gain of speaker Speaker(unsigned int deviceChan = 0, float az = 0.f, float el = 0.f, - int group = 0, float radius = 1.f, float gain = 1.f) - : deviceChannel(deviceChan), - azimuth(az), - elevation(el), - group(group), - radius(radius), - gain(gain) {} + int group = 0, float radius = 1.f, float gain = 1.f); /// Get position in Cartesian coordinate (in audio space) template @@ -90,47 +85,13 @@ class Speaker { return *this; } - void posCart2(Vec3d xyz) { - using namespace std; - - radius = - sqrt(float((xyz[0] * xyz[0]) + (xyz[1] * xyz[1]) + (xyz[2] * xyz[2]))); - float gd = sqrt(float((xyz[0] * xyz[0]) + (xyz[1] * xyz[1]))); - elevation = atan2f(float(xyz[2]), gd) * 180.f / float(M_PI); - azimuth = atan2f(float(xyz[0]), float(xyz[1])) * 180.f / float(M_PI); - } + void posCart2(Vec3d xyz); /// Get position as Cartesian coordinate (in audio space) - Vec3d vec() const { - // TODO doxygen style commenting on coordinates like ambisonics - double cosel = cos(toRad(double(elevation))); - double x = cos(toRad(double(azimuth))) * cosel * double(radius); - double y = sin(toRad(double(azimuth))) * cosel * double(radius); - double z = sin(toRad(double(elevation))) * double(radius); - // Ryan: the standard conversions assume +z is up, these are correct for - // allocore - - // double x = sin(toRad(azimuth)) * cosel * radius; - // double y = sin(toRad(elevation)) * radius; - // double z = -1*cos(toRad(azimuth)) * cosel * radius; - return Vec3d(x, y, z); - } + Vec3d vec() const; /// Get position as Cartesian coordinate (in graphics space) - Vec3d vecGraphics() const { - // TODO doxygen style commenting on coordinates like ambisonics - double cosel = cos(toRad(double(elevation))); - double x = cos(toRad(double(azimuth))) * cosel * double(radius); - double y = sin(toRad(double(azimuth))) * cosel * double(radius); - double z = sin(toRad(double(elevation))) * double(radius); - // Ryan: the standard conversions assume +z is up, these are correct for - // allocore - - // double x = sin(toRad(azimuth)) * cosel * radius; - // double y = sin(toRad(elevation)) * radius; - // double z = -1*cos(toRad(azimuth)) * cosel * radius; - return Vec3d(-y, z, -x); - } + Vec3d vecGraphics() const; static double toRad(double d) { return d * M_PI / 180.; } static float toRad(float d) { return d * float(M_PI) / 180.f; } @@ -139,95 +100,52 @@ class Speaker { /// A set of speakers typedef std::vector Speakers; -/// Base class for a configuration of multiple speakers -/// -/// @ingroup Sound -class SpeakerLayout { - public: - /// Get number of speakers - size_t numSpeakers() const { return speakers().size(); } - - /// Get speaker array - Speakers& speakers() { return mSpeakers; } - const Speakers& speakers() const { return mSpeakers; } - - /// Add speaker - SpeakerLayout& addSpeaker(const Speaker spkr) { - mSpeakers.push_back(spkr); - return *this; - } - - protected: - Speakers mSpeakers; -}; - /// Generic layout of N speakers spaced equidistantly in a ring /// /// @ingroup Sound +/// +/// @param[in] deviceChannelStart starting index of device channel +/// @param[in] phase starting phase of first speaker, +/// in degrees +/// @param[in] radius radius of all speakers +/// @param[in] gain gain of all speakers template -class SpeakerRingLayout : public SpeakerLayout { - public: - /// @param[in] deviceChannelStart starting index of device channel - /// @param[in] phase starting phase of first speaker, in - /// degrees - /// @param[in] radius radius of all speakers - /// @param[in] gain gain of all speakers - SpeakerRingLayout(unsigned int deviceChannelStart = 0, float phase = 0.f, - float radius = 1.f, float gain = 1.f) { - mSpeakers.reserve(N); - for (unsigned int i = 0; i < N; ++i) - addSpeaker(Speaker(i + deviceChannelStart, 360.f / N * i + phase, 0.f, 0, - radius, gain)); +Speakers SpeakerRingLayout(unsigned int deviceChannelStart = 0, + float phase = 0.f, float radius = 1.f, + float gain = 1.f) { + Speakers mSpeakers; + mSpeakers.reserve(N); + for (unsigned int i = 0; i < N; ++i) { + mSpeakers.emplace_back(Speaker( + i + deviceChannelStart, 360.f / N * i + phase, 0.f, 0, radius, gain)); } + return mSpeakers; }; /// Headset speaker layout /// /// @ingroup allocore -class HeadsetSpeakerLayout : public SpeakerRingLayout<2> { - public: - HeadsetSpeakerLayout(unsigned int deviceChannelStart = 0, float radius = 1.f, - float gain = 1.f) - : SpeakerRingLayout<2>(deviceChannelStart, 90, radius, gain) {} -}; +Speakers HeadsetSpeakerLayout(unsigned int deviceChannelStart = 0, + float radius = 1.f, float gain = 1.f); /// Stereo speaker layout /// /// @ingroup Sound -class StereoSpeakerLayout : public SpeakerLayout { - public: - StereoSpeakerLayout(unsigned int deviceChannelStart = 0, float angle = 30.f, - float distance = 1.f, float gain = 1.f) - : mLeft(deviceChannelStart, angle, 0, 0, distance, gain), - mRight(deviceChannelStart + 1, -angle, 0, 0, distance, gain) { - addSpeaker(mLeft); - addSpeaker(mRight); - } - - private: - Speaker mLeft; - Speaker mRight; -}; +Speakers StereoSpeakerLayout(unsigned int deviceChannelStart = 0, + float angle = 30.f, float distance = 1.f, + float gain = 1.f); /// Octophonic ring speaker layout /// /// @ingroup Sound -typedef SpeakerRingLayout<8> OctalSpeakerLayout; +Speakers OctalSpeakerLayout(unsigned int deviceChannelStart = 0, + float phase = 0.f, float radius = 1.f, + float gain = 1.f); /// Generic layout of 8 speakers arranged in a cube with listener in the middle /// /// @ingroup allocore -class CubeLayout : public SpeakerLayout { - public: - CubeLayout(unsigned int deviceChannelStart = 0) { - mSpeakers.reserve(8); - for (unsigned int i = 0; i < 4; ++i) { - addSpeaker(Speaker(i + deviceChannelStart, 45.f + (i * 90), 0)); - addSpeaker(Speaker(4 + i + deviceChannelStart, 45.f + (i * 90), 60, 0, - sqrt(5.f))); - } - } -}; +Speakers CubeLayout(unsigned int deviceChannelStart = 0); } // namespace al #endif diff --git a/include/al/sound/al_StereoPanner.hpp b/include/al/sound/al_StereoPanner.hpp index d65e28f6..dd64dd1e 100644 --- a/include/al/sound/al_StereoPanner.hpp +++ b/include/al/sound/al_StereoPanner.hpp @@ -19,13 +19,11 @@ namespace al { /// @ingroup Sound class StereoPanner : public Spatializer { public: - StereoPanner(SpeakerLayout& sl) : Spatializer(sl), numSpeakers(0) { + StereoPanner(Speakers& sl) : Spatializer(sl), numSpeakers(0) { numSpeakers = mSpeakers.size(); if (numSpeakers != 2) { - printf( - "Stereo Panner Requires exactly 2 speakers (%i used), no panning " - "will occur!\n", - numSpeakers); + std::cout << "Stereo Panner Requires exactly 2 speakers (" << numSpeakers + << " used). First two will be used!" << std::endl; } } @@ -40,7 +38,7 @@ class StereoPanner : public Spatializer { const unsigned int& numFrames) override; private: - int numSpeakers; + size_t numSpeakers; void equalPowerPan(const Vec3d& relPos, float& gainL, float& gainR); }; diff --git a/include/al/sound/al_Vbap.hpp b/include/al/sound/al_Vbap.hpp index 9d42a7c4..b10031cd 100644 --- a/include/al/sound/al_Vbap.hpp +++ b/include/al/sound/al_Vbap.hpp @@ -92,7 +92,7 @@ class Vbap : public Spatializer { } VbapOptions; /// @param[in] sl A speaker layout - Vbap(const SpeakerLayout& sl, bool is3D = false); + Vbap(const Speakers& sl, bool is3D = false); void setOptions(VbapOptions options) { mOptions = options; } diff --git a/include/al/sphere/al_AlloSphereSpeakerLayout.hpp b/include/al/sphere/al_AlloSphereSpeakerLayout.hpp index 501635a9..379bf233 100644 --- a/include/al/sphere/al_AlloSphereSpeakerLayout.hpp +++ b/include/al/sphere/al_AlloSphereSpeakerLayout.hpp @@ -3,75 +3,10 @@ #include "al/sound/al_Speaker.hpp" -#define NUM_SPEAKERS 54 - namespace al { /// Current arrangement of speakers in AlloSphere -struct AlloSphereSpeakerLayout : public SpeakerLayout { - AlloSphereSpeakerLayout() { - const int numSpeakers = 54; - Speaker alloSpeakers[numSpeakers] = { - Speaker(1 - 1, -77.660913, 41.000000, 1, 4.992118), - Speaker(2 - 1, -45.088015, 41.000000, 1, 5.571107), - Speaker(3 - 1, -14.797289, 41.000000, 1, 5.900603), - Speaker(4 - 1, 14.797289, 41.000000, 1, 5.900603), - Speaker(5 - 1, 45.088015, 41.000000, 1, 5.571107), - Speaker(6 - 1, 77.660913, 41.000000, 1, 4.992118), - Speaker(7 - 1, 102.339087, 41.000000, 1, 4.992118), - Speaker(8 - 1, 134.911985, 41.000000, 1, 5.571107), - Speaker(9 - 1, 165.202711, 41.000000, 1, 5.900603), - Speaker(10 - 1, -165.202711, 41.000000, 1, 5.900603), - Speaker(11 - 1, -134.911985, 41.000000, 1, 5.571107), - Speaker(12 - 1, -102.339087, 41.000000, 1, 4.992118), - Speaker(17 - 1, -77.660913, 0.000000, 0, 4.992118), - Speaker(18 - 1, -65.647587, 0.000000, 0, 5.218870), - Speaker(19 - 1, -54.081600, 0.000000, 0, 5.425483), - Speaker(20 - 1, -42.869831, 0.000000, 0, 5.604350), - Speaker(21 - 1, -31.928167, 0.000000, 0, 5.749461), - Speaker(22 - 1, -21.181024, 0.000000, 0, 5.856274), - Speaker(23 - 1, -10.559657, 0.000000, 0, 5.921613), - Speaker(24 - 1, 0.000000, 0.000000, 0, 5.943600), - Speaker(25 - 1, 10.559657, 0.000000, 0, 5.921613), - Speaker(26 - 1, 21.181024, 0.000000, 0, 5.856274), - Speaker(27 - 1, 31.928167, 0.000000, 0, 5.749461), - Speaker(28 - 1, 42.869831, 0.000000, 0, 5.604350), - Speaker(29 - 1, 54.081600, 0.000000, 0, 5.425483), - Speaker(30 - 1, 65.647587, 0.000000, 0, 5.218870), - Speaker(31 - 1, 77.660913, 0.000000, 0, 4.992118), - Speaker(32 - 1, 102.339087, 0.000000, 0, 4.992118), - Speaker(33 - 1, 114.352413, 0.000000, 0, 5.218870), - Speaker(34 - 1, 125.918400, 0.000000, 0, 5.425483), - Speaker(35 - 1, 137.130169, 0.000000, 0, 5.604350), - Speaker(36 - 1, 148.071833, 0.000000, 0, 5.749461), - Speaker(37 - 1, 158.818976, 0.000000, 0, 5.856274), - Speaker(38 - 1, 169.440343, 0.000000, 0, 5.921613), - Speaker(39 - 1, 180.000000, 0.000000, 0, 5.943600), - Speaker(40 - 1, -169.440343, 0.000000, 0, 5.921613), - Speaker(41 - 1, -158.818976, 0.000000, 0, 5.856274), - Speaker(42 - 1, -148.071833, 0.000000, 0, 5.749461), - Speaker(43 - 1, -137.130169, 0.000000, 0, 5.604350), - Speaker(44 - 1, -125.918400, 0.000000, 0, 5.425483), - Speaker(45 - 1, -114.352413, 0.000000, 0, 5.218870), - Speaker(46 - 1, -102.339087, 0.000000, 0, 4.992118), - Speaker(49 - 1, -77.660913, -32.500000, 2, 4.992118), - Speaker(50 - 1, -45.088015, -32.500000, 2, 5.571107), - Speaker(51 - 1, -14.797289, -32.500000, 2, 5.900603), - Speaker(52 - 1, 14.797289, -32.500000, 2, 5.900603), - Speaker(53 - 1, 45.088015, -32.500000, 2, 5.571107), - Speaker(54 - 1, 77.660913, -32.500000, 2, 4.992118), - Speaker(55 - 1, 102.339087, -32.500000, 2, 4.992118), - Speaker(56 - 1, 134.911985, -32.500000, 2, 5.571107), - Speaker(57 - 1, 165.202711, -32.500000, 2, 5.900603), - Speaker(58 - 1, -165.202711, -32.500000, 2, 5.900603), - Speaker(59 - 1, -134.911985, -32.500000, 2, 5.571107), - Speaker(60 - 1, -102.339087, -32.500000, 2, 4.992118), - }; - - mSpeakers.reserve(NUM_SPEAKERS); - for (int i = 0; i < NUM_SPEAKERS; ++i) addSpeaker(alloSpeakers[i]); - } -}; +Speakers AlloSphereSpeakerLayout(); } // namespace al #endif diff --git a/include/al/sphere/al_OmniRenderer.hpp b/include/al/sphere/al_OmniRenderer.hpp index 23ac04f2..b5368cba 100644 --- a/include/al/sphere/al_OmniRenderer.hpp +++ b/include/al/sphere/al_OmniRenderer.hpp @@ -3,10 +3,9 @@ #include -#include "al/sphere/al_Perprojection.hpp" -#include "al/sphere/al_SphereUtils.hpp" - #include "al/app/al_WindowApp.hpp" +#include "al/sphere/al_PerProjection.hpp" +#include "al/sphere/al_SphereUtils.hpp" //#include "al/graphics/al_GLFW.hpp" #include "al/graphics/al_Graphics.hpp" diff --git a/include/al/sphere/al_Perprojection.hpp b/include/al/sphere/al_PerProjection.hpp similarity index 100% rename from include/al/sphere/al_Perprojection.hpp rename to include/al/sphere/al_PerProjection.hpp diff --git a/include/al/sphere/al_SphereUtils.hpp b/include/al/sphere/al_SphereUtils.hpp index 52788398..45b3b1f7 100644 --- a/include/al/sphere/al_SphereUtils.hpp +++ b/include/al/sphere/al_SphereUtils.hpp @@ -3,17 +3,11 @@ #include #include +#include #include #include -// gethostname -#ifdef AL_WINDOWS -#include -#else -#include -#endif - -//#include "al/graphics/al_GLFW.hpp" +#include "al/app/al_NodeConfiguration.hpp" #include "al/math/al_Constants.hpp" std::string al_get_hostname(); @@ -22,29 +16,31 @@ namespace al { namespace sphere { -bool is_simulator(std::string const& host); +bool isSimulatorMachine(std::string const& host); -bool is_simulator(); +bool isSimulatorMachine(); -bool is_renderer(std::string const& host); +bool isRendererMachine(std::string const& host); -bool is_renderer(); +bool isRendererMachine(); std::string renderer_hostname(std::string const& fallback); -bool is_in_sphere(); +bool isSphereMachine(); + +void getFullscreenDimension(int* width, int* height); -void get_fullscreen_dimension(int* width, int* height); +std::string getCalibrationDirectory(std::string const& dir_if_not_renderer); -std::string config_directory(std::string const& dir_if_not_renderer); +std::string getRendererCalibrationFilepath(std::string const& host); -std::string renderer_config_file_path(std::string const& host); +std::string getRendererCalibrationFilepath(); -std::string renderer_config_file_path(); +std::string getCalibrationFilepath(std::string const& path_if_not_renderer); -std::string config_file_path(std::string const& path_if_not_renderer); +std::vector generateEquirectSampletex(int width, int height); -std::vector generate_equirect_sampletex(int width, int height); +std::map getSphereNodes(); } // namespace sphere diff --git a/include/al/types/al_Color.hpp b/include/al/types/al_Color.hpp index c0ceca04..b1615a08 100644 --- a/include/al/types/al_Color.hpp +++ b/include/al/types/al_Color.hpp @@ -36,9 +36,9 @@ File description: - RGBA, HSV, CIEXYZ, Lab, HCLab, Luv, and HCLuv color classes + RGBA, HSV, CIE_XYZ, Lab, HCLab, Luv, and HCLuv color classes - Color conversion code for CIEXYZ, Lab, HCLab, Luv, HCLuv was adapted from + Color conversion code for CIE_XYZ, Lab, HCLab, Luv, HCLuv was adapted from psuedo-code and formulas found at http://www.easyrgb.com/ and http://brucelindbloom.com/ @@ -56,7 +56,7 @@ struct HSV; struct Color; struct Colori; -struct CIEXYZ; +struct CIE_XYZ; struct Lab; struct HCLab; struct Luv; @@ -84,66 +84,66 @@ struct Color { /// @param[in] rgba 4-vector of RGBA components template - Color(const T* rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) {} + Color(const T *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) {} /// @param[in] gray red/green/blue components /// @param[in] a alpha component Color(float gray = 1.f, float a = 1.f) : r(gray), g(gray), b(gray), a(a) {} /// @param[in] c RGBA color to convert from - Color(const Colori& c) { *this = c; } + Color(const Colori &c) { *this = c; } /// @param[in] hsv HSV value /// @param[in] a alpha component - Color(const HSV& hsv, float a = 1.f) : a(a) { *this = hsv; } + Color(const HSV &hsv, float a = 1.f) : a(a) { *this = hsv; } /// @param[in] rgb RGB value /// @param[in] a alpha component - Color(const RGB& rgb, float a = 1.f) : a(a) { *this = rgb; } + Color(const RGB &rgb, float a = 1.f) : a(a) { *this = rgb; } - /// @param[in] xyz CIEXYZ value + /// @param[in] xyz CIE_XYZ value /// @param[in] a alpha component - Color(const CIEXYZ& xyz, float a = 1.f) : a(a) { *this = xyz; } + Color(const CIE_XYZ &xyz, float a = 1.f) : a(a) { *this = xyz; } /// @param[in] lab Lab value /// @param[in] a alpha component - Color(const Lab& lab, float a = 1.f) : a(a) { *this = lab; } + Color(const Lab &lab, float a = 1.f) : a(a) { *this = lab; } /// @param[in] hclab HCLab value /// @param[in] a alpha component - Color(const HCLab& hclab, float a = 1.f) : a(a) { *this = hclab; } + Color(const HCLab &hclab, float a = 1.f) : a(a) { *this = hclab; } /// @param[in] luv Luv value /// @param[in] a alpha component - Color(const Luv& luv, float a = 1.f) : a(a) { *this = luv; } + Color(const Luv &luv, float a = 1.f) : a(a) { *this = luv; } /// @param[in] hcluv HCLuv value /// @param[in] a alpha component - Color(const HCLuv& hcluv, float a = 1.f) : a(a) { *this = hcluv; } + Color(const HCLuv &hcluv, float a = 1.f) : a(a) { *this = hcluv; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } - RGB& rgb() { return *(RGB*)(components); } - const RGB& rgb() const { return *(const RGB*)(components); } + RGB &rgb() { return *(RGB *)(components); } + const RGB &rgb() const { return *(const RGB *)(components); } /// Set RGB from another color and alpha from argument - Color& set(const Color& c, float al) { + Color &set(const Color &c, float al) { a = al; return set(c.r, c.g, c.b); } /// Set RGBA components - Color& set(float re, float gr, float bl, float al) { + Color &set(float re, float gr, float bl, float al) { a = al; return set(re, gr, bl); } /// Set RGB components - Color& set(float re, float gr, float bl) { + Color &set(float re, float gr, float bl) { r = re; g = gr; b = bl; @@ -151,84 +151,84 @@ struct Color { } /// Set from gray value - Color& set(float v) { return set(v, v, v); } + Color &set(float v) { return set(v, v, v); } /// Set from gray value and alpha - Color& set(float v, float al) { return set(v, v, v, al); } + Color &set(float v, float al) { return set(v, v, v, al); } /// Set from an array of RGBA components template - Color& set(const T* rgba) { + Color &set(const T *rgba) { return set(rgba[0], rgba[1], rgba[2], rgba[3]); } /// Set from gray value - Color& operator=(float v) { return set(v); } - Color& operator=(double v) { return set(static_cast(v)); } + Color &operator=(float v) { return set(v); } + Color &operator=(double v) { return set(static_cast(v)); } /// Set components from integer color - Color& operator=(const Colori& v); + Color &operator=(const Colori &v); /// Set RGB components from HSV - Color& operator=(const HSV& v); + Color &operator=(const HSV &v); /// Set RGB components from RGB - Color& operator=(const RGB& v); + Color &operator=(const RGB &v); - /// Set RGB components from CIEXYZ - Color& operator=(const CIEXYZ& v); + /// Set RGB components from CIE_XYZ + Color &operator=(const CIE_XYZ &v); /// Set RGB components from Lab - Color& operator=(const Lab& v); + Color &operator=(const Lab &v); /// Set RGB components from HCLab - Color& operator=(const HCLab& v); + Color &operator=(const HCLab &v); /// Set RGB components from Luv - Color& operator=(const Luv& v); + Color &operator=(const Luv &v); /// Set RGB components from HCLuv - Color& operator=(const HCLuv& v); + Color &operator=(const HCLuv &v); /// Return true if all components are equal, false otherwise - bool operator==(const Color& v) const { + bool operator==(const Color &v) const { return v.r == r && v.g == g && v.b == b && v.a == a; } /// Return true if components are not equal, false otherwise - bool operator!=(const Color& v) const { return !(*this == v); } + bool operator!=(const Color &v) const { return !(*this == v); } - Color& operator+=(const Color& v) { + Color &operator+=(const Color &v) { return set(r + v.r, g + v.g, b + v.b, a + v.a); } - Color& operator-=(const Color& v) { + Color &operator-=(const Color &v) { return set(r - v.r, g - v.g, b - v.b, a - v.a); } - Color& operator*=(const Color& v) { + Color &operator*=(const Color &v) { return set(r * v.r, g * v.g, b * v.b, a * v.a); } - Color& operator/=(const Color& v) { + Color &operator/=(const Color &v) { return set(r / v.r, g / v.g, b / v.b, a / v.a); } - Color& operator+=(float v) { return set(r + v, g + v, b + v, a + v); } - Color& operator-=(float v) { return set(r - v, g - v, b - v, a - v); } - Color& operator*=(float v) { return set(r * v, g * v, b * v, a * v); } - Color& operator/=(float v) { return set(r / v, g / v, b / v, a / v); } + Color &operator+=(float v) { return set(r + v, g + v, b + v, a + v); } + Color &operator-=(float v) { return set(r - v, g - v, b - v, a - v); } + Color &operator*=(float v) { return set(r * v, g * v, b * v, a * v); } + Color &operator/=(float v) { return set(r / v, g / v, b / v, a / v); } Color operator-() const { return Color(-r, -g, -b, -a); } - Color operator+(const Color& v) const { return Color(*this) += v; } - Color operator-(const Color& v) const { return Color(*this) -= v; } - Color operator*(const Color& v) const { return Color(*this) *= v; } - Color operator/(const Color& v) const { return Color(*this) /= v; } + Color operator+(const Color &v) const { return Color(*this) += v; } + Color operator-(const Color &v) const { return Color(*this) -= v; } + Color operator*(const Color &v) const { return Color(*this) *= v; } + Color operator/(const Color &v) const { return Color(*this) /= v; } Color operator+(float v) const { return Color(*this) += v; } Color operator-(float v) const { return Color(*this) -= v; } Color operator*(float v) const { return Color(*this) *= v; } Color operator/(float v) const { return Color(*this) /= v; } /// Clamp all components into [0,max] range - Color& clamp(float max = 1.f) { + Color &clamp(float max = 1.f) { for (int i = 0; i < 4; ++i) { - float& v = components[i]; + float &v = components[i]; v < 0.f ? v = 0.f : (v > max ? v = max : 0); } return *this; @@ -238,7 +238,7 @@ struct Color { Color inverse() const { return Color(*this).invert(); } /// Invert RGB components - Color& invert(); + Color &invert(); /// Returns luminance value float luminance() const; @@ -246,7 +246,7 @@ struct Color { Color blackAndWhite() const; ///< Returns nearest black or white color /// Returns self linearly mixed with another color (0 = none) - Color mix(const Color& c, float amt = 0.5f) const { + Color mix(const Color &c, float amt = 0.5f) const { return (c - *this) * amt + *this; } @@ -283,7 +283,7 @@ struct Colori { /// @param[in] rgba 4-vector of RGBA components template - Colori(const T* rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) {} + Colori(const T *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) {} /// @param[in] gray red/green/blue components /// @param[in] a alpha component @@ -291,80 +291,80 @@ struct Colori { : r(gray), g(gray), b(gray), a(a) {} /// @param[in] c RGBA color to convert from - Colori(const Color& c) { *this = c; } + Colori(const Color &c) { *this = c; } /// @param[in] hsv HSV color /// @param[in] a alpha component - Colori(const HSV& hsv, uint8_t a = 255) : a(a) { *this = hsv; } + Colori(const HSV &hsv, uint8_t a = 255) : a(a) { *this = hsv; } /// @param[in] rgb RGB color /// @param[in] a alpha component - Colori(const RGB& rgb, uint8_t a = 255) : a(a) { *this = rgb; } + Colori(const RGB &rgb, uint8_t a = 255) : a(a) { *this = rgb; } - /// @param[in] xyz CIEXYZ color + /// @param[in] xyz CIE_XYZ color /// @param[in] a alpha component - Colori(const CIEXYZ& xyz, float a = 1.f) : a(static_cast(a * 255)) { + Colori(const CIE_XYZ &xyz, float a = 1.f) : a(static_cast(a * 255)) { *this = xyz; } /// @param[in] lab Lab color /// @param[in] a alpha component - Colori(const Lab& lab, float a = 1.f) : a(static_cast(a * 255)) { + Colori(const Lab &lab, float a = 1.f) : a(static_cast(a * 255)) { *this = lab; } /// @param[in] hclab HCLab value /// @param[in] a alpha component - Colori(const HCLab& hclab, float a = 1.f) : a(static_cast(a * 255)) { + Colori(const HCLab &hclab, float a = 1.f) : a(static_cast(a * 255)) { *this = hclab; } /// @param[in] luv Luv value /// @param[in] a alpha component - Colori(const Luv& luv, float a = 1.f) : a(static_cast(a * 255)) { + Colori(const Luv &luv, float a = 1.f) : a(static_cast(a * 255)) { *this = luv; } /// @param[in] hcluv HCLuv value /// @param[in] a alpha component - Colori(const HCLuv& hcluv, float a = 1.f) : a(static_cast(a * 255)) { + Colori(const HCLuv &hcluv, float a = 1.f) : a(static_cast(a * 255)) { *this = hcluv; } /// Set color component at index with no bounds checking - uint8_t& operator[](int i) { return components[i]; } + uint8_t &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const uint8_t& operator[](int i) const { return components[i]; } + const uint8_t &operator[](int i) const { return components[i]; } /// Set from floating-point color - Colori& operator=(const Color& v) { + Colori &operator=(const Color &v) { return set(toi(v.r), toi(v.g), toi(v.b), toi(v.a)); } /// Set RGB components from HSV - Colori& operator=(const HSV& v); + Colori &operator=(const HSV &v); /// Set RGB components from RGB - Colori& operator=(const RGB& v); + Colori &operator=(const RGB &v); - /// Set RGB components from CIEXYZ - Colori& operator=(const CIEXYZ& v); + /// Set RGB components from CIE_XYZ + Colori &operator=(const CIE_XYZ &v); /// Set RGB components from Lab - Colori& operator=(const Lab& v); + Colori &operator=(const Lab &v); /// Set RGB components from HCLab - Colori& operator=(const HCLab& v); + Colori &operator=(const HCLab &v); /// Set RGB components from Luv - Colori& operator=(const Luv& v); + Colori &operator=(const Luv &v); /// Set RGB components from HCLuv - Colori& operator=(const HCLuv& v); + Colori &operator=(const HCLuv &v); /// Set RGB components - Colori& set(uint8_t re, uint8_t gr, uint8_t bl) { + Colori &set(uint8_t re, uint8_t gr, uint8_t bl) { r = re; g = gr; b = bl; @@ -372,22 +372,22 @@ struct Colori { } /// Set RGBA components - Colori& set(uint8_t re, uint8_t gr, uint8_t bl, uint8_t al) { + Colori &set(uint8_t re, uint8_t gr, uint8_t bl, uint8_t al) { a = al; return set(re, gr, bl); } /// Set from gray value - Colori& set(uint8_t v) { return set(v, v, v); } + Colori &set(uint8_t v) { return set(v, v, v); } /// Set from gray value and alpha - Colori& set(uint8_t v, uint8_t al) { return set(v, v, v, al); } + Colori &set(uint8_t v, uint8_t al) { return set(v, v, v, al); } /// Returns inverted color Colori inverse() const { return Colori(*this).invert(); } /// Invert RGB components - Colori& invert() { return set(255 - r, 255 - g, 255 - b); } + Colori &invert() { return set(255 - r, 255 - g, 255 - b); } private: uint8_t toi(float v) { return uint8_t(v * 255.f); } @@ -411,79 +411,79 @@ struct HSV { /// @param[in] hsv 3-vector of HSV components template - HSV(const T* hsv) : h(hsv[0]), s(hsv[1]), v(hsv[2]) {} + HSV(const T *hsv) : h(hsv[0]), s(hsv[1]), v(hsv[2]) {} /// @param[in] v RGB color to convert from - HSV(const Color& v) { *this = v; } + HSV(const Color &v) { *this = v; } /// @param[in] v RGB color to convert from - HSV(const Colori& v) { *this = v; } + HSV(const Colori &v) { *this = v; } /// @param[in] v RGB color to convert from - HSV(const RGB& v) { *this = v; } + HSV(const RGB &v) { *this = v; } - /// @param[in] xyz CIEXYZ color to convert from - HSV(const CIEXYZ& xyz) { *this = xyz; } + /// @param[in] xyz CIE_XYZ color to convert from + HSV(const CIE_XYZ &xyz) { *this = xyz; } /// @param[in] lab Lab color to convert from - HSV(const Lab& lab) { *this = lab; } + HSV(const Lab &lab) { *this = lab; } /// @param[in] hclab HCLab color to convert from - HSV(const HCLab& hclab) { *this = hclab; } + HSV(const HCLab &hclab) { *this = hclab; } /// @param[in] luv Luv color to convert from - HSV(const Luv& luv) { *this = luv; } + HSV(const Luv &luv) { *this = luv; } /// @param[in] hcluv HCLuv color to convert from - HSV(const HCLuv& hcluv) { *this = hcluv; } + HSV(const HCLuv &hcluv) { *this = hcluv; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } /// Set from RGBA color - HSV& operator=(const Color& v) { return *this = v.rgb(); } + HSV &operator=(const Color &v) { return *this = v.rgb(); } /// Set from RGBA color - HSV& operator=(const Colori& v) { return *this = Color(v); } + HSV &operator=(const Colori &v) { return *this = Color(v); } /// Set from RGB color - HSV& operator=(const RGB& v); + HSV &operator=(const RGB &v); - /// Set from CIEXYZ color - HSV& operator=(const CIEXYZ& v); + /// Set from CIE_XYZ color + HSV &operator=(const CIE_XYZ &v); /// Set from Lab color - HSV& operator=(const Lab& v); + HSV &operator=(const Lab &v); /// Set from HCLab color - HSV& operator=(const HCLab& v); + HSV &operator=(const HCLab &v); /// Set from Luv color - HSV& operator=(const Luv& v); + HSV &operator=(const Luv &v); /// Set from HCLuv color - HSV& operator=(const HCLuv& v); + HSV &operator=(const HCLuv &v); /// Get new HSV with value component multiplied by a scalar HSV operator*(float a) const { return HSV(*this) *= a; } /// Multiply value component by a scalar - HSV& operator*=(float a) { + HSV &operator*=(float a) { v *= a; return *this; } /// Rotate hue in interval [0, 1) - HSV& rotateHue(float dh) { + HSV &rotateHue(float dh) { h += dh; return wrapHue(); } /// Wrap hue value into valid interval [0, 1) - HSV& wrapHue() { + HSV &wrapHue() { if (h > 1) { h -= int(h); } else if (h < 0) { @@ -517,46 +517,46 @@ struct RGB { /// @param[in] rgb 3-vector of RGB components template - RGB(const T* rgb) : r(rgb[0]), g(rgb[1]), b(rgb[2]) {} + RGB(const T *rgb) : r(rgb[0]), g(rgb[1]), b(rgb[2]) {} /// @param[in] gray red/green/blue components RGB(float gray = 1.f) : r(gray), g(gray), b(gray) {} /// @param[in] v RGB color to convert from - RGB(const Color& v) { *this = v; } + RGB(const Color &v) { *this = v; } /// @param[in] v Colori to convert from - RGB(const Colori& v) { *this = v; } + RGB(const Colori &v) { *this = v; } /// @param[in] hsv HSV value - RGB(const HSV& hsv) { *this = hsv; } + RGB(const HSV &hsv) { *this = hsv; } - /// @param[in] xyz CIEXYZ value - RGB(const CIEXYZ& xyz) { *this = xyz; } + /// @param[in] xyz CIE_XYZ value + RGB(const CIE_XYZ &xyz) { *this = xyz; } /// @param[in] lab Lab value - RGB(const Lab& lab) { *this = lab; } + RGB(const Lab &lab) { *this = lab; } /// @param[in] hclab HCLab value - RGB(const HCLab& hclab) { *this = hclab; } + RGB(const HCLab &hclab) { *this = hclab; } /// @param[in] luv Luv value - RGB(const Luv& luv) { *this = luv; } + RGB(const Luv &luv) { *this = luv; } /// @param[in] hcluv HCLuv value - RGB(const HCLuv& hcluv) { *this = hcluv; } + RGB(const HCLuv &hcluv) { *this = hcluv; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } /// Set from another RGB - RGB& set(const RGB& v) { return set(v.r, v.g, v.b); } + RGB &set(const RGB &v) { return set(v.r, v.g, v.b); } /// Set from RGB components - RGB& set(float re, float gr, float bl) { + RGB &set(float re, float gr, float bl) { r = re; g = gr; b = bl; @@ -564,73 +564,73 @@ struct RGB { } /// Set from gray value - RGB& set(float v) { return set(v, v, v); } + RGB &set(float v) { return set(v, v, v); } /// Set from an array of RGB components template - RGB& set(const T* rgb) { + RGB &set(const T *rgb) { return set(rgb[0], rgb[1], rgb[2]); } /// Set from gray value - RGB& operator=(float v) { return set(v); } - RGB& operator=(double v) { return set(static_cast(v)); } + RGB &operator=(float v) { return set(v); } + RGB &operator=(double v) { return set(static_cast(v)); } /// Set RGB components from HSV - RGB& operator=(const HSV& v); + RGB &operator=(const HSV &v); /// Set RGB components from Color - RGB& operator=(const Color& v) { return set(v.rgb()); } + RGB &operator=(const Color &v) { return set(v.rgb()); } /// Set RGB components from Colori - RGB& operator=(const Colori& v); + RGB &operator=(const Colori &v); - /// Set RGB components from CIEXYZ - RGB& operator=(const CIEXYZ& v); + /// Set RGB components from CIE_XYZ + RGB &operator=(const CIE_XYZ &v); /// Set RGB components from Lab - RGB& operator=(const Lab& v); + RGB &operator=(const Lab &v); /// Set RGB components from HCLab - RGB& operator=(const HCLab& v); + RGB &operator=(const HCLab &v); /// Set RGB components from Luv - RGB& operator=(const Luv& v); + RGB &operator=(const Luv &v); /// Set RGB components from HCLuv - RGB& operator=(const HCLuv& v); + RGB &operator=(const HCLuv &v); /// Return true if all components are equal, false otherwise - bool operator==(const RGB& v) const { + bool operator==(const RGB &v) const { return v.r == r && v.g == g && v.b == b; } /// Return true if components are not equal, false otherwise - bool operator!=(const RGB& v) const { return !(*this == v); } + bool operator!=(const RGB &v) const { return !(*this == v); } - RGB& operator+=(const RGB& v) { return set(r + v.r, g + v.g, b + v.b); } - RGB& operator-=(const RGB& v) { return set(r - v.r, g - v.g, b - v.b); } - RGB& operator*=(const RGB& v) { return set(r * v.r, g * v.g, b * v.b); } - RGB& operator/=(const RGB& v) { return set(r / v.r, g / v.g, b / v.b); } - RGB& operator+=(float v) { return set(r + v, g + v, b + v); } - RGB& operator-=(float v) { return set(r - v, g - v, b - v); } - RGB& operator*=(float v) { return set(r * v, g * v, b * v); } - RGB& operator/=(float v) { return set(r / v, g / v, b / v); } + RGB &operator+=(const RGB &v) { return set(r + v.r, g + v.g, b + v.b); } + RGB &operator-=(const RGB &v) { return set(r - v.r, g - v.g, b - v.b); } + RGB &operator*=(const RGB &v) { return set(r * v.r, g * v.g, b * v.b); } + RGB &operator/=(const RGB &v) { return set(r / v.r, g / v.g, b / v.b); } + RGB &operator+=(float v) { return set(r + v, g + v, b + v); } + RGB &operator-=(float v) { return set(r - v, g - v, b - v); } + RGB &operator*=(float v) { return set(r * v, g * v, b * v); } + RGB &operator/=(float v) { return set(r / v, g / v, b / v); } RGB operator-() const { return RGB(-r, -g, -b); } - RGB operator+(const RGB& v) const { return RGB(*this) += v; } - RGB operator-(const RGB& v) const { return RGB(*this) -= v; } - RGB operator*(const RGB& v) const { return RGB(*this) *= v; } - RGB operator/(const RGB& v) const { return RGB(*this) /= v; } + RGB operator+(const RGB &v) const { return RGB(*this) += v; } + RGB operator-(const RGB &v) const { return RGB(*this) -= v; } + RGB operator*(const RGB &v) const { return RGB(*this) *= v; } + RGB operator/(const RGB &v) const { return RGB(*this) /= v; } RGB operator+(float v) const { return RGB(*this) += v; } RGB operator-(float v) const { return RGB(*this) -= v; } RGB operator*(float v) const { return RGB(*this) *= v; } RGB operator/(float v) const { return RGB(*this) /= v; } /// Clamp all components into [0,max] range - RGB& clamp(float max = 1.f) { + RGB &clamp(float max = 1.f) { for (int i = 0; i < 3; ++i) { - float& v = components[i]; + float &v = components[i]; v < 0.f ? v = 0.f : (v > max ? v = max : 0); } return *this; @@ -640,77 +640,77 @@ struct RGB { RGB inverse() const { return RGB(*this).invert(); } /// Invert RGB components - RGB& invert() { return set(1.f - r, 1.f - g, 1.f - b); } + RGB &invert() { return set(1.f - r, 1.f - g, 1.f - b); } /// Returns luminance value (following ITU-R BT.601) float luminance() const { return r * 0.299f + g * 0.587f + b * 0.114f; } /// Returns self linearly mixed with another color (0 = none) - RGB mix(const RGB& v, float amt = 0.5f) const { + RGB mix(const RGB &v, float amt = 0.5f) const { return (v - *this) * amt + *this; } }; -struct CIEXYZ { +struct CIE_XYZ { union { struct { float x; /// < red component in [0, 1] float y; /// < green component in [0, 1] float z; /// < blue component in [0, 1] }; - float components[3]; ///< CIEXYZ component vector + float components[3]; ///< CIE_XYZ component vector }; /// @param[in] x CIE X /// @param[in] y CIE Y /// @param[in] z CIE Z - CIEXYZ(float x = 0, float y = 1, float z = 1) : x(x), y(y), z(z) {} + CIE_XYZ(float x = 0, float y = 1, float z = 1) : x(x), y(y), z(z) {} - /// @param[in] xyz 3-vector of CIEXYZ components + /// @param[in] xyz 3-vector of CIE_XYZ components template - CIEXYZ(const T* xyz) : x(xyz[0]), y(xyz[1]), z(xyz[2]) {} + CIE_XYZ(const T *xyz) : x(xyz[0]), y(xyz[1]), z(xyz[2]) {} /// @param[in] v RGB color to convert from - CIEXYZ(const Color& v) { *this = v; } + CIE_XYZ(const Color &v) { *this = v; } /// @param[in] v RGB color to convert from - CIEXYZ(const Colori& v) { *this = v; } + CIE_XYZ(const Colori &v) { *this = v; } /// @param[in] v RGB color to convert from - CIEXYZ(const RGB& v) { *this = v; } + CIE_XYZ(const RGB &v) { *this = v; } /// @param[in] v HSV color to convert from - CIEXYZ(const HSV& v) { *this = v; } + CIE_XYZ(const HSV &v) { *this = v; } /// @param[in] v Lab color to convert from - CIEXYZ(const Lab& v) { *this = v; } + CIE_XYZ(const Lab &v) { *this = v; } /// @param[in] v Luv color to convert from - CIEXYZ(const Luv& v) { *this = v; } + CIE_XYZ(const Luv &v) { *this = v; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } /// Set from RGBA color - CIEXYZ& operator=(const Color& v) { return *this = v.rgb(); } + CIE_XYZ &operator=(const Color &v) { return *this = v.rgb(); } /// Set from RGBA color - CIEXYZ& operator=(const Colori& v) { return *this = Color(v); } + CIE_XYZ &operator=(const Colori &v) { return *this = Color(v); } /// Set from RGB color - CIEXYZ& operator=(const RGB& v); + CIE_XYZ &operator=(const RGB &v); /// Set from HSV color - CIEXYZ& operator=(const HSV& v) { return *this = RGB(v); } + CIE_XYZ &operator=(const HSV &v) { return *this = RGB(v); } /// Set from Lab color - CIEXYZ& operator=(const Lab& v); + CIE_XYZ &operator=(const Lab &v); /// Set from Luv color - CIEXYZ& operator=(const Luv& v); + CIE_XYZ &operator=(const Luv &v); }; /// Color represented by L* (lightness), a*, b* @@ -738,54 +738,54 @@ struct Lab { /// @param[in] hsv 3-vector of Lab components template - Lab(const T* Lab) : l(Lab[0]), a(Lab[1]), b(Lab[2]) {} + Lab(const T *Lab) : l(Lab[0]), a(Lab[1]), b(Lab[2]) {} /// @param[in] v RGB color to convert from - Lab(const Color& v) { *this = v; } + Lab(const Color &v) { *this = v; } /// @param[in] v RGB color to convert from - Lab(const Colori& v) { *this = v; } + Lab(const Colori &v) { *this = v; } /// @param[in] v RGB color to convert from - Lab(const RGB& v) { *this = v; } + Lab(const RGB &v) { *this = v; } /// @param[in] v HSV color to convert from - Lab(const HSV& v) { *this = v; } + Lab(const HSV &v) { *this = v; } - /// @param[in] v CIEXYZ color to convert from - Lab(const CIEXYZ& v) { *this = v; } + /// @param[in] v CIE_XYZ color to convert from + Lab(const CIE_XYZ &v) { *this = v; } /// @param[in] v HCLab color to convert from - Lab(const HCLab& v) { *this = v; } + Lab(const HCLab &v) { *this = v; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } /// Set from RGBA color - Lab& operator=(const Color& v) { return *this = v.rgb(); } + Lab &operator=(const Color &v) { return *this = v.rgb(); } /// Set from RGBA color - Lab& operator=(const Colori& v) { return *this = Color(v); } + Lab &operator=(const Colori &v) { return *this = Color(v); } - /// Set from CIEXYZ color - Lab& operator=(const CIEXYZ& v); + /// Set from CIE_XYZ color + Lab &operator=(const CIE_XYZ &v); /// Set from RGB color - Lab& operator=(const RGB& v) { return *this = CIEXYZ(v); } + Lab &operator=(const RGB &v) { return *this = CIE_XYZ(v); } /// Set from HSV color - Lab& operator=(const HSV& v) { return *this = CIEXYZ(v); } + Lab &operator=(const HSV &v) { return *this = CIE_XYZ(v); } - Lab& operator=(const HCLab& v); + Lab &operator=(const HCLab &v); /// Get new Lab with value component multiplied by a scalar Lab operator*(float c) const { return Lab(*this) *= c; } /// Multiply lightness component by a scalar - Lab& operator*=(float c) { + Lab &operator*=(float c) { l *= c; return *this; } @@ -815,67 +815,67 @@ struct HCLab { /// @param[in] hcl 3-vector of HCLab components template - HCLab(const T* HCLab) : h(HCLab[0]), c(HCLab[1]), l(HCLab[2]) {} + HCLab(const T *HCLab) : h(HCLab[0]), c(HCLab[1]), l(HCLab[2]) {} /// @param[in] v RGB color to convert from - HCLab(const Color& v) { *this = v; } + HCLab(const Color &v) { *this = v; } /// @param[in] v RGB color to convert from - HCLab(const Colori& v) { *this = v; } + HCLab(const Colori &v) { *this = v; } /// @param[in] v RGB color to convert from - HCLab(const RGB& v) { *this = v; } + HCLab(const RGB &v) { *this = v; } /// @param[in] v HSV color to convert from - HCLab(const HSV& v) { *this = v; } + HCLab(const HSV &v) { *this = v; } - /// @param[in] v CIEXYZ color to convert from - HCLab(const CIEXYZ& v) { *this = v; } + /// @param[in] v CIE_XYZ color to convert from + HCLab(const CIE_XYZ &v) { *this = v; } /// @param[in] v Lab color to convert from - HCLab(const Lab& v) { *this = v; } + HCLab(const Lab &v) { *this = v; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } /// Set from RGBA color - HCLab& operator=(const Color& v) { return *this = v.rgb(); } + HCLab &operator=(const Color &v) { return *this = v.rgb(); } /// Set from RGBA color - HCLab& operator=(const Colori& v) { return *this = Color(v); } + HCLab &operator=(const Colori &v) { return *this = Color(v); } /// Set from RGB color - HCLab& operator=(const RGB& v) { return *this = Lab(v); } + HCLab &operator=(const RGB &v) { return *this = Lab(v); } /// Set from HSV color - HCLab& operator=(const HSV& v) { return *this = Lab(v); } + HCLab &operator=(const HSV &v) { return *this = Lab(v); } - /// Set from CIEXYZ color - HCLab& operator=(const CIEXYZ& v) { return *this = Lab(v); } + /// Set from CIE_XYZ color + HCLab &operator=(const CIE_XYZ &v) { return *this = Lab(v); } /// Set from Lab color - HCLab& operator=(const Lab& v); + HCLab &operator=(const Lab &v); /// Get new HCLab with value component multiplied by a scalar HCLab operator*(float a) const { return HCLab(*this) *= a; } /// Multiply luminance component by a scalar - HCLab& operator*=(float a) { + HCLab &operator*=(float a) { l *= a; return *this; } /// Rotate hue in interval [0, 1) - HCLab& rotateHue(float dh) { + HCLab &rotateHue(float dh) { h += dh; return wrapHue(); } /// Wrap hue value into valid interval [0, 1) - HCLab& wrapHue() { + HCLab &wrapHue() { if (h > 1) { h -= int(h); } else if (h < 0) { @@ -910,55 +910,55 @@ struct Luv { /// @param[in] hsv 3-vector of Luv components template - Luv(const T* Luv) : l(Luv[0]), u(Luv[1]), v(Luv[2]) {} + Luv(const T *Luv) : l(Luv[0]), u(Luv[1]), v(Luv[2]) {} /// @param[in] w RGB color to convert from - Luv(const Color& w) { *this = w; } + Luv(const Color &w) { *this = w; } /// @param[in] w RGB color to convert from - Luv(const Colori& w) { *this = w; } + Luv(const Colori &w) { *this = w; } /// @param[in] w RGB color to convert from - Luv(const RGB& w) { *this = w; } + Luv(const RGB &w) { *this = w; } /// @param[in] w HSV color to convert from - Luv(const HSV& w) { *this = w; } + Luv(const HSV &w) { *this = w; } - /// @param[in] w CIEXYZ color to convert from - Luv(const CIEXYZ& w) { *this = w; } + /// @param[in] w CIE_XYZ color to convert from + Luv(const CIE_XYZ &w) { *this = w; } /// @param[in] w HCLuv color to convert from - Luv(const HCLuv& w) { *this = w; } + Luv(const HCLuv &w) { *this = w; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } /// Set from RGBA color - Luv& operator=(const Color& w) { return *this = w.rgb(); } + Luv &operator=(const Color &w) { return *this = w.rgb(); } /// Set from RGBA color - Luv& operator=(const Colori& w) { return *this = Color(w); } + Luv &operator=(const Colori &w) { return *this = Color(w); } - /// Set from CIEXYZ color - Luv& operator=(const CIEXYZ& w); + /// Set from CIE_XYZ color + Luv &operator=(const CIE_XYZ &w); /// Set from HCLuv color - Luv& operator=(const HCLuv& w); + Luv &operator=(const HCLuv &w); /// Set from RGB color - Luv& operator=(const RGB& w) { return *this = CIEXYZ(w); } + Luv &operator=(const RGB &w) { return *this = CIE_XYZ(w); } /// Set from HSV color - Luv& operator=(const HSV& w) { return *this = CIEXYZ(w); } + Luv &operator=(const HSV &w) { return *this = CIE_XYZ(w); } /// Get new Luv with value component multiplied by a scalar Luv operator*(float a) const { return Luv(*this) *= a; } /// Multiply lightness component by a scalar - Luv& operator*=(float a) { + Luv &operator*=(float a) { l *= a; return *this; } @@ -988,67 +988,67 @@ struct HCLuv { /// @param[in] hcl 3-vector of HCLuv components template - HCLuv(const T* HCLuv) : h(HCLuv[0]), c(HCLuv[1]), l(HCLuv[2]) {} + HCLuv(const T *HCLuv) : h(HCLuv[0]), c(HCLuv[1]), l(HCLuv[2]) {} /// @param[in] w RGB color to convert from - HCLuv(const Color& w) { *this = w; } + HCLuv(const Color &w) { *this = w; } /// @param[in] w RGB color to convert from - HCLuv(const Colori& w) { *this = w; } + HCLuv(const Colori &w) { *this = w; } /// @param[in] w RGB color to convert from - HCLuv(const RGB& w) { *this = w; } + HCLuv(const RGB &w) { *this = w; } /// @param[in] w HSV color to convert from - HCLuv(const HSV& w) { *this = w; } + HCLuv(const HSV &w) { *this = w; } - /// @param[in] v CIEXYZ color to convert from - HCLuv(const CIEXYZ& w) { *this = w; } + /// @param[in] v CIE_XYZ color to convert from + HCLuv(const CIE_XYZ &w) { *this = w; } /// @param[in] w Luv color to convert from - HCLuv(const Luv& w) { *this = w; } + HCLuv(const Luv &w) { *this = w; } /// Set color component at index with no bounds checking - float& operator[](int i) { return components[i]; } + float &operator[](int i) { return components[i]; } /// Get color component at index with no bounds checking - const float& operator[](int i) const { return components[i]; } + const float &operator[](int i) const { return components[i]; } /// Set from RGBA color - HCLuv& operator=(const Color& w) { return *this = w.rgb(); } + HCLuv &operator=(const Color &w) { return *this = w.rgb(); } /// Set from RGBA color - HCLuv& operator=(const Colori& w) { return *this = Color(w); } + HCLuv &operator=(const Colori &w) { return *this = Color(w); } /// Set from RGB color - HCLuv& operator=(const RGB& w) { return *this = Luv(w); } + HCLuv &operator=(const RGB &w) { return *this = Luv(w); } /// Set from HSV color - HCLuv& operator=(const HSV& w) { return *this = Luv(w); } + HCLuv &operator=(const HSV &w) { return *this = Luv(w); } - /// Set from CIEXYZ color - HCLuv& operator=(const CIEXYZ& w) { return *this = Luv(w); } + /// Set from CIE_XYZ color + HCLuv &operator=(const CIE_XYZ &w) { return *this = Luv(w); } /// Set from Luv color - HCLuv& operator=(const Luv& w); + HCLuv &operator=(const Luv &w); /// Get new HCLuv with value component multiplied by a scalar HCLuv operator*(float a) const { return HCLuv(*this) *= a; } /// Multiply luminance component by a scalar - HCLuv& operator*=(float a) { + HCLuv &operator*=(float a) { l *= a; return *this; } /// Rotate hue in interval [0, 1) - HCLuv& rotateHue(float dh) { + HCLuv &rotateHue(float dh) { h += dh; return wrapHue(); } /// Wrap hue value into valid interval [0, 1) - HCLuv& wrapHue() { + HCLuv &wrapHue() { if (h > 1) { h -= int(h); } else if (h < 0) { @@ -1060,25 +1060,25 @@ struct HCLuv { // Implementation -------------------------------------------------------------- -inline RGB operator+(float s, const RGB& c) { return c + s; } -inline RGB operator-(float s, const RGB& c) { return -c + s; } -inline RGB operator*(float s, const RGB& c) { return c * s; } -inline RGB operator/(float s, const RGB& c) { +inline RGB operator+(float s, const RGB &c) { return c + s; } +inline RGB operator-(float s, const RGB &c) { return -c + s; } +inline RGB operator*(float s, const RGB &c) { return c * s; } +inline RGB operator/(float s, const RGB &c) { return RGB(s / c.r, s / c.g, s / c.b); } -inline RGB& RGB::operator=(const Colori& v) { +inline RGB &RGB::operator=(const Colori &v) { return set(float(v.r) / 255.f, float(v.g) / 255.f, float(v.b) / 255.f); } -inline Color operator+(float s, const Color& c) { return c + s; } -inline Color operator-(float s, const Color& c) { return -c + s; } -inline Color operator*(float s, const Color& c) { return c * s; } -inline Color operator/(float s, const Color& c) { +inline Color operator+(float s, const Color &c) { return c + s; } +inline Color operator-(float s, const Color &c) { return -c + s; } +inline Color operator*(float s, const Color &c) { return c * s; } +inline Color operator/(float s, const Color &c) { return Color(s / c.r, s / c.g, s / c.b, s / c.a); } -inline Color& Color::operator=(const Colori& v) { +inline Color &Color::operator=(const Colori &v) { r = tof(v.r); g = tof(v.g); b = tof(v.b); @@ -1086,16 +1086,16 @@ inline Color& Color::operator=(const Colori& v) { return *this; } -inline Color& Color::operator=(const HSV& v) { +inline Color &Color::operator=(const HSV &v) { rgb() = v; return *this; } -inline Color& Color::operator=(const RGB& v) { +inline Color &Color::operator=(const RGB &v) { rgb() = v; return *this; } -inline Color& Color::invert() { +inline Color &Color::invert() { rgb().invert(); return *this; } @@ -1106,13 +1106,13 @@ inline Color Color::blackAndWhite() const { return Color(luminance() > 0.5f ? 1.f : 0.f); } -inline Colori& Colori::operator=(const HSV& v) { return *this = RGB(v); } +inline Colori &Colori::operator=(const HSV &v) { return *this = RGB(v); } -inline Colori& Colori::operator=(const RGB& v) { +inline Colori &Colori::operator=(const RGB &v) { return set(toi(v.r), toi(v.g), toi(v.b), 255); } -inline HSV operator*(float s, const HSV& c) { return c * s; } +inline HSV operator*(float s, const HSV &c) { return c * s; } } // namespace al diff --git a/include/al/ui/al_Composition.hpp b/include/al/ui/al_Composition.hpp index f5ad146d..5095030c 100644 --- a/include/al/ui/al_Composition.hpp +++ b/include/al/ui/al_Composition.hpp @@ -74,8 +74,8 @@ class Composition : public osc::MessageConsumer { std::string getName(); - /// Archive current compostion - bool archiveComposition(); + // /// Archive current compostion + // bool archiveComposition(); void setSubDirectory(std::string subDir) { mSubDirectory = subDir; } @@ -138,18 +138,13 @@ class Composition : public osc::MessageConsumer { std::function mEndCallback; void *mEndCallbackData; - std::function - mSequencerEndCallbackCache; - void *mSequencerEndCallbackDataCache; + std::function mSequencerEndCallbackCache; std::vector loadCompositionSteps( std::string compositionSteps); std::string getRootPath(); std::string getCurrentPath(); - static void waitForSequencerCallback(bool finished, PresetSequencer *seq, - void *userData); - static void playbackThread(Composition *composition); }; diff --git a/include/al/ui/al_FileSelector.hpp b/include/al/ui/al_FileSelector.hpp index 30ca7a15..24ebeecf 100644 --- a/include/al/ui/al_FileSelector.hpp +++ b/include/al/ui/al_FileSelector.hpp @@ -52,10 +52,13 @@ namespace al { /// @ingroup UI class FileSelector { public: - FileSelector(std::string globalRoot = "", - std::function function = nullptr); + FileSelector( + std::string globalRoot = "", + std::function filterfunction = [](std::string) { + return true; + }); - void start(std::string currentDir); + void start(std::string currentDir = ""); /** * @brief drawFileSelector diff --git a/include/al/ui/al_Parameter.hpp b/include/al/ui/al_Parameter.hpp index 703b07e3..45e97fe1 100644 --- a/include/al/ui/al_Parameter.hpp +++ b/include/al/ui/al_Parameter.hpp @@ -42,12 +42,8 @@ Andrés Cabrera mantaraya36@gmail.com */ -#include "al/math/al_Vec.hpp" -#include "al/protocol/al_OSC.hpp" -#include "al/spatial/al_Pose.hpp" -#include "al/types/al_Color.hpp" - #include + #include #include #include @@ -60,13 +56,30 @@ #include #include +#include "al/math/al_Vec.hpp" +#include "al/protocol/al_OSC.hpp" +#include "al/spatial/al_Pose.hpp" +#include "al/types/al_Color.hpp" + namespace al { +enum class TimeMasterMode { + TIME_MASTER_AUDIO, + TIME_MASTER_GRAPHICS, + TIME_MASTER_FREE, + TIME_MASTER_CPU +}; + // ParameterField // @ingroup UI class ParameterField { public: - typedef enum { FLOAT, INT32, STRING } ParameterDataType; + typedef enum { FLOAT, INT32, STRING, NULLDATA } ParameterDataType; + + ParameterField() { + mData = nullptr; + mType = NULLDATA; + } ParameterField(const float value) { mType = FLOAT; @@ -77,7 +90,7 @@ class ParameterField { ParameterField(const double value) { mType = FLOAT; mData = new float; - *static_cast(mData) = value; + *static_cast(mData) = float(value); } ParameterField(const int32_t value) { @@ -98,6 +111,23 @@ class ParameterField { *static_cast(mData) = value; } + virtual ~ParameterField() { + switch (mType) { + case FLOAT: + delete static_cast(mData); + break; + case STRING: + delete static_cast(mData); + break; + case INT32: + delete static_cast(mData); + break; + case NULLDATA: + break; + } + } + + // Copy constructor ParameterField(const ParameterField ¶mField) : mType(paramField.mType) { switch (mType) { case FLOAT: @@ -114,33 +144,39 @@ class ParameterField { *static_cast(mData) = *static_cast(paramField.mData); break; + case NULLDATA: + break; } } - virtual ~ParameterField() { - switch (mType) { - case FLOAT: - delete static_cast(mData); - break; - case STRING: - delete static_cast(mData); - break; - case INT32: - delete static_cast(mData); - break; - } + // Move constructor + ParameterField(ParameterField &&that) noexcept + : mType(NULLDATA), mData(nullptr) { + swap(*this, that); } - ParameterDataType type() { return mType; } + // Copy assignment operator + ParameterField &operator=(const ParameterField &other) { + ParameterField copy(other); + swap(*this, copy); + return *this; + } - // float get() { - // assert(mType == FLOAT); - // return *static_cast(mData); - // } + // Move assignment operator + ParameterField &operator=(ParameterField &&that) { + swap(*this, that); + return *this; + } + + friend void swap(ParameterField &lhs, ParameterField &rhs) noexcept { + std::swap(lhs.mData, rhs.mData); + std::swap(lhs.mType, rhs.mType); + } + + ParameterDataType type() { return mType; } template type get() { - // assert(mType == STRING); return *static_cast(mData); } @@ -288,19 +324,20 @@ class ParameterMeta { return value; } - virtual void get(std::vector &fields) { + virtual void get(std::vector & /*fields*/) { std::cout << "get(std::vector &fields) not implemented for " << typeid(*this).name() << std::endl; } - virtual void set(std::vector &fields) { + virtual void set(std::vector & /*fields*/) { std::cout << "set(std::vector &fields) not implemented for " << typeid(*this).name() << std::endl; } virtual void sendValue(osc::Send &sender, std::string prefix = "") { + (void)prefix; // Remove compiler warning std::cout << "sendValue function not implemented for " << typeid(*this).name() << std::endl; } @@ -469,6 +506,53 @@ class ParameterWrapper : public ParameterMeta { */ void registerChangeCallback(ParameterChangeCallback cb); + /** + * @brief Determines whether value change callbacks are called synchronously + * @param synchronous + * + * If set to true, parameter change callbacks are called directly from the + * setter function, i.e. as soon as the parameter value changes. This behavior + * might be problematic in some cases, for example when an OSC message + * triggers a change in the opengl state. This will cause a crash as the + * opengl functions need to be called from the opengl context instead of from + * a thread in the network context. By setting this to false and then calling + * runChangeCallbacks within the opengl thread will call the callbacks + * whenever the value has changed, but at the right time, in the right + * context. + */ + void setSynchronousCallbacks(bool synchronous = true) { + mSynchronous = synchronous; + if (mCallbacks.size() > 0 && mCallbacks[0] == mAsyncCallback) { + if (synchronous) { + mCallbacks.erase(mCallbacks.begin()); + } else { + std::cout << "WARNING: setSynchronousCallbacks() already set to false" + << std::endl; + } + } else { + if (!synchronous) { + mCallbacks.insert(mCallbacks.begin(), mAsyncCallback); + } + } + } + + bool hasChange() { return mChanged; } + + /** + * @brief call change callbacks if value has changed since last call + */ + void processChange() { + if (mChanged && mCallbacks.size() > 0 && mCallbacks[0] == mAsyncCallback) { + auto callbackIt = mCallbacks.begin() + 1; + ParameterType value = get(); + mChanged = false; + while (callbackIt != mCallbacks.end()) { + (*(*callbackIt))(value); + callbackIt++; + } + } + } + std::vector *> operator<<( ParameterWrapper &newParam) { std::vector *> paramList; @@ -495,16 +579,24 @@ class ParameterWrapper : public ParameterMeta { ParameterType mMin; ParameterType mMax; + void runChangeCallbacksSynchronous(ParameterType &value); + std::shared_ptr mProcessCallback; // void * mProcessUdata; - std::vector> mCallbacks; // std::vector mCallbackUdata; private: - std::mutex - *mMutex; // pointer to avoid having to explicitly declare copy/move + // pointer to avoid having to explicitly declare copy/move + std::mutex *mMutex; ParameterType mValue; ParameterType mValueCache; + + bool mSynchronous{true}; + std::shared_ptr mAsyncCallback; + bool mChanged{false}; + + private: + std::vector> mCallbacks; }; /** @@ -608,7 +700,9 @@ class Parameter : public ParameterWrapper { } virtual void set(std::vector &fields) override { + assert(fields.size() == 1); if (fields.size() == 1) { + assert(fields[0].type() == ParameterField::FLOAT); set(fields[0].get()); } else { std::cout << "Wrong number of parameters for " << getFullAddress() @@ -1211,6 +1305,9 @@ ParameterWrapper::ParameterWrapper(std::string parameterName, mValue = defaultValue; mValueCache = defaultValue; mMutex = new std::mutex; + std::shared_ptr mAsyncCallback = + std::make_shared( + [&](ParameterType value) { mChanged = true; }); } template @@ -1260,6 +1357,21 @@ void ParameterWrapper::registerChangeCallback( // mCallbackUdata.push_back(userData); } +template +void ParameterWrapper::runChangeCallbacksSynchronous( + ParameterType &value) { + for (auto cb : mCallbacks) { + if (cb == mAsyncCallback) { + // Async callback is just a marker and should be the first callback + // in the vector + mChanged = true; + return; + } else { + (*cb)(value); + } + } +} + } // namespace al #endif // AL_PARAMETER_H diff --git a/include/al/ui/al_ParameterBundle.hpp b/include/al/ui/al_ParameterBundle.hpp index 81a332b0..1b6b97db 100644 --- a/include/al/ui/al_ParameterBundle.hpp +++ b/include/al/ui/al_ParameterBundle.hpp @@ -102,7 +102,9 @@ class ParameterBundle { std::vector ¶meters() { return mParameters; } - std::map &bundles() { return mBundles; } + std::map> &bundles() { + return mBundles; + } ParameterBundle &operator<<(ParameterMeta *parameter); ParameterBundle &operator<<(ParameterMeta ¶meter); @@ -118,7 +120,7 @@ class ParameterBundle { // index to identify bundle. std::vector mParameters; - std::map mBundles; + std::map> mBundles; std::vector mNotifiers; }; diff --git a/include/al/ui/al_ParameterGUI.hpp b/include/al/ui/al_ParameterGUI.hpp index 029304f3..7e65ae13 100644 --- a/include/al/ui/al_ParameterGUI.hpp +++ b/include/al/ui/al_ParameterGUI.hpp @@ -2,31 +2,29 @@ #define AL_PARAMETERGUI_H /* Allolib -- - Multimedia / virtual environment application class library + Multimedia / virtual environment application class library - Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, + Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB. Copyright (C) 2012-2018. The Regents of the University of California. - All rights reserved. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. + documentation and/or other materials provided with the distribution. - Neither the name of the University of California nor the names - of its contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + Neither the name of the University of California nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, @@ -36,10 +34,10 @@ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - File description: - Expose parameters on the network - File author(s): - Andrés Cabrera mantaraya36@gmail.com + File description: + Expose parameters on the network + File author(s): + Andrés Cabrera mantaraya36@gmail.com */ #include @@ -452,7 +450,7 @@ class SynthGUIManager { SequenceRecorder mPresetSequenceRecorder; // PolySynth *mSynth; - SynthSequencer mSequencer{PolySynth::TIME_MASTER_AUDIO}; + SynthSequencer mSequencer{TimeMasterMode::TIME_MASTER_AUDIO}; SynthRecorder mRecorder; std::vector> mBundles; diff --git a/include/al/ui/al_ParameterMIDI.hpp b/include/al/ui/al_ParameterMIDI.hpp index d882bd39..ebb7eccf 100644 --- a/include/al/ui/al_ParameterMIDI.hpp +++ b/include/al/ui/al_ParameterMIDI.hpp @@ -71,16 +71,16 @@ class ParameterMIDI : public MIDIMessageHandler { public: ParameterMIDI() {} - ParameterMIDI(int deviceIndex, bool verbose = false) { - MIDIMessageHandler::bindTo(mMidiIn); + ParameterMIDI(unsigned int deviceIndex, bool verbose = false) { + MIDIMessageHandler::bindTo(mRtMidiIn); mVerbose = verbose; try { - mMidiIn.openPort(deviceIndex); + mRtMidiIn.openPort(deviceIndex); printf("ParameterMIDI: Opened port to %s\n", - mMidiIn.getPortName(deviceIndex).c_str()); - } catch (al::MIDIError &error) { - std::cout << "ParameterMIDI Warning: Could not open MIDI port " - << deviceIndex << std::endl; + mRtMidiIn.getPortName(deviceIndex).c_str()); + } catch (RtMidiError &error) { + std::cout << "ParameterMIDI Warning opening MIDI port:" + << error.getMessage() << deviceIndex << std::endl; } } @@ -89,15 +89,15 @@ class ParameterMIDI : public MIDIMessageHandler { open(deviceIndex, verbose); } - void open(int deviceIndex = 0) { - MIDIMessageHandler::bindTo(mMidiIn); + void open(unsigned int deviceIndex = 0) { + MIDIMessageHandler::bindTo(mRtMidiIn); try { - mMidiIn.openPort(deviceIndex); + mRtMidiIn.openPort(deviceIndex); printf("ParameterMIDI: Opened port to %s\n", - mMidiIn.getPortName(deviceIndex).c_str()); - } catch (al::MIDIError &error) { - std::cout << "ParameterMIDI Warning: Could not open MIDI port " - << deviceIndex << std::endl; + mRtMidiIn.getPortName(deviceIndex).c_str()); + } catch (RtMidiError &error) { + std::cout << "ParameterMIDI Warning opening MIDI port:" + << error.getMessage() << deviceIndex << std::endl; } } @@ -107,7 +107,7 @@ class ParameterMIDI : public MIDIMessageHandler { } void close() { - mMidiIn.closePort(); + mRtMidiIn.closePort(); MIDIMessageHandler::clearBindings(); } @@ -174,7 +174,7 @@ class ParameterMIDI : public MIDIMessageHandler { mIncrementBindings.push_back(newBinding); } - bool isOpen() { return mMidiIn.isPortOpen(); } + bool isOpen() { return mRtMidiIn.isPortOpen(); } virtual void onMIDIMessage(const MIDIMessage &m) override { if (m.type() & MIDIByte::CONTROL_CHANGE) { @@ -264,7 +264,7 @@ class ParameterMIDI : public MIDIMessageHandler { std::vector getCurrentNoteBindings() { return mNoteBindings; } private: - MIDIIn mMidiIn; + RtMidiIn mRtMidiIn; bool mVerbose; std::vector mControlBindings; std::vector mNoteBindings; diff --git a/include/al/ui/al_PickableManager.hpp b/include/al/ui/al_PickableManager.hpp index ee087a49..d2eb3139 100644 --- a/include/al/ui/al_PickableManager.hpp +++ b/include/al/ui/al_PickableManager.hpp @@ -6,6 +6,7 @@ // #include #include "al/graphics/al_Graphics.hpp" +#include "al/io/al_Window.hpp" #include "al/math/al_Ray.hpp" #include "al/ui/al_Pickable.hpp" diff --git a/include/al/ui/al_PresetHandler.hpp b/include/al/ui/al_PresetHandler.hpp index 3ad8fd37..c59290da 100644 --- a/include/al/ui/al_PresetHandler.hpp +++ b/include/al/ui/al_PresetHandler.hpp @@ -1,32 +1,30 @@ #ifndef AL_PRESETHANDLER_H #define AL_PRESETHANDLER_H -/* Allocore -- - Multimedia / virtual environment application class library +/* Allolib -- + Multimedia / virtual environment application class library - Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, - UCSB. Copyright (C) 2016. The Regents of the University of California. All - rights reserved. + Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, + UCSB. Copyright (C) 2012-2018. The Regents of the University of California. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. + documentation and/or other materials provided with the distribution. - Neither the name of the University of California nor the names - of its contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + Neither the name of the University of California nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, @@ -36,10 +34,10 @@ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - File description: - Preset classes that encapsulates storing values for groups of parameters - File author(s): - Andrés Cabrera mantaraya36@gmail.com + File description: + Preset classes that encapsulates storing values for groups of parameters + File author(s): + Andrés Cabrera mantaraya36@gmail.com */ #include @@ -76,7 +74,19 @@ class PresetHandler { * @param verbose if true, print diagnostic messages * */ - PresetHandler(std::string rootDirectory = "presets", bool verbose = false); + PresetHandler(std::string rootDirectory, bool verbose = false); + + /** + * @brief Constructor with option to set time master mode + * @param timeMasterMode + * + * Only two modes are currently valid for PresetHandler: + * TIME_MASTER_CPU and TIME_MASTER_ASYNC. The first will start a CPU + * thread that handles morphing and setting values, the second does not + * start the thread, so user must manually call tick() + */ + PresetHandler(TimeMasterMode timeMasterMode = TimeMasterMode::TIME_MASTER_CPU, + std::string rootDirectory = "presets", bool verbose = false); ~PresetHandler(); @@ -149,22 +159,19 @@ class PresetHandler { * A factor of 0 uses preset 1 and a factor of 1 uses preset 2. Values * in between result in linear interpolation of the values. */ - void setInterpolatedPreset(int index1, int index2, double factor, - bool synchronous = true); + void setInterpolatedPreset(int index1, int index2, double factor); void setInterpolatedPreset(std::string presetName1, std::string presetName2, - double factor, bool synchronous = true); + double factor); + // static void setParameterValues(ParameterMeta *param, + // std::vector &values); /** - * @brief Interpolate between current values and new values according to + * @brief Interpolate between start and end values according to * factor */ - static void setParameterValues(ParameterMeta *param, - std::vector &values, - double factor = 1.0); - - void morphTo(ParameterStates ¶meterStates, float morphTime); - void stopMorph(); + void setInterpolatedValues(ParameterStates &startValues, + ParameterStates &endValues, double factor = 1.0); std::map availablePresets(); std::string getPresetName(int index); @@ -185,6 +192,17 @@ class PresetHandler { float getMorphTime(); void setMorphTime(float time); + void stopMorphing() { mTotalSteps.store(0); } + void morphTo(ParameterStates ¶meterStates, float morphTime); + void morphTo(std::string presetName, float morphTime); + + void setMorphStepTime(float stepTime) { mMorphInterval = stepTime; } + + void stepMorphing(double stepTime); + + /// Step morphing to adjust parameter values to next step. You need to call + /// this function only if TimeMasterMode is TIME_MASTER_ASYNC + void stepMorphing(); void setSubDirectory(std::string directory); std::string getSubDirectory() { return mSubDir; } @@ -276,140 +294,20 @@ class PresetHandler { void changeParameterValue(std::string presetName, std::string parameterPath, float newValue); - static int asciiToPresetIndex(int ascii, int offset = 0) { - int index = -1; - - switch (ascii) { - case '1': - index = 0; - break; - case '2': - index = 1; - break; - case '3': - index = 2; - break; - case '4': - index = 3; - break; - case '5': - index = 4; - break; - case '6': - index = 5; - break; - case '7': - index = 6; - break; - case '8': - index = 7; - break; - case '9': - index = 8; - break; - case '0': - index = 9; - break; - case 'q': - index = 10; - break; - case 'w': - index = 11; - break; - case 'e': - index = 12; - break; - case 'r': - index = 13; - break; - case 't': - index = 14; - break; - case 'y': - index = 15; - break; - case 'u': - index = 16; - break; - case 'i': - index = 17; - break; - case 'o': - index = 18; - break; - case 'p': - index = 19; - break; - case 'a': - index = 20; - break; - case 's': - index = 21; - break; - case 'd': - index = 22; - break; - case 'f': - index = 23; - break; - case 'g': - index = 24; - break; - case 'h': - index = 25; - break; - case 'j': - index = 26; - break; - case 'k': - index = 27; - break; - case 'l': - index = 28; - break; - case ';': - index = 29; - break; - ; - case 'z': - index = 30; - break; - case 'x': - index = 31; - break; - case 'c': - index = 32; - break; - case 'v': - index = 33; - break; - case 'b': - index = 34; - break; - case 'n': - index = 35; - break; - case 'm': - index = 36; - break; - case ',': - index = 37; - break; - case '.': - index = 38; - break; - case '/': - index = 39; - break; - } - if (index >= 0) { - index += offset; - } - - return index; - } + /** + * @brief Map QWERTY ascii keys to presets 0-49 + * @param ascii ascii code of the key to mapt + * @param offset add an offset on output + * @return the mapped value 0-39 plus offset. Returns -1 on failure to map. + * + * This maps four rows of 10 keys on the ASCII keyboard (regular QWERTY US + * keys) to numbers 0-39. This can be useful for quick preset mapping using + * the whole keyboard. + */ + static int asciiToPresetIndex(int ascii, int offset = 0); - void verbose(bool isVerbose) { mVerbose = isVerbose; } + [[deprecated]] void verbose(bool isVerbose) { mVerbose = isVerbose; } + void setVerbose(bool isVerbose = true) { mVerbose = isVerbose; } bool verbose() { return mVerbose; } /** @@ -430,39 +328,54 @@ class PresetHandler { bool savePresetValues(const ParameterStates &values, std::string presetName, bool overwrite = true); + void setTimeMaster(TimeMasterMode masterMode); + + void startCpuThread(); + void stopCpuThread(); + private: - std::vector getParameterValue(ParameterMeta *p); - void setParametersInBundle(ParameterBundle *bundle, std::string bundlePrefix, - PresetHandler *handler, float factor = 1.0); + // std::vector getParameterValue(ParameterMeta *p); + // void setParametersInBundle(ParameterBundle *bundle, std::string + // bundlePrefix, + // PresetHandler *handler, double factor = 1.0); static void morphingFunction(PresetHandler *handler); ParameterStates getBundleStates(ParameterBundle *bundle, std::string id); bool mVerbose{false}; - bool mUseCallbacks{false}; + bool mUseCallbacks{true}; std::string mRootDir; std::string mSubDir; // Optional sub directory, e.g. for preset map archives - std::string mFileName; + std::string mCurrentMapName; + std::string mCurrentPresetName; std::vector mParameters; + std::vector mSkipParameters; std::mutex mSkipParametersLock; std::map> mBundles; + // Protects file writing from this class. Only one file may be written at + // a time. std::mutex mFileLock; - bool mRunning{false}; // To keep the morphing thread alive - // bool mMorph; // To be able to trip and stop morphing at any time. - std::atomic mMorphRemainingSteps; - float mMorphInterval; - Parameter mMorphTime; - // std::mutex mMorphLock; std::mutex mTargetLock; - std::condition_variable mMorphConditionVar; ParameterStates mTargetValues; + ParameterStates mStartValues; + + TimeMasterMode mTimeMasterMode{TimeMasterMode::TIME_MASTER_CPU}; - std::thread mMorphingThread; + Parameter mMorphTime{"morphTime", "", 0.0, "", 0.0, 20.0}; + + std::atomic mMorphStepCount{0}; + std::atomic mTotalSteps{0}; + // std::atomic mCurrentMorphIndex; + bool mCpuThreadRunning{false}; // To keep the morphing thread alive + std::unique_ptr mMorphingThread; + // std::condition_variable mMorphConditionVar; + double mMorphInterval{0.05}; + // std::atomic mMorphing; std::vector> mCallbacks; @@ -473,7 +386,6 @@ class PresetHandler { std::vector> mPresetsMapCbs; std::map mPresetsMap; - std::string mCurrentPresetName; }; } // namespace al diff --git a/include/al/ui/al_PresetMIDI.hpp b/include/al/ui/al_PresetMIDI.hpp index b613258d..75416c51 100644 --- a/include/al/ui/al_PresetMIDI.hpp +++ b/include/al/ui/al_PresetMIDI.hpp @@ -61,12 +61,12 @@ class PresetMIDI : public MIDIMessageHandler { PresetMIDI() {} PresetMIDI(int deviceIndex) : mPresetHandler(nullptr) { - MIDIMessageHandler::bindTo(mMidiIn); + MIDIMessageHandler::bindTo(mRtMidiIn); try { - mMidiIn.openPort(deviceIndex); + mRtMidiIn.openPort(deviceIndex); printf("PresetMIDI: Opened port to %s\n", - mMidiIn.getPortName(deviceIndex).c_str()); - } catch (al::MIDIError error) { + mRtMidiIn.getPortName(deviceIndex).c_str()); + } catch (RtMidiError error) { std::cout << "PresetMIDI Warning: Could not open MIDI port " << deviceIndex << std::endl; } @@ -92,32 +92,32 @@ class PresetMIDI : public MIDIMessageHandler { } void open(int deviceIndex) { - MIDIMessageHandler::bindTo(mMidiIn); + MIDIMessageHandler::bindTo(mRtMidiIn); - if (mMidiIn.isPortOpen()) { - mMidiIn.closePort(); + if (mRtMidiIn.isPortOpen()) { + mRtMidiIn.closePort(); } try { - if (deviceIndex >= 0 && deviceIndex < (int)mMidiIn.getPortCount()) { - mMidiIn.openPort(deviceIndex); + if (deviceIndex >= 0 && deviceIndex < (int)mRtMidiIn.getPortCount()) { + mRtMidiIn.openPort(deviceIndex); printf("PresetMIDI: Opened port to %s\n", - mMidiIn.getPortName(deviceIndex).c_str()); + mRtMidiIn.getPortName(deviceIndex).c_str()); } else { std::cerr << "PresetMIDI Warning: Could not open MIDI port " << deviceIndex << std::endl; } - } catch (al::MIDIError error) { + } catch (RtMidiError error) { std::cerr << "PresetMIDI Warning: Could not open MIDI port " << deviceIndex << std::endl; } } void close() { - mMidiIn.closePort(); + mRtMidiIn.closePort(); MIDIMessageHandler::clearBindings(); } - bool isOpen() { return mMidiIn.isPortOpen(); } + bool isOpen() { return mRtMidiIn.isPortOpen(); } void setPresetHandler(PresetHandler &presetHandler) { mPresetHandler = &presetHandler; @@ -163,7 +163,7 @@ class PresetMIDI : public MIDIMessageHandler { PresetHandler *mPresetHandler; - MIDIIn mMidiIn; + RtMidiIn mRtMidiIn; std::vector mNoteBindings; std::vector mProgramBindings; }; diff --git a/include/al/ui/al_PresetSequencer.hpp b/include/al/ui/al_PresetSequencer.hpp index e86fc7b0..806c5be6 100644 --- a/include/al/ui/al_PresetSequencer.hpp +++ b/include/al/ui/al_PresetSequencer.hpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,24 @@ class Composition; * * The file should end with two colons (::). * + * Individual parameters can also be sequenced through the preset sequencer. + * They must be registered through registerParameter() or the streaming (<<) + * operator. + * + * The line should start with the '+' character followed by the delta time to + * the previous line. Note that parameters have delta times relative to both + * preset and parameter steps, but preset events are relative to other preset + * steps and ignore parameter and event steps. + * + * @code + * preset1:0.0:3.0 + * +0.1:/X:0.3 + * +0.1:/X:0.4 + * +0.1:/X:0.5 + * preset2:4.0:2.0 + * :: + * @endcode + * * The directory where sequences are loaded is taken from the PresetHandler * object registered with the sequencer. * @@ -100,22 +119,24 @@ class PresetSequencer : public osc::MessageConsumer { friend class Composition; public: - PresetSequencer(); + PresetSequencer( + TimeMasterMode timeMasterMode = TimeMasterMode::TIME_MASTER_CPU); ~PresetSequencer() override; typedef enum { PRESET, EVENT, PARAMETER } StepType; struct Step { StepType type = PRESET; - std::string presetName; + std::string name; float morphTime; // The time to get to the preset float waitTime; // The time to stay in the preset before the next step - std::vector params; + std::vector params; }; typedef struct { std::string eventName; - std::function ¶ms)> callback; + std::function ¶ms)> + callback; void *callbackData; } EventCallback; @@ -128,7 +149,8 @@ class PresetSequencer : public osc::MessageConsumer { * a sequence is playing when this command is issued, the current playback * is interrupted and the new sequence requested starts immediately. */ - void playSequence(std::string sequenceName, double timeScale = 1.0f); + void playSequence(std::string sequenceName, double timeScale = 1.0f, + double timeOffset = 0.0); void stopSequence(bool triggerCallbacks = true); @@ -145,21 +167,7 @@ class PresetSequencer : public osc::MessageConsumer { */ void rewind(); - bool playbackFinished() { return mSteps.size() == 0; } - - /** - * @brief Stores a copy of a sequence with its associated presets - * @param sequenceName Name of sequence without extension. Searched for in - * mDirectory - * @param overwrite if directory exists, delete it before writing if overwrite - * is true. - * @return returns false if there was any error archiving sequence - * - * Stores a copy of the sequence and all its associated presets in a new - * folder. A PresetHandler must be registered for this to work as this sets - * the current Sequence and preset directory. - */ - bool archiveSequence(std::string sequenceName, bool overwrite = true); + bool playbackFinished() { return mSteps.size() == mCurrentStep; } /** * @brief getSequenceList returns a list of sequences in the current sequence @@ -199,12 +207,7 @@ class PresetSequencer : public osc::MessageConsumer { * preset handler. The sequencer's directory is set to * the preset handler's directory */ - PresetSequencer ®isterPresetHandler(PresetHandler &presetHandler) { - mPresetHandler = &presetHandler; - mDirectory = mPresetHandler->getCurrentPath(); - // std::cout << "Path set to:" << mDirectory << std::endl; - return *this; - } + PresetSequencer ®isterPresetHandler(PresetHandler &presetHandler); /** * @brief Register PresetHandler through the << operator @@ -233,8 +236,8 @@ class PresetSequencer : public osc::MessageConsumer { * The sequence is searched in the PresetHandler current path or the * PresetSequencer's directory if PresetHandler not registered. */ - std::queue loadSequence(std::string sequenceName, - double timeScale = 1.0f); + std::vector loadSequence(std::string sequenceName, + double timeScale = 1.0); std::string currentSequence() { return mCurrentSequence; } @@ -248,7 +251,8 @@ class PresetSequencer : public osc::MessageConsumer { */ void registerEventCommand( std::string eventName, - std::function ¶ms)> callback, + std::function ¶ms)> + callback, void *data); void setOSCSubPath(std::string subPath) { mOSCsubPath = subPath; } @@ -260,9 +264,7 @@ class PresetSequencer : public osc::MessageConsumer { * as it is ready to start playing before calling the first step. */ void registerBeginCallback( - std::function - beginCallback, - void *userData = nullptr); + std::function beginCallback); void enableBeginCallback(bool enable) { mBeginCallbackEnabled = enable; } @@ -278,17 +280,15 @@ class PresetSequencer : public osc::MessageConsumer { * user prematurely, it will send false. */ void registerEndCallback( - std::function - endCallback, - void *userData = nullptr); + std::function endCallback); void enableEndCallback(bool enable) { mEndCallbackEnabled = enable; } void toggleEnableEndCallback() { mEndCallbackEnabled = !mEndCallbackEnabled; } void registerTimeChangeCallback(std::function func, - float minTimeDeltaSec = 0.05f); + float minTimeDeltaSec = -1.0); + float getSequenceStartOffset(std::string sequenceName); float getSequenceTotalDuration(std::string sequenceName); // For programmatic control of the sequencer: @@ -303,127 +303,86 @@ class PresetSequencer : public osc::MessageConsumer { */ void appendStep(Step &newStep); + void setTimeMaster(TimeMasterMode masterMode); + + /** + * @brief step sequencer forward dt amount of time + * @param dt amount of time (seconds) to step + * + * Any parameter and preset events that fall within this delta time will + * be applied. + */ + void stepSequencer(double dt); + + /** + * @brief move sequencer forward by time set using setSequencerStepTime() + */ + void stepSequencer(); + + void setSequencerStepTime(double stepTime) { mGranularity = stepTime * 1e9; } + protected: virtual bool consumeMessage(osc::Message &m, std::string rootOSCPath) override; + void processTimeChangeRequest(); + void updateTime(double time); + + void updateSequencer(); + private: static void sequencerFunction(PresetSequencer *sequencer); std::string buildFullPath(std::string sequenceName); - std::queue mSteps; - std::queue - mMostRecentSequence; // Steps from last sequence loaded from disk + void startCpuThread(); + void stopCpuThread(); + + std::vector mSteps; std::string mDirectory; PresetHandler *mPresetHandler{nullptr}; std::vector mParameters; std::string mOSCsubPath; std::string mCurrentSequence; - std::mutex mSequenceLock; - std::mutex mPlayWaitLock; - std::condition_variable mPlayWaitVariable; - std::atomic mTimeRequest{-1.0f}; // Request setting the current time. // Passes info to playback thread - bool mSequencerActive; + TimeMasterMode mTimeMasterMode; + + bool mSequencerActive{false}; bool mRunning; - bool mStartingRun; - std::unique_ptr mSequencerThread; - const int mGranularity = 10; // milliseconds + bool mStartRunning; + std::queue mParameterList; + double mCurrentTime = 0.0; // Current time (in seconds) + double mTargetTime; + double mLastPresetTime; // To anchor parameter deltas + double mParameterTargetTime; + double mLastTimeUpdate = 0.0; + double mStepTime; + + uint64_t mGranularity = 10e6; // nanoseconds bool mBeginCallbackEnabled; - std::function mBeginCallback; - void *mBeginCallbackData; + std::function mBeginCallback; bool mEndCallbackEnabled; - std::function mEndCallback; - void *mEndCallbackData; - + std::function mEndCallback; std::vector mEventCallbacks; - std::function mTimeChangeCallback; - float mTimeChangeMinTimeDelta = 0; -}; - -/// SequenceServer -/// @ingroup UI -class SequenceServer : public osc::PacketHandler, public OSCNotifier { - public: - /** - * @brief SequenceServer constructor - * - * @param oscAddress The network address on which to listen to. If empty use - * all available network interfaces. Defaults to "127.0.0.1". - * @param oscPort The network port on which to listen. Defaults to 9012. - * - * The sequencer server triggers sequences when it receives a valid sequence - * name on OSC path /sequence. - */ + std::vector> mTimeChangeCallbacks; + float mTimeChangeMinTimeDelta = 0.05f; - SequenceServer(std::string oscAddress = "127.0.0.1", int oscPort = 9012); - /** - * @brief using this constructor reuses the existing osc::Recv server from the - * ParameterServer object - * @param paramServer an existing ParameterServer object - * - * You will want to reuse an osc::Recv server when you want to expose the - * interface thorugh the same network port. Since network ports are exclusive, - * once a port is bound, it can't be used. You might need to expose the - * parameters on the same network port when using things like - * interface.simpleserver.js That must connect all interfaces to the same - * network port. - */ - SequenceServer(ParameterServer ¶mServer); - ~SequenceServer(); - - virtual void onMessage(osc::Message &m); - - // Special cases of objects that are handled in specific ways - SequenceServer ®isterSequencer(PresetSequencer &sequencer); - SequenceServer ®isterRecorder(SequenceRecorder &recorder); - SequenceServer ®isterMessageConsumer(osc::MessageConsumer &consumer); - - /** - * @brief print prints information about the server to std::out - */ - void print(); + // CPU thread - /** - * @brief stopServer stops the OSC server thread. Calling this function - * is sometimes required when this object is destroyed abruptly and the - * destructor is not called. - */ - void stopServer(); - - SequenceServer &operator<<(PresetSequencer &sequencer) { - return registerSequencer(sequencer); - } - SequenceServer &operator<<(SequenceRecorder &recorder) { - return registerRecorder(recorder); - } - SequenceServer &operator<<(osc::MessageConsumer &consumer) { - return registerMessageConsumer(consumer); - } - - void setAddress(std::string address); - std::string getAddress(); - - protected: - // void attachPacketHandler(osc::PacketHandler *handler); - static void changeCallback(int value, void *sender, void *userData); - - private: - osc::Recv *mServer; - PresetSequencer *mSequencer; - SequenceRecorder *mRecorder; - // ParameterServer *mParamServer; - std::vector mCompositions; - // std::mutex mServerLock; - std::string mOSCpath; - std::string mOSCQueryPath; - // std::mutex mHandlerLock; - // std::vector mHandlers; - std::vector mConsumers; + // std::chrono::high_resolution_clock::time_point mSequenceStart = + // std::chrono::high_resolution_clock::now(); + std::unique_ptr mSequencerThread; + std::mutex mSequenceLock; + uint64_t mCurrentStep; + PresetHandler::ParameterStates mStartValues; + std::mutex mPlayWaitLock; + std::condition_variable mPlayWaitVariable; + // std::mutex mPlayStartedLock; + // std::condition_variable mPlayStartedVariable; + std::shared_ptr> mPlayPromiseObj; }; } // namespace al diff --git a/include/al/ui/al_SequenceRecorder.hpp b/include/al/ui/al_SequenceRecorder.hpp index 799adb08..43150783 100644 --- a/include/al/ui/al_SequenceRecorder.hpp +++ b/include/al/ui/al_SequenceRecorder.hpp @@ -103,6 +103,15 @@ class SequenceRecorder : public osc::MessageConsumer { std::string lastSequenceName(); std::string lastSequenceSubDir(); + /** + * @brief setDirectory sets the working directory for the SequenceRecorder + * @param directory + * + * If a PresetHandler is registered, this value + * is ignored. + */ + void setDirectory(std::string directory); + std::string getCurrentPath() { return mPresetHandler->getCurrentPath(); } SequenceRecorder &operator<<(PresetHandler &handler) { @@ -146,8 +155,8 @@ class SequenceRecorder : public osc::MessageConsumer { // struct Step { // std::string presetName; // float delta; // The time to get to the preset - // float duration; // The time to stay in the preset before the next - //step + // float duration; // The time to stay in the preset before the + // next step // }; std::string mDirectory; diff --git a/include/al/ui/al_SequenceServer.hpp b/include/al/ui/al_SequenceServer.hpp new file mode 100644 index 00000000..43bef3ba --- /dev/null +++ b/include/al/ui/al_SequenceServer.hpp @@ -0,0 +1,95 @@ +#ifndef INCLUDE_AL_SEQUENCESERVER +#define INCLUDE_AL_SEQUENCESERVER + +#include + +#include "al/ui/al_ParameterServer.hpp" +#include "al/ui/al_PresetSequencer.hpp" +#include "al/ui/al_SequenceRecorder.hpp" + +namespace al { + +/// SequenceServer +/// @ingroup UI +class SequenceServer : public osc::PacketHandler, public OSCNotifier { + public: + /** + * @brief SequenceServer constructor + * + * @param oscAddress The network address on which to listen to. If empty use + * all available network interfaces. Defaults to "127.0.0.1". + * @param oscPort The network port on which to listen. Defaults to 9012. + * + * The sequencer server triggers sequences when it receives a valid sequence + * name on OSC path /sequence. + */ + + SequenceServer(std::string oscAddress = "127.0.0.1", int oscPort = 9012); + /** + * @brief using this constructor reuses the existing osc::Recv server from the + * ParameterServer object + * @param paramServer an existing ParameterServer object + * + * You will want to reuse an osc::Recv server when you want to expose the + * interface thorugh the same network port. Since network ports are exclusive, + * once a port is bound, it can't be used. You might need to expose the + * parameters on the same network port when using things like + * interface.simpleserver.js That must connect all interfaces to the same + * network port. + */ + SequenceServer(ParameterServer ¶mServer); + ~SequenceServer(); + + virtual void onMessage(osc::Message &m); + + // Special cases of objects that are handled in specific ways + SequenceServer ®isterSequencer(PresetSequencer &sequencer); + SequenceServer ®isterRecorder(SequenceRecorder &recorder); + SequenceServer ®isterMessageConsumer(osc::MessageConsumer &consumer); + + /** + * @brief print prints information about the server to std::out + */ + void print(); + + /** + * @brief stopServer stops the OSC server thread. Calling this function + * is sometimes required when this object is destroyed abruptly and the + * destructor is not called. + */ + void stopServer(); + + SequenceServer &operator<<(PresetSequencer &sequencer) { + return registerSequencer(sequencer); + } + SequenceServer &operator<<(SequenceRecorder &recorder) { + return registerRecorder(recorder); + } + SequenceServer &operator<<(osc::MessageConsumer &consumer) { + return registerMessageConsumer(consumer); + } + + void setAddress(std::string address); + std::string getAddress(); + + protected: + // void attachPacketHandler(osc::PacketHandler *handler); + static void changeCallback(int value, void *sender, void *userData); + + private: + osc::Recv *mServer; + PresetSequencer *mSequencer; + SequenceRecorder *mRecorder; + // ParameterServer *mParamServer; + std::vector mCompositions; + // std::mutex mServerLock; + std::string mOSCpath; + std::string mOSCQueryPath; + // std::mutex mHandlerLock; + // std::vector mHandlers; + std::vector mConsumers; +}; + +} // namespace al + +#endif // INCLUDE_AL_SEQUENCESERVER diff --git a/readme.md b/readme.md index 2b19522b..ddc6e42e 100644 --- a/readme.md +++ b/readme.md @@ -73,3 +73,12 @@ The library will be built in build/lib. # Building aplications with allolib There are two options provided to build allolib applications. The first is [allolib_playground](https://github.com/AlloSphere-Research-Group/allolib_playground) that is great for prototyping single file applications and for exploring the examples. For more complex projects with multiple source files and dependencies, use [allotemplate](https://github.com/AlloSphere-Research-Group/allotemplate) + +# Extensions + +Allolib provides an extension mechanism for libraries that have large or +platform dependent dependencies. The stable set of extensions can be found in +the [al_ext repo](https://github.com/AlloSphere-Research-Group/al_ext). +The allotemplate repo linked above shows how to integrate them. al_ext +is a separate repo that depends on the allolib library and provides cmake +facilities for easy integration with it. diff --git a/src/app/al_App.cpp b/src/app/al_App.cpp index 00144c22..1ca30b73 100644 --- a/src/app/al_App.cpp +++ b/src/app/al_App.cpp @@ -216,6 +216,8 @@ void App::start() { std::bind(&App::onMouseMove, this, std::placeholders::_1); mDefaultWindowDomain->window().onMouseScroll = std::bind(&App::onMouseScroll, this, std::placeholders::_1); + mDefaultWindowDomain->window().onResize = std::bind( + &App::onResize, this, std::placeholders::_1, std::placeholders::_2); mDefaultWindowDomain->window().append(stdControls); stdControls.app = this; stdControls.mWindow = &mDefaultWindowDomain->window(); diff --git a/src/app/al_AudioDomain.cpp b/src/app/al_AudioDomain.cpp index f6c72456..711dc25e 100644 --- a/src/app/al_AudioDomain.cpp +++ b/src/app/al_AudioDomain.cpp @@ -2,9 +2,16 @@ using namespace al; +AudioDomain::AudioDomain() { + mParameters = {&mGainParameter}; + + mGainParameter.registerChangeCallback( + [&](float value) { mAudioIO.gain(value); }); +} + bool AudioDomain::initialize(ComputationDomain *parent) { + (void)parent; bool ret = true; - callInitializeCallbacks(); return ret; } diff --git a/src/app/al_DistributedApp.cpp b/src/app/al_DistributedApp.cpp index 195ab1f5..94376ba7 100644 --- a/src/app/al_DistributedApp.cpp +++ b/src/app/al_DistributedApp.cpp @@ -1,5 +1,12 @@ #include "al/app/al_DistributedApp.hpp" +#include "al/sphere/al_SphereUtils.hpp" + +#ifdef AL_WINDOWS +//#include +#include +#endif + using namespace al; DistributedApp::DistributedApp() : App() {} @@ -18,95 +25,84 @@ void DistributedApp::initialize() { #endif TomlLoader appConfig("distributed_app.toml"); auto nodesTable = appConfig.root->get_table_array("node"); + std::vector mListeners; // First get role from config file if (nodesTable) { for (const auto &table : *nodesTable) { std::string host = *table->get_as("host"); std::string role = *table->get_as("role"); - if (strncmp(name().c_str(), host.c_str(), name().size()) == 0) { + if (name() == host) { // Now set capabilities from role - if (role == "desktop") { - mCapabilites = (Capability)(CAP_SIMULATOR | CAP_RENDERING | - CAP_AUDIO_IO | CAP_OSC); - } else if (role == "renderer") { - mCapabilites = - (Capability)(CAP_SIMULATOR | CAP_OMNIRENDERING | CAP_OSC); - } else if (role == "audio") { - mCapabilites = (Capability)(CAP_SIMULATOR | CAP_AUDIO_IO | - CAP_CONSOLE_IO | CAP_OSC); - } else if (role == "simulator") { - mCapabilites = (Capability)(CAP_SIMULATOR | CAP_CONSOLE_IO | CAP_OSC); - } else if (role == "replica") { - mCapabilites = (Capability)(CAP_SIMULATOR | CAP_OMNIRENDERING | - CAP_AUDIO_IO | CAP_OSC); - } else if (role == "control") { - mCapabilites = (Capability)(CAP_RENDERING | CAP_OSC); - } else { - std::cerr << "WARNING: Setting no capabilities for this app from " - "config file" - << std::endl; - } + setRole(role); } mRoleMap[host] = role; - if (table->contains("dataRoot") && - strncmp(name().c_str(), host.c_str(), name().size()) == - 0) { // Set configuration for this node when found - std::string dataRootValue = *table->get_as("dataRoot"); - mGlobalDataRootPath = File::conformPathToOS(dataRootValue); + if (table->contains("dataRoot")) { + if (name() == host) { // Set configuration for this node when found + std::string dataRootValue = *table->get_as("dataRoot"); + dataRoot = File::conformPathToOS(dataRootValue); + } } else { std::cout << "WARNING: node " << host.c_str() << " not given dataRoot" << std::endl; } if (table->contains("rank")) { - rank = *table->get_as("rank"); + if (name() == host) { // Set configuration for this node when found + rank = *table->get_as("rank"); + } } else { std::cout << "WARNING: node " << host.c_str() << " not given rank" << std::endl; } if (table->contains("group")) { - group = *table->get_as("group"); + if (name() == host) { // Set configuration for this node when found + group = *table->get_as("group"); + } } else { std::cout << "WARNING: node " << host.c_str() << " not given group" << std::endl; } } } else { // No nodes table in config file. Use desktop role - mCapabilites = - (Capability)(CAP_SIMULATOR | CAP_RENDERING | CAP_AUDIO_IO | CAP_OSC); - group = 0; - } - - // if (mRunDistributed) { - // for (auto entry: mRoleMap) { - // if (strncmp(name().c_str(), entry.first.c_str(), - // name().size()) == 0) { - // mRole = entry.second; - //// std::cout << name() << ":Running distributed as " << - /// roleName() << std::endl; - // } - // } - - // } + + auto defaultCapabilities = al::sphere::getSphereNodes(); + if (defaultCapabilities.find(name()) != defaultCapabilities.end()) { + mCapabilites = defaultCapabilities[name()].mCapabilites; + group = defaultCapabilities[name()].group; + rank = defaultCapabilities[name()].rank; + } else { + mCapabilites = + (Capability)(CAP_SIMULATOR | CAP_RENDERING | CAP_AUDIO_IO | CAP_OSC); + group = 0; + } + } + if (hasCapability(CAP_SIMULATOR)) { - TomlLoader configLoader; - configLoader.setFile("distributed_app.toml"); - configLoader.setDefaultValue("broadcastAddress", - std::string("192.168.0.255")); - configLoader.writeFile(); + if (al::sphere::isSphereMachine()) { + appConfig.setDefaultValue("broadcastAddress", + std::string("192.168.10.255")); + appConfig.writeFile(); + } + } + if (appConfig.hasKey("broadcastAddress")) { + additionalConfig["broadcastAddress"] = appConfig.gets("broadcastAddress"); } osc::Recv testServer; // probe to check if first port available, this will determine if this // application is the primary or the replica if (!testServer.open(mOSCDomain->port, mOSCDomain->interfaceIP.c_str())) { + // If port taken, run this instance as a renderer mCapabilites = (Capability)(CAP_SIMULATOR | CAP_OMNIRENDERING | CAP_OSC); - rank = 1; + rank = 99; std::cout << "Replica: " << name() << ":Running distributed" << std::endl; - } else { + } else if (rank == 0) { testServer.stop(); std::cout << "Primary: " << name() << ":Running distributed" << std::endl; + } else { + testServer.stop(); + std::cout << "Secondary: rank " << rank << std::endl; } if (hasCapability(CAP_AUDIO_IO)) { @@ -181,9 +177,12 @@ void DistributedApp::start() { std::bind(&App::onMouseScroll, this, std::placeholders::_1); } - if (!isPrimary()) { - mSimulationDomain - ->disableProcessingCallback(); // Replicas won't call onAnimate() + if (isPrimary()) { + for (auto hostRole : mRoleMap) { + if (hostRole.first != name()) { + parameterServer().addListener(hostRole.first, oscDomain()->port); + } + } } onInit(); @@ -227,3 +226,45 @@ void al::DistributedApp::registerDynamicScene(DynamicScene &scene) { scene.prepare(audioIO()); } + +Graphics &DistributedApp::graphics() { + if (hasCapability(CAP_OMNIRENDERING)) { + return omniRendering->graphics(); + } else { + return mDefaultWindowDomain->graphics(); + } +} + +Window &DistributedApp::defaultWindow() { + if (hasCapability(CAP_OMNIRENDERING)) { + return omniRendering->window(); + } else { + return mDefaultWindowDomain->window(); + } +} + +Viewpoint &DistributedApp::view() { + if (hasCapability(CAP_OMNIRENDERING)) { + return omniRendering->view(); + } else { + return mDefaultWindowDomain->view(); + } +} + +Pose &DistributedApp::pose() { + if (hasCapability(CAP_OMNIRENDERING)) { + return omniRendering->nav(); + } else { + return mDefaultWindowDomain->nav(); + } +} + +Lens &DistributedApp::lens() { return view().lens(); } + +Nav &DistributedApp::nav() { + if (hasCapability(CAP_OMNIRENDERING)) { + return omniRendering->nav(); + } else { + return mDefaultWindowDomain->nav(); + } +} diff --git a/src/app/al_OmniRendererDomain.cpp b/src/app/al_OmniRendererDomain.cpp index a2cfd8bf..b336ba82 100644 --- a/src/app/al_OmniRendererDomain.cpp +++ b/src/app/al_OmniRendererDomain.cpp @@ -26,13 +26,16 @@ bool GLFWOpenGLOmniRendererDomain::initialize(al::ComputationDomain *parent) { mGraphics = std::make_unique(); } if (!mWindow->created()) { + if (render_stereo) { + mWindow->displayMode(Window::DisplayMode::STEREO_BUF); + } bool ret = mWindow->create(); if (ret) { mGraphics->init(); } } - if (sphere::is_renderer()) { + if (sphere::isRendererMachine()) { spanAllDesktop(); loadPerProjectionConfiguration(false); running_in_sphere_renderer = true; @@ -84,7 +87,7 @@ void GLFWOpenGLOmniRendererDomain::setEyeToRenderForDesktopMode(int eye) { void GLFWOpenGLOmniRendererDomain::spanAllDesktop() { int width, height; - sphere::get_fullscreen_dimension(&width, &height); + sphere::getFullscreenDimension(&width, &height); if (width != 0 && height != 0) { mWindow->dimensions(0, 0, width, height); mWindow->decorated(false); @@ -99,8 +102,8 @@ void GLFWOpenGLOmniRendererDomain::loadPerProjectionConfiguration( if (!desktop) { // need to be called before pp_render.init pp_render.load_calibration_data( - sphere::config_directory("data").c_str(), // path - sphere::renderer_hostname("config").c_str() // hostname + sphere::getCalibrationDirectory("data").c_str(), // path + sphere::renderer_hostname("config").c_str() // hostname ); // parameters will be used to look for file ${path}/${hostname}.txt pp_render.init(mGraphics->lens()); } else { diff --git a/src/app/al_SimulationDomain.cpp b/src/app/al_SimulationDomain.cpp index 60333aba..69343068 100644 --- a/src/app/al_SimulationDomain.cpp +++ b/src/app/al_SimulationDomain.cpp @@ -1 +1,14 @@ #include "al/app/al_SimulationDomain.hpp" + +using namespace al; + +bool SimulationDomain::tick() { + bool ret = tickSubdomains(true); + if (mUseCallback) { + simulationFunction(timeDelta()); + } + ret &= tickSubdomains(false); + return true; +} + +void SimulationDomain::disableProcessingCallback() { mUseCallback = false; } diff --git a/src/app/al_WindowApp.cpp b/src/app/al_WindowApp.cpp deleted file mode 100644 index 8fa403f6..00000000 --- a/src/app/al_WindowApp.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "al/app/al_WindowApp.hpp" - -using namespace al; - -bool WindowApp::StandardWindowAppKeyControls::keyDown(const Keyboard& k) { - if (k.ctrl()) { - switch (k.key()) { - case 'q': - window().close(); - return false; - // case 'h': window().hide(); return false; - // case 'm': window().iconify(); return false; - case 'u': - window().cursorHideToggle(); - return false; - default:; - } - } else { - switch (k.key()) { - case Keyboard::ESCAPE: - window().fullScreenToggle(); - return false; - default:; - } - } - return true; -} - -WindowApp::WindowApp() { - append(stdControls); - append(windowEventHandler()); -} - -void WindowApp::start() { - initializeWindowManager(); - onInit(); - Window::create(is_verbose); - onCreate(); - FPS::startFPS(); - while (!shouldQuit()) { - onAnimate(dt_sec()); - onDraw(mGraphics); - Window::refresh(); - FPS::tickFPS(); - } - onExit(); - Window::destroy(); // destroy window - terminateWindowManager(); -} - -bool WindowApp::resize(int dw, int dh) { - onResize(dw, dh); - return true; -} - -bool WindowApp::visibility(bool v) { - onVisibility(v); - return true; -} diff --git a/src/graphics/al_Image.cpp b/src/graphics/al_Image.cpp index 73c54a0f..c6774ba1 100644 --- a/src/graphics/al_Image.cpp +++ b/src/graphics/al_Image.cpp @@ -1,5 +1,7 @@ #include "al/graphics/al_Image.hpp" + #include + #include "al_stb_image.hpp" namespace al { @@ -24,6 +26,8 @@ bool Image::load(const std::string &filename) { if (!image_data.data) return false; int arr_size = 4 * image_data.width * image_data.height; + mWidth = image_data.width; + mHeight = image_data.height; mArray.resize(arr_size); static_assert( sizeof(unsigned char) == sizeof(uint8_t), diff --git a/src/graphics/al_OpenGL.cpp b/src/graphics/al_OpenGL.cpp index 404110ba..825ba666 100644 --- a/src/graphics/al_OpenGL.cpp +++ b/src/graphics/al_OpenGL.cpp @@ -101,91 +101,97 @@ int al::gl::numBytes(GLenum v) { #undef CS } +namespace al { +namespace gl { + template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_BYTE; } template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_UNSIGNED_BYTE; } template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_SHORT; } template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_UNSIGNED_SHORT; } template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_INT; } template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_UNSIGNED_INT; } template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_FLOAT; } template <> -GLenum al::gl::toDataType() { +GLenum toDataType() { return GL_DOUBLE; } -void al::gl::blending(bool doBlend) { +void blending(bool doBlend) { if (doBlend) glEnable(GL_BLEND); else glDisable(GL_BLEND); } -void al::gl::depthMask(bool maskDepth) { +void depthMask(bool maskDepth) { glDepthMask(maskDepth ? GL_TRUE : GL_FALSE); } -void al::gl::depthTest(bool testDepth) { +void depthTest(bool testDepth) { if (testDepth) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); } -void al::gl::viewport(int left, int bottom, int width, int height) { +void viewport(int left, int bottom, int width, int height) { glViewport(left, bottom, width, height); } -void al::gl::scissorTest(bool testScissor) { +void scissorTest(bool testScissor) { if (testScissor) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); } -void al::gl::scissorArea(int left, int bottom, int width, int height) { +void scissorArea(int left, int bottom, int width, int height) { glScissor(left, bottom, width, height); } -void al::gl::faceCulling(bool doCulling) { +void faceCulling(bool doCulling) { if (doCulling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); } -void al::gl::faceToCull(unsigned int face) { glCullFace(face); } -void al::gl::pointSize(float size) { glPointSize(size); } -void al::gl::polygonMode(unsigned int mode) { +void faceToCull(unsigned int face) { glCullFace(face); } +void pointSize(float size) { glPointSize(size); } +void polygonMode(unsigned int mode) { glPolygonMode(GL_FRONT_AND_BACK, mode); } -void al::gl::blendMode(unsigned int src, unsigned int dst, unsigned int eq) { +void blendMode(unsigned int src, unsigned int dst, unsigned int eq) { glBlendEquation(eq); glBlendFunc(src, dst); } -void al::gl::clearColor(float r, float g, float b, float a) { +void clearColor(float r, float g, float b, float a) { float c[] = {r, g, b, a}; glClearBufferfv(GL_COLOR, 0, c); } -void al::gl::clearDepth(float d) { +void clearDepth(float d) { float depth = d; glClearBufferfv(GL_DEPTH, 0, &depth); } -void al::gl::clearBuffer(int buffer, float r, float g, float b, float a) { +void clearBuffer(int buffer, float r, float g, float b, float a) { float c[] = {r, g, b, a}; glClearBufferfv(GL_COLOR, buffer, c); } -void al::gl::bufferToDraw(unsigned int buffer) { glDrawBuffer(buffer); } +void bufferToDraw(unsigned int buffer) { glDrawBuffer(buffer); } + +} +} diff --git a/src/io/al_AudioIO.cpp b/src/io/al_AudioIO.cpp index 3a84e2aa..0991c15a 100644 --- a/src/io/al_AudioIO.cpp +++ b/src/io/al_AudioIO.cpp @@ -1,3 +1,5 @@ +#include "al/io/al_AudioIO.hpp" + #include #include #include @@ -9,8 +11,6 @@ #include #include -#include "al/io/al_AudioIO.hpp" - #ifdef AL_AUDIO_RTAUDIO #include "RtAudio.h" #endif @@ -577,7 +577,7 @@ static int rtaudioCallback(void *output, void *input, unsigned int frameCount, double streamTime, RtAudioStreamStatus status, void *userData); -bool AudioBackend::open(int framesPerSecond, int framesPerBuffer, +bool AudioBackend::open(int framesPerSecond, unsigned int framesPerBuffer, void *userdata) { assert(framesPerBuffer != 0 && framesPerSecond != 0 && userdata != NULL); // Set the same number of channels for both input and output. @@ -595,7 +595,7 @@ bool AudioBackend::open(int framesPerSecond, int framesPerBuffer, return false; } - if (deviceBufferSize != static_cast(framesPerBuffer)) { + if (deviceBufferSize != framesPerBuffer) { printf("WARNING: Device opened with buffer size: %d", deviceBufferSize); } return true; @@ -635,9 +635,8 @@ bool AudioBackend::stop() { try { data->audio.stopStream(); } catch (RtAudioError &e) { + e.printMessage(); return false; - // e.printMessage(); - // goto cleanup; } return true; } diff --git a/src/io/al_AudioIOData.cpp b/src/io/al_AudioIOData.cpp index 4c263644..9f038110 100644 --- a/src/io/al_AudioIOData.cpp +++ b/src/io/al_AudioIOData.cpp @@ -1,3 +1,5 @@ +#include "al/io/al_AudioIOData.hpp" + #include #include #include @@ -5,8 +7,6 @@ #include #include /* memset() */ -#include "al/io/al_AudioIOData.hpp" - namespace al { //============================================================================== @@ -121,7 +121,7 @@ unsigned int AudioIOData::channelsOut() const { return mNumO; } unsigned int AudioIOData::channelsBus() const { return mNumB; } double AudioIOData::framesPerSecond() const { return mFramesPerSecond; } -unsigned int AudioIOData::framesPerBuffer() const { return mFramesPerBuffer; } +uint64_t AudioIOData::framesPerBuffer() const { return mFramesPerBuffer; } double AudioIOData::secondsPerBuffer() const { return (double)framesPerBuffer() / framesPerSecond(); } diff --git a/src/io/al_CSVReader.cpp b/src/io/al_CSVReader.cpp index 8f2e6a7c..ba465217 100644 --- a/src/io/al_CSVReader.cpp +++ b/src/io/al_CSVReader.cpp @@ -1,8 +1,8 @@ +#include "al/io/al_CSVReader.hpp" + #include #include -#include "al/io/al_CSVReader.hpp" - using namespace al; CSVReader::~CSVReader() { @@ -76,7 +76,7 @@ bool CSVReader::readFile(std::string fileName, bool hasColumnNames) { if (commaSeparated) { if ((unsigned long)std::count(line.begin(), line.end(), ',') == mDataTypes.size() - 1) { // Check that we have enough commas - int byteCount = 0; + size_t byteCount = 0; for (auto type : mDataTypes) { std::string field; std::getline(ss, field, ','); diff --git a/src/io/al_File.cpp b/src/io/al_File.cpp index f2305045..1cd9403b 100644 --- a/src/io/al_File.cpp +++ b/src/io/al_File.cpp @@ -1,8 +1,9 @@ #include "al/io/al_File.hpp" -#include "minFileSys.hpp" #include // realpath (POSIX), _fullpath (Windows) +#include "minFileSys.hpp" + #ifdef AL_WINDOWS #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN @@ -235,6 +236,14 @@ std::string File::absolutePath(const std::string& path) { #endif } +std::string File::currentPath() { + // Can be removed once moved to C++17 std::file_system::current_path() + char buf[FILENAME_MAX]; + platform_getcwd(buf, FILENAME_MAX); + std::string currentDir(buf); + return File::conformPathToOS(currentDir); +} + std::string File::baseName(const std::string& path, const std::string& suffix) { auto posSlash = path.find_last_of("/\\"); // handle '/' or '\' path delimiters diff --git a/src/io/al_MIDI.cpp b/src/io/al_MIDI.cpp index 463fb455..a50b5a61 100644 --- a/src/io/al_MIDI.cpp +++ b/src/io/al_MIDI.cpp @@ -105,7 +105,7 @@ void MIDIMessage::print(std::ostream& stream) const { stream << ", time " << timeStamp() << std::endl; } -void MIDIMessageHandler::bindTo(RtMidiIn& midiIn, unsigned port) { +void MIDIMessageHandler::bindTo(RtMidiIn& RtMidiIn, unsigned port) { struct F { static void callback(double t, std::vector* msgPtr, void* user) { @@ -130,8 +130,8 @@ void MIDIMessageHandler::bindTo(RtMidiIn& midiIn, unsigned port) { } }; - Binding b = {&midiIn, this, port}; + Binding b = {&RtMidiIn, this, port}; mBindings.push_back(b); - midiIn.setCallback(F::callback, &mBindings.back()); + RtMidiIn.setCallback(F::callback, &mBindings.back()); } diff --git a/src/io/al_Window.cpp b/src/io/al_Window.cpp index b015db97..2bcd26e3 100644 --- a/src/io/al_Window.cpp +++ b/src/io/al_Window.cpp @@ -1,4 +1,5 @@ #include "al/io/al_Window.hpp" + #include namespace al { @@ -68,11 +69,11 @@ void Mouse::scroll(double x, double y) { mScrollY = y; } -WindowEventHandler::WindowEventHandler() : mWindow(NULL) {} +WindowEventHandler::WindowEventHandler() : mWindow(nullptr) {} WindowEventHandler::~WindowEventHandler() { removeFromWindow(); } void WindowEventHandler::removeFromWindow() { if (attached()) { - window().remove(this); + window().remove(*this); mWindow = nullptr; } } @@ -181,10 +182,10 @@ void Window::displayMode(DisplayMode v) { } } -void Window::fullScreen(bool v) { +void Window::fullScreen(bool v, int monitorIndex) { if (v != mFullScreen) { mFullScreen = v; - if (created()) implSetFullScreen(); + if (created()) implSetFullScreen(monitorIndex); } } diff --git a/src/io/al_WindowGLFW.cpp b/src/io/al_WindowGLFW.cpp index 7bf045c4..117ccd01 100644 --- a/src/io/al_WindowGLFW.cpp +++ b/src/io/al_WindowGLFW.cpp @@ -1,8 +1,5 @@ #define GLFW_INCLUDE_NONE #include -#include "al/graphics/al_OpenGL.hpp" - -#include "al/io/al_Window.hpp" #include #include // exit, EXIT_FAILURE @@ -11,6 +8,9 @@ #include #include +#include "al/graphics/al_OpenGL.hpp" +#include "al/io/al_Window.hpp" + using namespace std; static void cbError(int code, const char* description) { @@ -226,6 +226,7 @@ class WindowImpl { glfwSetKeyCallback(mGLFWwindow, cbKeyboard); glfwSetMouseButtonCallback(mGLFWwindow, cbMouse); glfwSetCursorPosCallback(mGLFWwindow, cbMotion); + glfwSetWindowSizeCallback(mGLFWwindow, cbReshape); glfwSetScrollCallback(mGLFWwindow, cbScroll); // glfwSetWindowCloseCallback(mGLFWwindow, cbClose); // glfwSetWindowRefreshCallback(window, cb_windowrefresh); @@ -402,13 +403,19 @@ void Window::implSetDimensions() { } } -void Window::implSetFullScreen() { +void Window::implSetFullScreen(int monitorIndex) { if (mFullScreen) { // TODO: selection for multi-monitor - - GLFWmonitor* primary = glfwGetPrimaryMonitor(); - const GLFWvidmode* mode = glfwGetVideoMode(primary); - glfwSetWindowMonitor(mImpl->mGLFWwindow, primary, 0, 0, mode->width, + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor* monitor; + if (monitorCount > monitorIndex && monitorIndex >= 0) { + monitor = monitors[monitorIndex]; + } else { + monitor = glfwGetPrimaryMonitor(); + } + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(mImpl->mGLFWwindow, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); vsync(mVSync); } else { diff --git a/src/io/minFileSys.hpp b/src/io/minFileSys.hpp index 99ee2b5c..3d7bfcd8 100644 --- a/src/io/minFileSys.hpp +++ b/src/io/minFileSys.hpp @@ -3,15 +3,15 @@ /* minimal and basic filesystem functions - + - for reading / writing file, use std::*stream - does not support unicode keehong youn, 2017, younkeehong@gmail.com */ -#include #include +#include namespace minFileSys { void readDir(const std::string& name, std::vector& v); @@ -21,11 +21,11 @@ bool isDirEmpty(std::string const& dir_path); void copyFile(std::string const& orig, std::string const& cpyd); bool deleteFile(std::string const& f); bool createDir(std::string const& dir_path); -bool deleteDir(std::string const& dir_path); // only for empty dir -bool deleteDirRecursively(std::string const& dir_path); // delete non-empty dir +bool deleteDir(std::string const& dir_path); // only for empty dir +bool deleteDirRecursively(std::string const& dir_path); // delete non-empty dir std::string addPath(std::string parent, std::string const& child); -} // namespace minFileSys - +std::string currentPath(); +} // namespace minFileSys // IMPLEMENTATION -------------------------------------------------------------- @@ -46,393 +46,385 @@ std::string addPath(std::string parent, std::string const& child); #include /* // for unicode -#include #include +#include std::wstring_convert> converter; */ #endif #ifdef ITS_POSIX -#include #include -#include // std::remove -#include // int stat(const char*, struct stat*), mkdir -#include // rmdir #include +#include // int stat(const char*, struct stat*), mkdir +#include +#include // rmdir + +#include // std::remove #endif +#include // std::strcmp #include #include -#include // std::strcmp namespace minFileSys { -inline std::string addPath(std::string parent, std::string const& child) -{ +inline std::string addPath(std::string parent, std::string const& child) { #ifdef ITS_WINDOWS - if (parent[parent.size() - 1] != '\\') parent += "\\"; - parent += child; - return parent; + if (parent[parent.size() - 1] != '\\') parent += "\\"; + parent += child; + return parent; #endif #ifdef ITS_POSIX - if (parent[parent.size() - 1] != '/') parent += "/"; - parent += child; - return parent; + if (parent[parent.size() - 1] != '/') parent += "/"; + parent += child; + return parent; #endif } // http://www.martinbroadhurst.com/list-the-files-in-a-directory-in-c.html -inline void readDir(const std::string& name, std::vector& v) -{ - if (!pathExists(name)) { - std::cout << "[!] [readDir] " << name << " does not exist" << std::endl; - return; - } +inline void readDir(const std::string& name, std::vector& v) { + if (!pathExists(name)) { + std::cout << "[!] [readDir] " << name << " does not exist" << std::endl; + return; + } #ifdef ITS_WINDOWS - std::string pattern(name); - pattern.append("\\*"); - - // unicode - // std::wstring wide = converter.from_bytes(pattern); - - WIN32_FIND_DATAA data; - HANDLE hFind; - - // unicode - // if ((hFind = FindFirstFile(wide.c_str(), &data)) != INVALID_HANDLE_VALUE) - - if ((hFind = FindFirstFileA(pattern.c_str(), &data)) != INVALID_HANDLE_VALUE) - { - do { - if (std::strcmp(data.cFileName, ".") == 0) continue; - if (std::strcmp(data.cFileName, "..") == 0) continue; - // unicode - // v.push_back(converter.to_bytes(data.cFileName)); - - v.push_back(data.cFileName); - } - while (FindNextFileA(hFind, &data) != 0); - FindClose(hFind); - } + std::string pattern(name); + pattern.append("\\*"); + + // unicode + // std::wstring wide = converter.from_bytes(pattern); + + WIN32_FIND_DATAA data; + HANDLE hFind; + + // unicode + // if ((hFind = FindFirstFile(wide.c_str(), &data)) != INVALID_HANDLE_VALUE) + + if ((hFind = FindFirstFileA(pattern.c_str(), &data)) != + INVALID_HANDLE_VALUE) { + do { + if (std::strcmp(data.cFileName, ".") == 0) continue; + if (std::strcmp(data.cFileName, "..") == 0) continue; + // unicode + // v.push_back(converter.to_bytes(data.cFileName)); + + v.push_back(data.cFileName); + } while (FindNextFileA(hFind, &data) != 0); + FindClose(hFind); + } #endif #ifdef ITS_POSIX - DIR* dirp = opendir(name.c_str()); - struct dirent * dp; - while ((dp = readdir(dirp)) != NULL) { - if (std::strcmp(dp->d_name, ".") == 0) continue; - if (std::strcmp(dp->d_name, "..") == 0) continue; - v.push_back(dp->d_name); - } - closedir(dirp); + DIR* dirp = opendir(name.c_str()); + struct dirent* dp; + while ((dp = readdir(dirp)) != NULL) { + if (std::strcmp(dp->d_name, ".") == 0) continue; + if (std::strcmp(dp->d_name, "..") == 0) continue; + v.push_back(dp->d_name); + } + closedir(dirp); #endif } -inline bool pathExists(std::string const& path) -{ +inline bool pathExists(std::string const& path) { #ifdef ITS_WINDOWS - // unicode - // return !(INVALID_FILE_ATTRIBUTES == GetFileAttributes(path.c_str())); + // unicode + // return !(INVALID_FILE_ATTRIBUTES == GetFileAttributes(path.c_str())); - return !(INVALID_FILE_ATTRIBUTES == GetFileAttributesA(path.c_str())); + return !(INVALID_FILE_ATTRIBUTES == GetFileAttributesA(path.c_str())); #endif #ifdef ITS_POSIX - struct stat result; - return (stat(path.c_str(), &result) == 0); + struct stat result; + return (stat(path.c_str(), &result) == 0); #endif } -inline bool isPathDir(std::string const& path) -{ +inline bool isPathDir(std::string const& path) { #ifdef ITS_WINDOWS - // unicode - // auto attr = GetFileAttributes(path.c_str()); + // unicode + // auto attr = GetFileAttributes(path.c_str()); - auto attr = GetFileAttributesA(path.c_str()); - if (attr == INVALID_FILE_ATTRIBUTES) return false; - return attr & FILE_ATTRIBUTE_DIRECTORY; + auto attr = GetFileAttributesA(path.c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) return false; + return attr & FILE_ATTRIBUTE_DIRECTORY; #endif #ifdef ITS_POSIX - struct stat result; - if (stat(path.c_str(), &result) != 0) return false; - return S_ISDIR(result.st_mode); + struct stat result; + if (stat(path.c_str(), &result) != 0) return false; + return S_ISDIR(result.st_mode); #endif } -inline bool isDirEmpty(std::string const& dir_path) -{ +inline bool isDirEmpty(std::string const& dir_path) { #ifdef ITS_WINDOWS - std::string pattern(dir_path); - pattern.append("\\*"); + std::string pattern(dir_path); + pattern.append("\\*"); - // unicode - // std::wstring wide = converter.from_bytes(pattern); + // unicode + // std::wstring wide = converter.from_bytes(pattern); - WIN32_FIND_DATAA data; - HANDLE hFind; + WIN32_FIND_DATAA data; + HANDLE hFind; - // unicode - // if ((hFind = FindFirstFile(wide.c_str(), &data)) != INVALID_HANDLE_VALUE) + // unicode + // if ((hFind = FindFirstFile(wide.c_str(), &data)) != INVALID_HANDLE_VALUE) - if ((hFind = FindFirstFileA(pattern.c_str(), &data)) != INVALID_HANDLE_VALUE) - { - do { - if (std::strcmp(data.cFileName, ".") == 0) continue; - if (std::strcmp(data.cFileName, "..") == 0) continue; - FindClose(hFind); - return false; - } - while (FindNextFileA(hFind, &data) != 0); - FindClose(hFind); - return true; - } + if ((hFind = FindFirstFileA(pattern.c_str(), &data)) != + INVALID_HANDLE_VALUE) { + do { + if (std::strcmp(data.cFileName, ".") == 0) continue; + if (std::strcmp(data.cFileName, "..") == 0) continue; + FindClose(hFind); + return false; + } while (FindNextFileA(hFind, &data) != 0); + FindClose(hFind); + return true; + } #endif #ifdef ITS_POSIX - DIR* dirp = opendir(dir_path.c_str()); - struct dirent * dp; - while ((dp = readdir(dirp)) != NULL) { - if (std::strcmp(dp->d_name, ".") == 0) continue; - if (std::strcmp(dp->d_name, "..") == 0) continue; - closedir(dirp); - return false; - } + DIR* dirp = opendir(dir_path.c_str()); + struct dirent* dp; + while ((dp = readdir(dirp)) != NULL) { + if (std::strcmp(dp->d_name, ".") == 0) continue; + if (std::strcmp(dp->d_name, "..") == 0) continue; closedir(dirp); - return true; + return false; + } + closedir(dirp); + return true; #endif } -inline void copyFile(std::string const& orig, std::string const& cpyd) -{ - std::ifstream origStream{ orig, std::ios::binary }; - std::ofstream cpydStream{ cpyd, std::ios::binary }; - cpydStream << origStream.rdbuf(); - origStream.close(); - cpydStream.close(); +inline void copyFile(std::string const& orig, std::string const& cpyd) { + std::ifstream origStream{orig, std::ios::binary}; + std::ofstream cpydStream{cpyd, std::ios::binary}; + cpydStream << origStream.rdbuf(); + origStream.close(); + cpydStream.close(); } -inline bool deleteFile(std::string const& f) -{ +inline bool deleteFile(std::string const& f) { #ifdef ITS_WINDOWS - // unicode - // DeleteFile(f.c_str()); + // unicode + // DeleteFile(f.c_str()); - // If the function succeeds, the return value is nonzero. - auto result = DeleteFileA(f.c_str()); - return !(result == 0); + // If the function succeeds, the return value is nonzero. + auto result = DeleteFileA(f.c_str()); + return !(result == 0); #endif #ifdef ITS_POSIX - // 0​ upon success or non-zero value on error. - auto result = std::remove(f.c_str()); - return (result == 0); + // 0​ upon success or non-zero value on error. + auto result = std::remove(f.c_str()); + return (result == 0); #endif } -inline bool createDir(std::string const& path) -{ - if (pathExists(path)) { - std::cout << "[!] [createDir] " << path << " already exists" << std::endl; - return true; - } - if (path.size() == 0) { - return false; - } +inline bool createDir(std::string const& path) { + if (pathExists(path)) { + std::cout << "[!] [createDir] " << path << " already exists" << std::endl; + return true; + } + if (path.size() == 0) { + return false; + } #ifdef ITS_WINDOWS - // Create all intermediate dirs up to last one - for(unsigned i=0; i files; - readDir(dir_path, files); - for (auto const& f : files) { - auto added_path = addPath(dir_path, f); - std::cout << added_path << std::endl; - if (isPathDir(added_path)) { - if (!deleteDirRecursively(added_path)) return false; - } - else { - if (!deleteFile(added_path)) return false; - } + std::vector files; + readDir(dir_path, files); + for (auto const& f : files) { + auto added_path = addPath(dir_path, f); + std::cout << added_path << std::endl; + if (isPathDir(added_path)) { + if (!deleteDirRecursively(added_path)) return false; + } else { + if (!deleteFile(added_path)) return false; } - return deleteDir(dir_path); + } + return deleteDir(dir_path); #endif } - -} // namespace minFileSys +} // namespace minFileSys #if defined(ITS_WINDOWS) #undef ITS_WINDOWS @@ -454,21 +446,22 @@ inline bool deleteDirRecursively(std::string const& dir_path) The mkdir() function shall fail if: [EACCES] Search permission is denied on a component of the path prefix, - or write permission is denied on the parent directory of the directory to be created. - [EEXIST] The named file exists. - [ELOOP] A loop exists in symbolic links encountered during resolution of the path argument. + or write permission is denied on the parent directory of the + directory to be created. [EEXIST] The named file exists. [ELOOP] A loop + exists in symbolic links encountered during resolution of the path argument. [EMLINK] The link count of the parent directory would exceed {LINK_MAX}. [ENAMETOOLONG] The length of the path argument exceeds {PATH_MAX} or a pathname component is longer than {NAME_MAX}. [ENOENT] A component of the path prefix specified by path does not name an existing directory or path is an empty string. [ENOSPC] The file system does not contain enough space to hold - the contents of the new directory or to extend the parent directory of the new directory. - [ENOTDIR] A component of the path prefix is not a directory. - [EROFS] The parent directory resides on a read-only file system. + the contents of the new directory or to extend the parent directory + of the new directory. [ENOTDIR] A component of the path prefix is not a + directory. [EROFS] The parent directory resides on a read-only file system. The mkdir() function may fail if: - [ELOOP] More than {SYMLOOP_MAX} symbolic links were encountered during resolution of the path argument. - [ENAMETOOLONG] As a result of encountering a symbolic link in resolution of - the path argument, the length of the substituted pathname string exceeded {PATH_MAX}. + [ELOOP] More than {SYMLOOP_MAX} symbolic links were encountered during + resolution of the path argument. [ENAMETOOLONG] As a result of encountering a + symbolic link in resolution of the path argument, the length of the + substituted pathname string exceeded {PATH_MAX}. */ diff --git a/src/protocol/al_OSC.cpp b/src/protocol/al_OSC.cpp index 651e8e14..b3bae29f 100644 --- a/src/protocol/al_OSC.cpp +++ b/src/protocol/al_OSC.cpp @@ -1,19 +1,19 @@ #include "al/protocol/al_OSC.hpp" + #include // isgraph #include // printf #include -#include "al/system/al_Printing.hpp" +#include + +#include "al/system/al_Printing.hpp" +#include "ip/UdpSocket.h" #include "osc/OscException.h" #include "osc/OscOutboundPacketStream.h" #include "osc/OscPacketListener.h" #include "osc/OscReceivedElements.h" #include "osc/OscTypes.h" -#include "ip/UdpSocket.h" - -#include - /* Summary of OSC 1.0 spec from http://opensoundcontrol.org @@ -75,14 +75,14 @@ namespace osc { class Packet::Impl : public ::osc::OutboundPacketStream { public: - Impl(char* buf, int size) : ::osc::OutboundPacketStream(buf, size) {} + Impl(char* buf, size_t size) : ::osc::OutboundPacketStream(buf, size) {} }; Packet::Packet(int size) : mImpl(0), mData(size) { OSCTRY("Packet::Packet", mImpl = new Impl(&mData[0], size);) } -Packet::Packet(const char* contents, int size) : mImpl(0), mData(size) { +Packet::Packet(const char* contents, size_t size) : mImpl(0), mData(size) { OSCTRY("Packet::Packet1", memcpy(&mData[0], contents, size); mImpl = new Impl(&mData[0], size);) } @@ -163,7 +163,7 @@ void Packet::printRaw() const { } } -int Packet::size() const { return mImpl->Size(); } +size_t Packet::size() const { return mImpl->Size(); } class Message::Impl : public ::osc::ReceivedMessage { public: @@ -198,49 +198,49 @@ Message::Message(const char* message, int size, const TimeTag& timeTag, Message::~Message() { OSCTRY("~Message()", delete mImpl;) } void Message::print() const { - OSCTRY("Message::print", - printf("%s, %s %" AL_PRINTF_LL "d from %s\n", addressPattern().c_str(), - typeTags().c_str(), timeTag(), mSenderAddr); - - ::osc::ReceivedMessageArgumentIterator it = mImpl->ArgumentsBegin(); - - printf("\targs = ("); - for (unsigned i = 0; i < typeTags().size(); ++i) { - char tag = typeTags()[i]; - switch (tag) { - case 'f': { - float v = it->AsFloat(); - printf("%g", v); - } break; - case 'i': { - long v = it->AsInt32(); - printf("%ld", v); - } break; - case 'h': { - long long v = it->AsInt64(); - printf("%" AL_PRINTF_LL "d", v); - } break; - case 'c': { - char v = it->AsChar(); - printf("'%c' (=%3d)", isprint(v) ? v : ' ', v); - } break; - case 'd': { - double v = it->AsDouble(); - printf("%g", v); - } break; - case 's': { - const char* v = it->AsString(); - printf("%s", v); - } break; - case 'b': - printf("blob"); - break; - default: - printf("?"); - } - if (i < (typeTags().size() - 1)) printf(", "); - ++it; - } printf(")\n");) + OSCTRY( + "Message::print", + printf("%s, %s %" AL_PRINTF_LL "d from %s\n", addressPattern().c_str(), + typeTags().c_str(), timeTag(), mSenderAddr); + + ::osc::ReceivedMessageArgumentIterator it = mImpl->ArgumentsBegin(); + + printf("\targs = ("); for (unsigned i = 0; i < typeTags().size(); ++i) { + char tag = typeTags()[i]; + switch (tag) { + case 'f': { + float v = it->AsFloat(); + printf("%g", v); + } break; + case 'i': { + long v = it->AsInt32(); + printf("%ld", v); + } break; + case 'h': { + long long v = it->AsInt64(); + printf("%" AL_PRINTF_LL "d", v); + } break; + case 'c': { + char v = it->AsChar(); + printf("'%c' (=%3d)", isprint(v) ? v : ' ', v); + } break; + case 'd': { + double v = it->AsDouble(); + printf("%g", v); + } break; + case 's': { + const char* v = it->AsString(); + printf("%s", v); + } break; + case 'b': + printf("blob"); + break; + default: + printf("?"); + } + if (i < (typeTags().size() - 1)) printf(", "); + ++it; + } printf(")\n");) } Message& Message::resetStream() { @@ -293,54 +293,55 @@ void PacketHandler::parse(const char* packet, int size, TimeTag timeTag, int i = 1; #endif - OSCTRY("PacketHandler::parse", - DPRINTF("PacketHandler::parse(size %d, packet %p)\n", size, packet); - DPRINTF("Data to parse: "); - for (int i = 0; i < size; - ++i) { DPRINTF("%c", packet[i]); } DPRINTF("\n"); - - // this is the only generic entry point for parsing packets - ::osc::ReceivedPacket p(packet, size); - - DPRINTF("Just made an ::osc::ReceivedPacket that has contents %p and " - "size %d\n", - p.Contents(), (int)p.Size()); - - // iterate through all the bundle elements (bundles or messages) - if (p.IsBundle()) { - DPRINTF("It's a bundle\n"); - // char *afterTimeTag = (char *)packet+16; // "#bundle\0" plus - // 8-byte time tag - DPRINTF("First bundle element has size %d\n", - ntohl(*((int*)(packet + 16)) /*firstBundleElementSize*/)); - - ::osc::ReceivedBundle r(p); - - // DPRINTF("Just made an ::osc::ReceivedBundle that has time tag at - // %p and %d elements\n", r.timeTag_, r.ElementCount() ); - - for (auto it = r.ElementsBegin(); it != r.ElementsEnd(); ++it) { - const ::osc::ReceivedBundleElement& e = *it; - - DPRINTF( - "Just made an ::osc::ReceivedBundleElement with contents %p " - "and size %d\n", - e.Contents(), (int)e.Size()); - DPRINTF("Parsing bundle element %d\n", i++); - DPRINTF( - "Made an ::osc::ReceivedBundleElement out of the iterator.\n"); - DPRINTF("\tcontents: %p\n", e.Contents()); - DPRINTF("\tsize: %d\n", (int)e.Size()); - DPRINTF("\ttimeTag %lu\n", (unsigned long)r.TimeTag()); - DPRINTF("\tLet's try to parse it...\n"); - - parse(e.Contents(), e.Size(), r.TimeTag(), senderAddr); - } - } else if (p.IsMessage()) { - DPRINTF("Parsing a message\n"); - Message m(packet, size, timeTag, senderAddr); - onMessage(m); - }) // OSCTRY + OSCTRY( + "PacketHandler::parse", + DPRINTF("PacketHandler::parse(size %d, packet %p)\n", size, packet); + DPRINTF("Data to parse: "); for (int i = 0; i < size; ++i) { + DPRINTF("%c", packet[i]); + } DPRINTF("\n"); + + // this is the only generic entry point for parsing packets + ::osc::ReceivedPacket p(packet, size); + + DPRINTF("Just made an ::osc::ReceivedPacket that has contents %p and " + "size %d\n", + p.Contents(), (int)p.Size()); + + // iterate through all the bundle elements (bundles or messages) + if (p.IsBundle()) { + DPRINTF("It's a bundle\n"); + // char *afterTimeTag = (char *)packet+16; // "#bundle\0" plus + // 8-byte time tag + DPRINTF("First bundle element has size %d\n", + ntohl(*((int*)(packet + 16)) /*firstBundleElementSize*/)); + + ::osc::ReceivedBundle r(p); + + // DPRINTF("Just made an ::osc::ReceivedBundle that has time tag at + // %p and %d elements\n", r.timeTag_, r.ElementCount() ); + + for (auto it = r.ElementsBegin(); it != r.ElementsEnd(); ++it) { + const ::osc::ReceivedBundleElement& e = *it; + + DPRINTF( + "Just made an ::osc::ReceivedBundleElement with contents %p " + "and size %d\n", + e.Contents(), (int)e.Size()); + DPRINTF("Parsing bundle element %d\n", i++); + DPRINTF( + "Made an ::osc::ReceivedBundleElement out of the iterator.\n"); + DPRINTF("\tcontents: %p\n", e.Contents()); + DPRINTF("\tsize: %d\n", (int)e.Size()); + DPRINTF("\ttimeTag %lu\n", (unsigned long)r.TimeTag()); + DPRINTF("\tLet's try to parse it...\n"); + + parse(e.Contents(), e.Size(), r.TimeTag(), senderAddr); + } + } else if (p.IsMessage()) { + DPRINTF("Parsing a message\n"); + Message m(packet, size, timeTag, senderAddr); + onMessage(m); + }) // OSCTRY } class Send::SocketSender { @@ -350,7 +351,7 @@ class Send::SocketSender { SocketSender(uint16_t port, const char* address) : transmitSocket{IpEndpointName{address, port}} {} - int send(const char* data, std::size_t size) { + size_t send(const char* data, std::size_t size) { transmitSocket.Send(data, size); return size; } @@ -359,7 +360,7 @@ class Send::SocketSender { Send::Send() {} Send::Send(int size) : Packet(size) {} -Send::Send(uint16_t port, const char* address, al_sec timeout, int size) +Send::Send(uint16_t port, const char* address, al_sec /*timeout*/, int size) : Packet(size) { open(port, address); } @@ -379,14 +380,14 @@ bool Send::open(uint16_t port, const char* address) { return true; } -int Send::send() { - int r = send(*this); +size_t Send::send() { + size_t r = send(*this); OSCTRY("Packet::endMessage", Packet::clear();) return r; } -int Send::send(const Packet& p) { - int r = 0; +size_t Send::send(const Packet& p) { + size_t r = 0; OSCTRY("Packet::endMessage", r = sockerSender->send(p.data(), p.size());) return r; } @@ -498,8 +499,8 @@ bool Recv::portAvailable(uint16_t port, const char* address) { } } catch (const std::runtime_error& e) { - // std::cout << "run time exception at Recv::open: " << e.what() << " - // " << address << ":" << port << std::endl; + std::cout << "run time exception at Recv::open: " << e.what() << " " + << address << " : " << port << std::endl; return false; } return true; diff --git a/src/scene/al_DistributedScene.cpp b/src/scene/al_DistributedScene.cpp index 8385a87f..cc51ebfb 100644 --- a/src/scene/al_DistributedScene.cpp +++ b/src/scene/al_DistributedScene.cpp @@ -3,7 +3,7 @@ using namespace al; DistributedScene::DistributedScene(std::string name, int threadPoolSize, - PolySynth::TimeMasterMode masterMode) + TimeMasterMode masterMode) : DynamicScene(threadPoolSize, masterMode) { mName = name; diff --git a/src/scene/al_DynamicScene.cpp b/src/scene/al_DynamicScene.cpp index 5206fa8f..a3548598 100644 --- a/src/scene/al_DynamicScene.cpp +++ b/src/scene/al_DynamicScene.cpp @@ -1,4 +1,5 @@ #include "al/scene/al_DynamicScene.hpp" + #include "al/graphics/al_Shapes.hpp" using namespace std; @@ -58,7 +59,7 @@ void ThreadPool::stopThreads() { DynamicScene::DynamicScene(int threadPoolSize, TimeMasterMode masterMode) : PolySynth(masterMode) { - SpeakerLayout sl = StereoSpeakerLayout(); // Stereo by default + Speakers sl = StereoSpeakerLayout(); // Stereo by default setSpatializer(sl); if (threadPoolSize > 0) { mWorkerThreads = std::make_unique(threadPoolSize); @@ -121,7 +122,7 @@ void DynamicScene::render(Graphics &g) { g.draw(mWorldMarker); } - if (mMasterMode == TIME_MASTER_GRAPHICS) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_GRAPHICS) { processVoices(); // Turn off voices processVoiceTurnOff(); @@ -145,7 +146,7 @@ void DynamicScene::render(Graphics &g) { } voice = voice->next; } - if (mMasterMode == TIME_MASTER_GRAPHICS) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_GRAPHICS) { processInactiveVoices(); } } @@ -157,7 +158,7 @@ void DynamicScene::render(AudioIOData &io) { assert(mSpatializer && "ERROR: call setSpatializer before starting audio"); io.frame(0); mSpatializer->prepare(io); - if (mMasterMode == TIME_MASTER_AUDIO) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_AUDIO) { processVoices(); // Turn off voices processVoiceTurnOff(); @@ -267,13 +268,13 @@ void DynamicScene::render(AudioIOData &io) { io.frame(0); cb->onAudioCB(io); } - if (mMasterMode == TIME_MASTER_AUDIO) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_AUDIO) { processInactiveVoices(); } } void DynamicScene::update(double dt) { - if (mMasterMode == TIME_MASTER_ASYNC) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_FREE) { processVoices(); // Turn off voices processVoiceTurnOff(); @@ -300,7 +301,7 @@ void DynamicScene::update(double dt) { mWorkerThreads->waitForProcessingDone(); } // Update - if (mMasterMode == TIME_MASTER_ASYNC) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_FREE) { processInactiveVoices(); } } @@ -353,7 +354,7 @@ void DynamicScene::audioThreadFunc(DynamicScene *scene, int id) { // active voices are put in mThreadMap if (find(idsToProcess.begin(), idsToProcess.end(), voice->id()) != idsToProcess.end()) { // voice has been assigned to this thread - int offset = voice->getStartOffsetFrames(fpb); + unsigned int offset = voice->getStartOffsetFrames(fpb); if (offset < fpb) { io.frame(offset); int endOffsetFrames = voice->getEndOffsetFrames(fpb); diff --git a/src/scene/al_PolySynth.cpp b/src/scene/al_PolySynth.cpp index f4d604a2..af9e4c0a 100644 --- a/src/scene/al_PolySynth.cpp +++ b/src/scene/al_PolySynth.cpp @@ -1,11 +1,12 @@ -#include - #include "al/scene/al_PolySynth.hpp" +#include + using namespace al; #ifdef __GNUG__ #include + #include #include @@ -209,11 +210,207 @@ int al::asciiToMIDI(int asciiKey, int offset) { return 0; } +// --------- SynthVoice + +bool SynthVoice::setTriggerParams(std::vector pFields, + bool noCalls) { + if (pFields.size() < mTriggerParams.size()) { + // std::cout << "pField count mismatch. Ignoring." << std::endl; + return false; + } + auto it = pFields.begin(); + // Trigger parameters should not trigger callbacks when set through + // this function as these values are initial "construction" values + // If you need the callbacks to propagate, set the parameter values + // directly instead of through these functions. + if (noCalls) { + for (auto ¶m : mTriggerParams) { + if (it->type() == ParameterField::FLOAT) { + if (strcmp(typeid(*param).name(), typeid(Parameter).name()) == 0) { + static_cast(param)->setNoCalls(it->get()); + } else if (strcmp(typeid(*param).name(), typeid(ParameterInt).name()) == + 0) { + static_cast(param)->setNoCalls(it->get()); + } else if (strcmp(typeid(*param).name(), + typeid(ParameterMenu).name()) == 0) { + static_cast(param)->setNoCalls(it->get()); + } else if (strcmp(typeid(*param).name(), + typeid(ParameterString).name()) == 0) { + static_cast(param)->setNoCalls( + std::to_string(it->get())); + } else { + std::cerr << "ERROR: p-field string not setting parameter. Invalid " + "parameter type for parameter " + << param->getFullAddress() << std::endl; + } + } else if (it->type() == ParameterField::STRING) { + if (strcmp(typeid(*param).name(), typeid(ParameterString).name()) == + 0) { + static_cast(param)->setNoCalls( + it->get()); + } else if (strcmp(typeid(*param).name(), + typeid(ParameterMenu).name()) == 0) { + static_cast(param)->setCurrent( + it->get(), noCalls); + } else { + std::cerr << "ERROR: p-field string not setting parameter. Invalid " + "parameter type for parameter " + << param->getFullAddress() << std::endl; + } + } + it++; + } + } else { + for (auto ¶m : mTriggerParams) { + if (it->type() == ParameterField::FLOAT) { + if (strcmp(typeid(*param).name(), typeid(Parameter).name()) == 0) { + static_cast(param)->set(it->get()); + } else if (strcmp(typeid(*param).name(), typeid(ParameterInt).name()) == + 0) { + static_cast(param)->set(it->get()); + } else if (strcmp(typeid(*param).name(), + typeid(ParameterMenu).name()) == 0) { + static_cast(param)->set(it->get()); + } else if (strcmp(typeid(*param).name(), + typeid(ParameterString).name()) == 0) { + static_cast(param)->set( + std::to_string(it->get())); + } else { + std::cerr << "ERROR: p-field string not setting parameter. Invalid " + "parameter type for parameter " + << param->getFullAddress() << std::endl; + } + } else if (it->type() == ParameterField::STRING) { + if (strcmp(typeid(*param).name(), typeid(ParameterString).name()) == + 0) { + static_cast(param)->set(it->get()); + } else if (strcmp(typeid(*param).name(), + typeid(ParameterMenu).name()) == 0) { + static_cast(param)->setCurrent( + it->get(), noCalls); + } else { + std::cerr << "ERROR: p-field string not setting parameter. Invalid " + "parameter type for parameter " + << param->getFullAddress() << std::endl; + } + } + it++; + } + } + return true; +} + +int SynthVoice::getTriggerParams(float *pFields, int maxParams) { + std::vector pFieldsVector = getTriggerParams(); + if (maxParams == -1) { + assert(pFieldsVector.size() < INT_MAX); + maxParams = int(pFieldsVector.size()); + } + int count = 0; + for (auto param : pFieldsVector) { + if (count == maxParams) { + break; + } + if (param.type() == ParameterField::FLOAT) { + *pFields++ = param.get(); + } else { + *pFields++ = 0.0f; // Ignore strings... + } + count++; + } + return count; +} + +std::vector SynthVoice::getTriggerParams() { + std::vector pFields; + pFields.reserve(mTriggerParams.size()); + for (auto param : mTriggerParams) { + if (param) { + if (strcmp(typeid(*param).name(), typeid(ParameterString).name()) == 0) { + pFields.push_back(static_cast(param)->get()); + } else if (strcmp(typeid(*param).name(), typeid(ParameterMenu).name()) == + 0) { + pFields.push_back(static_cast(param)->getCurrent()); + } else { + pFields.push_back(param->toFloat()); + } + } + } + return pFields; +} + +void SynthVoice::triggerOn(int offsetFrames) { + mOnOffsetFrames = offsetFrames; + mActive = true; + onTriggerOn(); +} + +void SynthVoice::triggerOff(int offsetFrames) { + mOffOffsetFrames = + offsetFrames; // TODO implement offset frames for trigger off. + // Currently ignoring and turning off at start of buffer + onTriggerOff(); +} + +int SynthVoice::getStartOffsetFrames(unsigned int framesPerBuffer) { + int frames = mOnOffsetFrames; + mOnOffsetFrames -= framesPerBuffer; + if (mOnOffsetFrames < 0) { + mOnOffsetFrames = 0; + } + return frames; +} + +int SynthVoice::getEndOffsetFrames(unsigned int framesPerBuffer) { + int frames = mOffOffsetFrames; + mOffOffsetFrames -= framesPerBuffer; + if (mOffOffsetFrames < 0) { + mOffOffsetFrames = 0; + } + return frames; +} + +std::shared_ptr SynthVoice::createInternalTriggerParameter( + std::string name, float defaultValue, float minValue, float maxValue) { + mInternalParameters.push_back( + std::make_shared(name, defaultValue, minValue, maxValue)); + registerTriggerParameter(*mInternalParameters.back().get()); + return mInternalParameters.back(); +} + +Parameter &SynthVoice::getInternalParameter(std::string name) { + for (auto param : mInternalParameters) { + if (param->getName() == name && + strcmp(typeid(*param).name(), typeid(Parameter).name()) == 0) { + return *param; + } + } + std::cerr << "Parameter not found! Aborting: " << name << std::endl; + throw "Invalid parameter name"; +} + +float SynthVoice::getInternalParameterValue(std::string name) { + for (auto param : mInternalParameters) { + if (param->getName() == name) { + return param->get(); + } + } + return 0.0; +} + +void SynthVoice::setInternalParameterValue(std::string name, float value) { + for (auto param : mInternalParameters) { + if (param->getName() == name) { + param->set(value); + // return; + } + } +} + // ---------------------------- -PolySynth::PolySynth(PolySynth::TimeMasterMode masterMode) - : mMasterMode(masterMode) { - if (mMasterMode == TIME_MASTER_CPU) { +PolySynth::PolySynth(TimeMasterMode masterMode) : mMasterMode(masterMode) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_CPU) { startCpuClockThread(); } } @@ -330,7 +527,7 @@ void PolySynth::render(AudioIOData &io) { if (!m_internalAudioConfigured) { prepare(io); } - if (mMasterMode == TIME_MASTER_AUDIO) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_AUDIO) { processVoices(); // Turn off voices processVoiceTurnOff(); @@ -384,13 +581,13 @@ void PolySynth::render(AudioIOData &io) { io.frame(0); cb->onAudioCB(io); } - if (mMasterMode == TIME_MASTER_AUDIO) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_AUDIO) { processInactiveVoices(); } } void PolySynth::render(Graphics &g) { - if (mMasterMode == TIME_MASTER_GRAPHICS) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_GRAPHICS) { processVoices(); // Turn off voices processVoiceTurnOff(); @@ -404,13 +601,13 @@ void PolySynth::render(Graphics &g) { } voice = voice->next; } - if (mMasterMode == TIME_MASTER_GRAPHICS) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_GRAPHICS) { processInactiveVoices(); } } void PolySynth::update(double dt) { - if (mMasterMode == TIME_MASTER_ASYNC) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_FREE) { processVoices(); // Turn off voices processVoiceTurnOff(); @@ -423,7 +620,7 @@ void PolySynth::update(double dt) { } voice = voice->next; } - if (mMasterMode == TIME_MASTER_ASYNC) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_FREE) { processInactiveVoices(); } } @@ -479,9 +676,9 @@ bool PolySynth::popFreeVoice(SynthVoice *voice) { return false; } -void PolySynth::setTimeMaster(PolySynth::TimeMasterMode masterMode) { +void PolySynth::setTimeMaster(TimeMasterMode masterMode) { mMasterMode = masterMode; - if (mMasterMode == TIME_MASTER_CPU) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_CPU) { mRunCPUClock = true; startCpuClockThread(); } else { @@ -571,6 +768,24 @@ void PolySynth::print(std::ostream &stream) { } } +void PolySynth::registerTriggerOnCallback( + std::function cb, void *userData) { + TriggerOnCallback cbNode(cb, userData); + mTriggerOnCallbacks.push_back(cbNode); +} + +void PolySynth::registerTriggerOffCallback(std::function cb, + void *userData) { + TriggerOffCallback cbNode(cb, userData); + mTriggerOffCallbacks.push_back(cbNode); +} + +void PolySynth::registerFreeCallback(std::function cb, + void *userData) { + FreeCallback cbNode(cb, userData); + mFreeCallbacks.push_back(cbNode); +} + SynthVoice *PolySynth::allocateVoice(std::string name) { if (mCreators.find(name) != mCreators.end()) { if (mVerbose) { @@ -613,20 +828,28 @@ void PolySynth::startCpuClockThread() { } } -int SynthVoice::getStartOffsetFrames(unsigned int framesPerBuffer) { - int frames = mOnOffsetFrames; - mOnOffsetFrames -= framesPerBuffer; - if (mOnOffsetFrames < 0) { - mOnOffsetFrames = 0; - } - return frames; +void PolySynth::prepare(AudioIOData &io) { + internalAudioIO.framesPerBuffer(io.framesPerBuffer()); + internalAudioIO.channelsIn(mVoiceMaxInputChannels); + internalAudioIO.channelsOut(mVoiceMaxOutputChannels); + internalAudioIO.channelsBus(mVoiceBusChannels); + if ((int)io.channelsBus() < mVoiceBusChannels) { + std::cout << "WARNING: You don't have enough buses in AudioIO object. " + "This is likely to crash." + << std::endl; + } + // mThreadedAudioData.resize(mAudioThreads.size()); + // for (auto &threadio: mThreadedAudioData) { + // threadio.framesPerBuffer(io.framesPerBuffer()); + // threadio.channelsIn(mVoiceMaxInputChannels); + // threadio.channelsOut(mVoiceMaxOutputChannels); + // threadio.channelsBus(mVoiceBusChannels); + // } + m_internalAudioConfigured = true; } -int SynthVoice::getEndOffsetFrames(unsigned int framesPerBuffer) { - int frames = mOffOffsetFrames; - mOffOffsetFrames -= framesPerBuffer; - if (mOffOffsetFrames < 0) { - mOffOffsetFrames = 0; - } - return frames; +void PolySynth::registerAllocateCallback( + std::function cb, void *userData) { + AllocationCallback cbNode(cb, userData); + mAllocationCallbacks.push_back(cbNode); } diff --git a/src/scene/al_SynthSequencer.cpp b/src/scene/al_SynthSequencer.cpp index 7227c1f4..51518ce4 100644 --- a/src/scene/al_SynthSequencer.cpp +++ b/src/scene/al_SynthSequencer.cpp @@ -1,4 +1,6 @@ +#include "al/scene/al_SynthSequencer.hpp" + #include #include #include @@ -7,12 +9,10 @@ #include #include // For class name instrospection -#include "al/scene/al_SynthSequencer.hpp" - using namespace al; void SynthSequencer::render(AudioIOData &io) { - if (mMasterMode == PolySynth::TIME_MASTER_AUDIO) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_AUDIO) { double timeIncrement = mNormalizedTempo * io.framesPerBuffer() / (double)io.framesPerSecond(); double blockStartTime = mMasterTime; @@ -23,7 +23,7 @@ void SynthSequencer::render(AudioIOData &io) { } void SynthSequencer::render(Graphics &g) { - if (mMasterMode == PolySynth::TIME_MASTER_GRAPHICS) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_GRAPHICS) { double timeIncrement = 1.0 / mFps; double blockStartTime = mMasterTime; mMasterTime += timeIncrement; @@ -43,7 +43,7 @@ bool SynthSequencer::playSequence(std::string sequenceName, float startTime) { // before the sequence mMasterTime = startTime; double currentMasterTime = mMasterTime; - const double startPad = 0.1; + const double startPad = 0.0; std::list events = loadSequence(sequenceName, currentMasterTime - startTime + startPad); std::unique_lock lk(mEventLock); @@ -56,7 +56,7 @@ bool SynthSequencer::playSequence(std::string sequenceName, float startTime) { for (auto cb : mSequenceBeginCallbacks) { cb(mLastSequencePlayed); } - if (mMasterMode == PolySynth::TIME_MASTER_CPU) { + if (mMasterMode == TimeMasterMode::TIME_MASTER_CPU) { mCpuThread = std::make_shared([&](int granularityns = 1000) { bool running = true; auto startTime = std::chrono::high_resolution_clock::now(); diff --git a/src/sound/al_Ambisonics.cpp b/src/sound/al_Ambisonics.cpp index 584aff81..3518252f 100644 --- a/src/sound/al_Ambisonics.cpp +++ b/src/sound/al_Ambisonics.cpp @@ -1,7 +1,7 @@ -#include - #include "al/sound/al_Ambisonics.hpp" +#include + #ifdef USE_GAMMA #include "scl.h" #define COS gam::scl::cosT8 @@ -275,7 +275,7 @@ void AmbiDecode::setSpeakers(Speakers* spkrs) { // for (unsigned i = 0; i < mSpeakers.size(); i++) { // Speaker &spkr = spkrs->at(i); // setSpeaker(i, spkr.deviceChannel, spkr.azimuth, spkr.elevation, - //spkr.gain); + // spkr.gain); // } setSpeakers(*spkrs); } @@ -378,10 +378,13 @@ void AmbiEncode::print(std::ostream& stream) { // Ambisonics Spatializer ----------------- -AmbisonicsSpatializer::AmbisonicsSpatializer(SpeakerLayout& sl, int dim, - int order, int flavor) +AmbisonicsSpatializer::AmbisonicsSpatializer() + : Spatializer({}), mDecoder(3, 1, 8, 1), mEncoder(3, 1) {} + +AmbisonicsSpatializer::AmbisonicsSpatializer(Speakers& sl, int dim, int order, + int flavor) : Spatializer(sl), - mDecoder(dim, order, sl.numSpeakers(), flavor), + mDecoder(dim, order, sl.size(), flavor), mEncoder(dim, order){}; void AmbisonicsSpatializer::zeroAmbi() { @@ -437,8 +440,8 @@ void AmbisonicsSpatializer::renderBuffer(AudioIOData& io, const float* samples, const unsigned int& numFrames) { // // compute azimuth & elevation of relative position in current - //listener's coordinate frame: Vec3d urel(relpos); urel.normalize(); // unit - //vector in axis listener->source + // listener's coordinate frame: Vec3d urel(relpos); urel.normalize(); + // // unit vector in axis listener->source // /* project into listener's coordinate frame: // Vec3d axis; @@ -460,7 +463,7 @@ void AmbisonicsSpatializer::renderBuffer(AudioIOData& io, // for(int i = 0; i < numFrames; i++){ //// // cheaper: //// Vec3d direction = - ///mListener->quatHistory()[i].rotateTransposed(urel); + /// mListener->quatHistory()[i].rotateTransposed(urel); //// //mEncoder.direction(azimuth, elevation); //// //mEncoder.direction(-rf, -rr, ru); diff --git a/src/sound/al_AudioScene.cpp b/src/sound/al_AudioScene.cpp deleted file mode 100644 index 08649df4..00000000 --- a/src/sound/al_AudioScene.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include "al/sound/al_AudioScene.hpp" -#include "al/math/al_Constants.hpp" - -#include - -namespace al { - -void AudioSceneObject::updateHistory() { mPosHistory(mPose.pos()); } - -Listener::Listener(int numFrames_, Spatializer* spatializer) - : mSpatializer(spatializer), mIsCompiled(false) { - numFrames(numFrames_); -} - -void Listener::numFrames(unsigned v) { - if (mQuatHistory.size() != v) mQuatHistory.resize(v); - mSpatializer->numFrames(v); -} - -void Listener::updateHistory(int numFrames) { - AudioSceneObject::updateHistory(); - - // Create a buffer of spherically interpolated quaternions from the previous - // quat to the current quat: - Quatd qnew = pose().quat(); - Quatd::slerpBuffer(mQuatPrev, qnew, &mQuatHistory[0], numFrames); - mQuatPrev = qnew; -} - -void Listener::compile() { - mIsCompiled = true; - mSpatializer->compile(); -} - -SoundSource::SoundSource(double nearClip, double farClip, AttenuationLaw law, - DopplerType dopplerType, double sampleRate, - double farBias, int delaySize) - : DistAtten(nearClip, farClip, law, farBias), - mSound(delaySize), - mUseAtten(true), - mDopplerType(dopplerType), - mUsePerSampleProcessing(false), - mCachedIndex(0), - mSampleRate(sampleRate), - mSpeedOfSound(340), - mFrameCounter(0) { - // initialize the position history to be VERY FAR AWAY so that we don't deafen - // ourselves... - for (int i = 0; i < mPosHistory.size(); ++i) { - mPosHistory(Vec3d(1000, 0, 0)); - } - - presenceFilter.set(2700); -} - -int SoundSource::bufferSize(double samplerate, double speedOfSound, - double distance) { - return (int)ceil(samplerate * distance / speedOfSound); -} - -AudioScene::AudioScene(int numFrames_) - : mNumFrames(0), mPerSampleProcessing(false) { - numFrames(numFrames_); -} - -AudioScene::~AudioScene() { - for (Listeners::iterator it = mListeners.begin(); it != mListeners.end(); - ++it) { - delete (*it); - } -} - -void AudioScene::addSource(SoundSource& src) { mSources.push_back(&src); } - -void AudioScene::removeSource(SoundSource& src) { mSources.remove(&src); } - -void AudioScene::numFrames(int v) { - if (mNumFrames != v) { - Listeners::iterator it = mListeners.begin(); - while (it != mListeners.end()) { - (*it)->numFrames(v); - ++it; - } - mNumFrames = v; - mBuffer.reserve(mNumFrames); - } -} - -Listener* AudioScene::createListener(Spatializer* spatializer) { - Listener* l = new Listener(mNumFrames, spatializer); - l->compile(); - mListeners.push_back(l); - return l; -} - -/* - So, for large numbers of sources it quickly gets too expensive. - Having one delayline per soundsource (for doppler) is itself quite - taxing. e.g., at 44100kHz sampling rate, a speed of sound of 343m/s and an - audible distance of 50m implies a delay of at least 6428 samples (need to add - blocksize to that too). The actual buffersize sets the effective doppler - far-clip; beyond this it always uses max-delay size (no doppler) The - head-size sets the effective doppler near-clip. -*/ - -void AudioScene::render(AudioIOData& io) { - const int numFrames = io.framesPerBuffer(); - // double sampleRate = io.framesPerSecond(); - io.zeroOut(); - - // iterate through all listeners adding contribution from all sources - for (unsigned il = 0; il < mListeners.size(); ++il) { - Listener& l = *mListeners[il]; - - Spatializer* spatializer = l.mSpatializer; - spatializer->prepare(io); - - // update listener history data: - l.updateHistory(numFrames); - - // iterate through all sound sources - for (Sources::iterator it = mSources.begin(); it != mSources.end(); ++it) { - SoundSource& src = *(*it); - if (mPerSampleProcessing) { // audioscene per sample processing - src.frame(0); - for (int i = 0; i < numFrames; ++i) { - Pose relpos(src.pose().pos() - l.pose().pos(), - src.pose().quat() / l.pose().quat()); - spatializer->renderSample(io, relpos, src.getNextSample(l), i); - } - } else { // more efficient, per buffer processing for audioscene (does - // not work well with doppler) - src.getBuffer(l, mBuffer.data(), numFrames); - Pose relpos(src.pose().pos() - l.pose().pos(), - src.pose().quat() / l.pose().quat()); - // std::cout << l.pose().x() << "," << - //l.pose().z() << " ---- "; std::cout << src.pos().x << "," << - //src.pos().z << " ----- "; std::cout << relpos.x << "," << relpos.z << - //std::endl; - spatializer->renderBuffer(io, relpos, mBuffer.data(), numFrames); - } - } // end for each source - - spatializer->finalize(io); - - } // end for each listener -} - -} // namespace al - -// From hydrogen bond... -/* -struct AmbiSource{ - - AmbiSource(uint32_t order, uint32_t dim) - : doppler(0.1), dist(2, 0.001), angH(2), angV(2), enc(order, dim) - {} - - void operator()(double * outA, double * inT, int numT, double -dopScale=1, double distCoef=2, double ampOffset=0){ - - // do distance coding - for(int i=0; i dist, angH, angV; // Low-pass filters on source -position AmbiEncode enc; -}; - - - // block rate - // (double * outA, double * inT, int numT, double dopScale=1, double -distCoef=2, double ampOffset=0) ambiH (io.aux(AMBI_NUM_SOURCES), io.aux(0), -io.numFrames(), 1./32); ambiO (io.aux(AMBI_NUM_SOURCES), io.aux(1), -io.numFrames(), 1./32, 2, 0.1); ambiZn(io.aux(AMBI_NUM_SOURCES), io.aux(2), -io.numFrames(), 1./32, 2, 0.1); - - //---- Ambisonic decoding: - static double chanMap[16] = { - 2, 3, 4, 5, 6, 7, 8, 9, // MOTU #1 - 16, 17, 18, 19, 20, 21, 22, 23 // MOTU #2 - }; - - //void decode(T ** dec, const T ** enc, uint32_t numDecFrames); - - // loop speakers - for(int s=0; s < 16; ++s){ - double * out = io.out(chanMap[s]); - - // loop ambi channels - for(unsigned int c=0; c - #include "al/sound/al_Lbap.hpp" +#include + using namespace al; void Lbap::compile() { - std::map speakerRingMap; + std::map speakerRingMap; for (auto &speaker : mSpeakers) { if (speakerRingMap.find(speaker.group) == speakerRingMap.end()) { - speakerRingMap[speaker.group] = SpeakerLayout(); + speakerRingMap[speaker.group] = Speakers(); } - speakerRingMap[speaker.group].addSpeaker(speaker); + speakerRingMap[speaker.group].push_back(speaker); } for (auto speakerRing : speakerRingMap) { mRings.push_back(LdapRing(speakerRing.second)); diff --git a/src/sound/al_SoundFile.cpp b/src/sound/al_SoundFile.cpp index 2832f122..c7928e3c 100644 --- a/src/sound/al_SoundFile.cpp +++ b/src/sound/al_SoundFile.cpp @@ -3,13 +3,15 @@ #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #define DR_FLAC_IMPLEMENTATION -#include "dr_flac.h" - #include #include #include -bool al::SoundFile::open(const char* path) { +#include "dr_flac.h" + +using namespace al; + +bool SoundFile::open(const char* path) { auto len = std::strlen(path); if (len < 5) { @@ -27,7 +29,7 @@ bool al::SoundFile::open(const char* path) { channels = (int)c; sampleRate = (int)s; frameCount = (long long int)f; - size_t n = size_t(channels * frameCount); + size_t n = size_t(c * f); data.resize(n); std::memcpy(data.data(), file_data, sizeof(float) * n); drwav_free(file_data); @@ -56,7 +58,7 @@ bool al::SoundFile::open(const char* path) { channels = (int)c; sampleRate = (int)s; frameCount = (long long int)f; - size_t n = (size_t)(channels * frameCount); + size_t n = (size_t)(c * f); data.resize(n); std::memcpy(data.data(), file_data, sizeof(float) * n); drflac_free(file_data); @@ -69,18 +71,18 @@ bool al::SoundFile::open(const char* path) { return false; } -float* al::SoundFile::getFrame(long long int frame) { +float* SoundFile::getFrame(long long int frame) { return data.data() + frame * channels; } -al::SoundFile al::getResampledSoundFile(SoundFile* toConvert, - unsigned int newSampleRate) { +SoundFile al::getResampledSoundFile(SoundFile* toConvert, + unsigned int newSampleRate) { // TODO! return {}; } -void al::SoundFilePlayer::getFrames(int numFrames, float* buffer, - int bufferLength) { +void SoundFilePlayer::getFrames(uint64_t numFrames, float* buffer, + int bufferLength) { if (pause || !soundFile) { for (int i = 0; i < bufferLength; i += 1) { buffer[i] = 0.0f; @@ -116,3 +118,47 @@ void al::SoundFilePlayer::getFrames(int numFrames, float* buffer, } frame += n; } + +SoundFileStreaming::SoundFileStreaming(const char* path) { + if (path) { + if (!open(path)) { + std::cerr << "ERROR opening file:" << path << std::endl; + } + } +} + +SoundFileStreaming::~SoundFileStreaming() { close(); } + +uint32_t SoundFileStreaming::sampleRate() { + return static_cast(mImpl)->sampleRate; +} + +uint64_t SoundFileStreaming::totalFrames() { + return static_cast(mImpl)->totalPCMFrameCount; +} + +uint16_t SoundFileStreaming::numChannels() { + return static_cast(mImpl)->channels; +} + +bool SoundFileStreaming::open(const char* path) { + close(); + mImpl = new drwav; + if (!drwav_init_file((drwav*)mImpl, path)) { + return false; + } + return true; +} + +void SoundFileStreaming::close() { + if (mImpl) { + drwav_uninit((drwav*)mImpl); + delete (drwav*)mImpl; + } +} + +uint64_t SoundFileStreaming::getFrames(uint64_t numFrames, float* buffer) { + drwav_uint64 framesRead = + drwav_read_pcm_frames_f32((drwav*)mImpl, numFrames, buffer); + return framesRead; +} diff --git a/src/sound/al_Spatializer.cpp b/src/sound/al_Spatializer.cpp index d8696161..8049d461 100644 --- a/src/sound/al_Spatializer.cpp +++ b/src/sound/al_Spatializer.cpp @@ -2,9 +2,4 @@ using namespace al; -Spatializer::Spatializer(const SpeakerLayout& sl) { - size_t numSpeakers = sl.speakers().size(); - for (unsigned i = 0; i < numSpeakers; ++i) { - mSpeakers.push_back(sl.speakers()[i]); - } -} +Spatializer::Spatializer(const Speakers &sl) { mSpeakers = sl; } diff --git a/src/sound/al_Speaker.cpp b/src/sound/al_Speaker.cpp new file mode 100644 index 00000000..3d0a29e9 --- /dev/null +++ b/src/sound/al_Speaker.cpp @@ -0,0 +1,83 @@ +#include "al/sound/al_Speaker.hpp" + +using namespace al; + +Speaker::Speaker(unsigned int deviceChan, float az, float el, int group, + float radius, float gain) + : deviceChannel(deviceChan), + azimuth(az), + elevation(el), + group(group), + radius(radius), + gain(gain) {} + +void Speaker::posCart2(Vec3d xyz) { + using namespace std; + + radius = + sqrt(float((xyz[0] * xyz[0]) + (xyz[1] * xyz[1]) + (xyz[2] * xyz[2]))); + float gd = sqrt(float((xyz[0] * xyz[0]) + (xyz[1] * xyz[1]))); + elevation = atan2f(float(xyz[2]), gd) * 180.f / float(M_PI); + azimuth = atan2f(float(xyz[0]), float(xyz[1])) * 180.f / float(M_PI); +} + +Vec3d Speaker::vec() const { + // TODO doxygen style commenting on coordinates like ambisonics + double cosel = cos(toRad(double(elevation))); + double x = cos(toRad(double(azimuth))) * cosel * double(radius); + double y = sin(toRad(double(azimuth))) * cosel * double(radius); + double z = sin(toRad(double(elevation))) * double(radius); + // Ryan: the standard conversions assume +z is up, these are correct for + // allocore + + // double x = sin(toRad(azimuth)) * cosel * radius; + // double y = sin(toRad(elevation)) * radius; + // double z = -1*cos(toRad(azimuth)) * cosel * radius; + return Vec3d(x, y, z); +} + +Vec3d Speaker::vecGraphics() const { + // TODO doxygen style commenting on coordinates like ambisonics + double cosel = cos(toRad(double(elevation))); + double x = cos(toRad(double(azimuth))) * cosel * double(radius); + double y = sin(toRad(double(azimuth))) * cosel * double(radius); + double z = sin(toRad(double(elevation))) * double(radius); + // Ryan: the standard conversions assume +z is up, these are correct for + // allocore + + // double x = sin(toRad(azimuth)) * cosel * radius; + // double y = sin(toRad(elevation)) * radius; + // double z = -1*cos(toRad(azimuth)) * cosel * radius; + return Vec3d(-y, z, -x); +} + +// -------------------- +namespace al { +Speakers HeadsetSpeakerLayout(unsigned int deviceChannelStart, float radius, + float gain) { + return SpeakerRingLayout<2>(deviceChannelStart, 90, radius, gain); +} + +Speakers StereoSpeakerLayout(unsigned int deviceChannelStart, float angle, + float distance, float gain) { + return Speakers{{deviceChannelStart, angle, 0, 0, distance, gain}, + {deviceChannelStart + 1, -angle, 0, 0, distance, gain}}; +} + +Speakers OctalSpeakerLayout(unsigned int deviceChannelStart, float phase, + float radius, float gain) { + return SpeakerRingLayout<8>(deviceChannelStart, phase, radius, gain); +} + +Speakers CubeLayout(unsigned int deviceChannelStart) { + Speakers mSpeakers; + mSpeakers.reserve(8); + for (unsigned int i = 0; i < 4; ++i) { + mSpeakers.emplace_back(Speaker(i + deviceChannelStart, 45.f + (i * 90), 0)); + mSpeakers.emplace_back( + Speaker(4 + i + deviceChannelStart, 45.f + (i * 90), 60, 0, sqrt(5.f))); + } + return mSpeakers; +} + +} // namespace al diff --git a/src/sound/al_StereoPanner.cpp b/src/sound/al_StereoPanner.cpp index d3473ac8..985cbf44 100644 --- a/src/sound/al_StereoPanner.cpp +++ b/src/sound/al_StereoPanner.cpp @@ -1,7 +1,7 @@ -#include - #include "al/sound/al_StereoPanner.hpp" +#include + void al::StereoPanner::renderSample(al::AudioIOData &io, const al::Pose &listeningPose, const float &sample, @@ -16,7 +16,8 @@ void al::StereoPanner::renderSample(al::AudioIOData &io, io.out(0, frameIndex) += gainL * sample; io.out(1, frameIndex) += gainR * sample; } else { // don't pan - for (int i = 0; i < numSpeakers; i++) io.out(i, frameIndex) = sample; + for (unsigned int i = 0; i < numSpeakers; i++) + io.out(i, frameIndex) = sample; } } @@ -34,12 +35,12 @@ void al::StereoPanner::renderBuffer(al::AudioIOData &io, float gainL, gainR; equalPowerPan(vec, gainL, gainR); - for (int i = 0; i < numFrames; i++) { + for (unsigned int i = 0; i < numFrames; i++) { bufL[i] += gainL * samples[i]; bufR[i] += gainR * samples[i]; } } else { // dont pan - for (int i = 0; i < numSpeakers; i++) { + for (unsigned int i = 0; i < numSpeakers; i++) { memcpy(io.outBuffer(i), samples, sizeof(float) * numFrames); } } @@ -48,7 +49,7 @@ void al::StereoPanner::renderBuffer(al::AudioIOData &io, void al::StereoPanner::equalPowerPan(const al::Vec3d &relPos, float &gainL, float &gainR) { double panVal = 0.5; - if (relPos.z != 0 || relPos.x != 0) { + if (relPos.z != 0.0 || relPos.x != 0.0) { panVal = 1.0 - std::fabs(std::atan2(relPos.z, relPos.x) / M_PI); } diff --git a/src/sound/al_Vbap.cpp b/src/sound/al_Vbap.cpp index 94e085e9..ba4275cc 100755 --- a/src/sound/al_Vbap.cpp +++ b/src/sound/al_Vbap.cpp @@ -47,7 +47,7 @@ bool SpeakerTriple::loadVectors(const std::vector &spkrs) { return hasInverse; } -Vbap::Vbap(const SpeakerLayout &sl, bool is3D) : Spatializer(sl), mIs3D(is3D) {} +Vbap::Vbap(const Speakers &sl, bool is3D) : Spatializer(sl), mIs3D(is3D) {} void Vbap::addTriple(const SpeakerTriple &st) { mTriplets.push_back(st); } diff --git a/src/sphere/al_AlloSphereSpeakerLayout.cpp b/src/sphere/al_AlloSphereSpeakerLayout.cpp new file mode 100644 index 00000000..9b52c874 --- /dev/null +++ b/src/sphere/al_AlloSphereSpeakerLayout.cpp @@ -0,0 +1,64 @@ +#include "al/sphere/al_AlloSphereSpeakerLayout.hpp" + +namespace al { + +Speakers AlloSphereSpeakerLayout() { + return Speakers{ + Speaker(1 - 1, -77.660913f, 41.000000, 1, 4.992118f), + Speaker(2 - 1, -45.088015f, 41.000000, 1, 5.571107f), + Speaker(3 - 1, -14.797289f, 41.000000, 1, 5.900603f), + Speaker(4 - 1, 14.797289f, 41.000000f, 1, 5.900603f), + Speaker(5 - 1, 45.088015f, 41.000000f, 1, 5.571107f), + Speaker(6 - 1, 77.660913f, 41.000000f, 1, 4.992118f), + Speaker(7 - 1, 102.339087f, 41.000000f, 1, 4.992118f), + Speaker(8 - 1, 134.911985f, 41.000000f, 1, 5.571107f), + Speaker(9 - 1, 165.202711f, 41.000000f, 1, 5.900603f), + Speaker(10 - 1, -165.202711f, 41.000000f, 1, 5.900603f), + Speaker(11 - 1, -134.911985f, 41.000000f, 1, 5.571107f), + Speaker(12 - 1, -102.339087f, 41.000000f, 1, 4.992118f), + Speaker(17 - 1, -77.660913f, 0.000000f, 0, 4.992118f), + Speaker(18 - 1, -65.647587f, 0.000000f, 0, 5.218870f), + Speaker(19 - 1, -54.081600f, 0.000000f, 0, 5.425483f), + Speaker(20 - 1, -42.869831f, 0.000000f, 0, 5.604350f), + Speaker(21 - 1, -31.928167f, 0.000000f, 0, 5.749461f), + Speaker(22 - 1, -21.181024f, 0.000000f, 0, 5.856274f), + Speaker(23 - 1, -10.559657f, 0.000000f, 0, 5.921613f), + Speaker(24 - 1, 0.000000f, 0.000000f, 0, 5.943600f), + Speaker(25 - 1, 10.559657f, 0.000000f, 0, 5.921613f), + Speaker(26 - 1, 21.181024f, 0.000000f, 0, 5.856274f), + Speaker(27 - 1, 31.928167f, 0.000000f, 0, 5.749461f), + Speaker(28 - 1, 42.869831f, 0.000000f, 0, 5.604350f), + Speaker(29 - 1, 54.081600f, 0.000000f, 0, 5.425483f), + Speaker(30 - 1, 65.647587f, 0.000000f, 0, 5.218870f), + Speaker(31 - 1, 77.660913f, 0.000000f, 0, 4.992118f), + Speaker(32 - 1, 102.339087f, 0.000000f, 0, 4.992118f), + Speaker(33 - 1, 114.352413f, 0.000000f, 0, 5.218870f), + Speaker(34 - 1, 125.918400f, 0.000000f, 0, 5.425483f), + Speaker(35 - 1, 137.130169f, 0.000000f, 0, 5.604350f), + Speaker(36 - 1, 148.071833f, 0.000000f, 0, 5.749461f), + Speaker(37 - 1, 158.818976f, 0.000000f, 0, 5.856274f), + Speaker(38 - 1, 169.440343f, 0.000000f, 0, 5.921613f), + Speaker(39 - 1, 180.000000f, 0.000000f, 0, 5.943600f), + Speaker(40 - 1, -169.440343f, 0.000000f, 0, 5.921613f), + Speaker(41 - 1, -158.818976f, 0.000000f, 0, 5.856274f), + Speaker(42 - 1, -148.071833f, 0.000000f, 0, 5.749461f), + Speaker(43 - 1, -137.130169f, 0.000000f, 0, 5.604350f), + Speaker(44 - 1, -125.918400f, 0.000000f, 0, 5.425483f), + Speaker(45 - 1, -114.352413f, 0.000000f, 0, 5.218870f), + Speaker(46 - 1, -102.339087f, 0.000000f, 0, 4.992118f), + Speaker(49 - 1, -77.660913f, -32.500000f, 2, 4.992118f), + Speaker(50 - 1, -45.088015f, -32.500000f, 2, 5.571107f), + Speaker(51 - 1, -14.797289f, -32.500000f, 2, 5.900603f), + Speaker(52 - 1, 14.797289f, -32.500000f, 2, 5.900603f), + Speaker(53 - 1, 45.088015f, -32.500000f, 2, 5.571107f), + Speaker(54 - 1, 77.660913f, -32.500000f, 2, 4.992118f), + Speaker(55 - 1, 102.339087f, -32.500000f, 2, 4.992118f), + Speaker(56 - 1, 134.911985f, -32.500000f, 2, 5.571107f), + Speaker(57 - 1, 165.202711f, -32.500000f, 2, 5.900603f), + Speaker(58 - 1, -165.202711f, -32.500000f, 2, 5.900603f), + Speaker(59 - 1, -134.911985f, -32.500000f, 2, 5.571107f), + Speaker(60 - 1, -102.339087f, -32.500000f, 2, 4.992118f), + }; +} + +} // namespace al diff --git a/src/sphere/al_PerProjection.cpp b/src/sphere/al_PerProjection.cpp index 1a149778..365169f0 100644 --- a/src/sphere/al_PerProjection.cpp +++ b/src/sphere/al_PerProjection.cpp @@ -1,4 +1,4 @@ -#include "al/sphere/al_Perprojection.hpp" +#include "al/sphere/al_PerProjection.hpp" al::Mat4f al::get_cube_mat(int face) { switch (face) { diff --git a/src/sphere/al_SphereUtils.cpp b/src/sphere/al_SphereUtils.cpp index 529394ee..69fb4df0 100644 --- a/src/sphere/al_SphereUtils.cpp +++ b/src/sphere/al_SphereUtils.cpp @@ -1,7 +1,16 @@ - +#define GLFW_INCLUDE_NONE #include "al/sphere/al_SphereUtils.hpp" +// gethostname +#ifdef AL_WINDOWS +#include +#else +#include +#endif + +#include + std::string al_get_hostname() { char hostname[256]; #ifdef AL_WINDOWS @@ -14,32 +23,36 @@ std::string al_get_hostname() { using namespace al; -bool sphere::is_simulator(const std::string &host) { +bool sphere::isSimulatorMachine(const std::string &host) { return (host.substr(0, 5) == "audio"); } -bool sphere::is_simulator() { return is_simulator(al_get_hostname()); } +bool sphere::isSimulatorMachine() { + return isSimulatorMachine(al_get_hostname()); +} -bool sphere::is_renderer(const std::string &host) { +bool sphere::isRendererMachine(const std::string &host) { return (host.substr(0, 2) == "gr"); } -bool sphere::is_renderer() { return is_renderer(al_get_hostname()); } +bool sphere::isRendererMachine() { + return isRendererMachine(al_get_hostname()); +} std::string sphere::renderer_hostname(const std::string &fallback) { auto const host = al_get_hostname(); - if (is_renderer(host)) + if (isRendererMachine(host)) return host; else return fallback; } -bool sphere::is_in_sphere() { +bool sphere::isSphereMachine() { auto const host = al_get_hostname(); - return is_simulator(host) || is_renderer(host); + return isSimulatorMachine(host) || isRendererMachine(host); } -void sphere::get_fullscreen_dimension(int *width, int *height) { +void sphere::getFullscreenDimension(int *width, int *height) { // Window Size in AlloSphere // considering Mosaic'ed displays as one bing screen @@ -54,46 +67,45 @@ void sphere::get_fullscreen_dimension(int *width, int *height) { *width = 0; *height = 0; - assert(0 == 1); - - // FIXME implement - // GLFWmonitor** monitors = glfwGetMonitors(&count); + GLFWmonitor **monitors = glfwGetMonitors(&count); - // for (int i = 0; i < count; i += 1) { - // int x, y; - // glfwGetMonitorPos(monitors[i], &x, &y); - // const GLFWvidmode* vm = glfwGetVideoMode(monitors[i]); - // int xmax = x + vm->width; - // int ymax = y + vm->height; - // if (*width < xmax) *width = xmax; - // if (*height < ymax) *height = ymax; - // } + for (int i = 0; i < count; i += 1) { + int x, y; + glfwGetMonitorPos(monitors[i], &x, &y); + const GLFWvidmode *vm = glfwGetVideoMode(monitors[i]); + int xmax = x + vm->width; + int ymax = y + vm->height; + if (*width < xmax) *width = xmax; + if (*height < ymax) *height = ymax; + } } -std::string sphere::config_directory(const std::string &dir_if_not_renderer) { - if (is_renderer()) +std::string sphere::getCalibrationDirectory( + const std::string &dir_if_not_renderer) { + if (isRendererMachine()) return "/home/sphere/calibration-current/"; else return dir_if_not_renderer; } -std::string sphere::renderer_config_file_path(const std::string &host) { +std::string sphere::getRendererCalibrationFilepath(const std::string &host) { return "/home/sphere/calibration-current/" + host + ".txt"; } -std::string sphere::renderer_config_file_path() { - return renderer_config_file_path(al_get_hostname()); +std::string sphere::getRendererCalibrationFilepath() { + return getRendererCalibrationFilepath(al_get_hostname()); } -std::string sphere::config_file_path(const std::string &path_if_not_renderer) { +std::string sphere::getCalibrationFilepath( + const std::string &path_if_not_renderer) { auto const host = al_get_hostname(); - if (is_renderer(host)) - return renderer_config_file_path(host); + if (isRendererMachine(host)) + return getRendererCalibrationFilepath(host); else return path_if_not_renderer; } -std::vector sphere::generate_equirect_sampletex(int width, int height) { +std::vector sphere::generateEquirectSampletex(int width, int height) { std::vector arr; arr.resize(width * height * 4); for (int i = 0; i < width; i++) { @@ -109,3 +121,29 @@ std::vector sphere::generate_equirect_sampletex(int width, int height) { } return arr; } + +std::map sphere::getSphereNodes() { + std::map nodes = { + {"ar01", + NodeConfiguration{ + 0, 0, "/Volumes/Data", + (Capability)(Capability::CAP_SIMULATOR | Capability::CAP_RENDERING | + Capability::CAP_AUDIO_IO | Capability::CAP_OSC)}}, + {"atari.1g", + NodeConfiguration{ + 0, 0, "e:/", + (Capability)(Capability::CAP_SIMULATOR | Capability::CAP_RENDERING | + Capability::CAP_AUDIO_IO | Capability::CAP_OSC)}}, + + }; + + char str[3]; + for (uint16_t i = 1; i <= 14; i++) { + snprintf(str, 3, "%02d", i); + std::string name = "gr" + std::string(str); + nodes[name] = NodeConfiguration{ + 0, i, "/alloshare", + (Capability)(CAP_SIMULATOR | CAP_OMNIRENDERING | CAP_OSC)}; + }; + return nodes; +} diff --git a/src/types/al_Color.cpp b/src/types/al_Color.cpp index 1b030bb7..dfd85bec 100644 --- a/src/types/al_Color.cpp +++ b/src/types/al_Color.cpp @@ -88,14 +88,14 @@ HSV& HSV::operator=(const RGB& c) { } // RGB operators -RGB& RGB::operator=(const CIEXYZ& v) { +RGB& RGB::operator=(const CIE_XYZ& v) { // using sRGB and reference white D65 static const Mat3f transformMatrix(3.2405f, -1.5371f, -0.4985f, -0.9693f, 1.8760f, 0.0416f, 0.0556f, -0.2040f, 1.0572f); float X = v.x, Y = v.y, Z = v.z; - // convert CIEXYZ to rgb (linear with respect to energy) + // convert CIE_XYZ to rgb (linear with respect to energy) Vec3f xyz(X, Y, Z); Vec3f rgb = transformMatrix * xyz; float rl = rgb[0], gl = rgb[1], bl = rgb[2]; @@ -121,12 +121,13 @@ RGB& RGB::operator=(const CIEXYZ& v) { else if (b < 0.f) b = 0.f; - // cout << "RGB from CIEXYZ: {" << r << ", " << g << ", " << b << "}" << endl; + // cout << "RGB from CIE_XYZ: {" << r << ", " << g << ", " << b << "}" << + // endl; return *this; } -// CIEXYZ operators -CIEXYZ& CIEXYZ::operator=(const RGB& v) { +// CIE_XYZ operators +CIE_XYZ& CIE_XYZ::operator=(const RGB& v) { // using sRGB and reference white D65 static const Mat3f transformMatrix(0.4124f, 0.3576f, 0.1805f, 0.2126f, 0.7152f, 0.0722f, 0.0193f, 0.1192f, @@ -137,18 +138,19 @@ CIEXYZ& CIEXYZ::operator=(const RGB& v) { G = (float)(G <= 0.04045) ? G / 12.92 : pow(((G + 0.055) / 1.055), 2.4); B = (float)(B <= 0.04045) ? B / 12.92 : pow(((B + 0.055) / 1.055), 2.4); - // convert rgb to CIEXYZ + // convert rgb to CIE_XYZ Vec3f rgb(R, G, B); Vec3f xyz = transformMatrix * rgb; x = xyz[0]; y = xyz[1]; z = xyz[2]; - // cout << "CIEXYZ from RGB: {" << x << ", " << y << ", " << z << "}" << endl; + // cout << "CIE_XYZ from RGB: {" << x << ", " << y << ", " << z << "}" << + // endl; return *this; }; -CIEXYZ& CIEXYZ::operator=(const Lab& v) { +CIE_XYZ& CIE_XYZ::operator=(const Lab& v) { float l, a, b, fx, fy, fz, xr, yr, zr; float epsilon = (216.0f / 24389.0f), kappa = (24389.0f / 27.0f); // using reference white D65 @@ -173,11 +175,12 @@ CIEXYZ& CIEXYZ::operator=(const Lab& v) { y = yr * Yn; z = zr * Zn; - // cout << "CIEXYZ from Lab: {" << x << ", " << y << ", " << z << "}" << endl; + // cout << "CIE_XYZ from Lab: {" << x << ", " << y << ", " << z << "}" << + // endl; return *this; } -CIEXYZ& CIEXYZ::operator=(const Luv& w) { +CIE_XYZ& CIE_XYZ::operator=(const Luv& w) { float l, u, v, a, b, c, d, ur, vr; float epsilon = (216.0f / 24389.0f), kappa = (24389.0f / 27.0f); // using reference white D65 @@ -200,18 +203,19 @@ CIEXYZ& CIEXYZ::operator=(const Luv& w) { x = (d - b) / (a - c); z = x * a + b; - // cout << "CIEXYZ from Luv: {" << x << ", " << y << ", " << z << "}" << endl; + // cout << "CIE_XYZ from Luv: {" << x << ", " << y << ", " << z << "}" << + // endl; return *this; } // Lab operators -Lab& Lab::operator=(const CIEXYZ& v) { +Lab& Lab::operator=(const CIE_XYZ& v) { float fx, fy, fz, xr, yr, zr; float epsilon = (216.0f / 24389.0f), kappa = (24389.0f / 27.0f); // using reference white D65 float Xn = 0.95047f, Yn = 1.0f, Zn = 1.08883f; - // convert CIEXYZ to Lab + // convert CIE_XYZ to Lab xr = v.x / Xn; yr = v.y / Yn; zr = v.z / Zn; @@ -253,7 +257,7 @@ HCLab& HCLab::operator=(const Lab& v) { } // Luv operators -Luv& Luv::operator=(const CIEXYZ& w) { +Luv& Luv::operator=(const CIE_XYZ& w) { float up, vp, ur, yr, vr, x, y, z; float epsilon = (216.0f / 24389.0f), kappa = (24389.0f / 27.0f); // using reference white D65 @@ -262,7 +266,7 @@ Luv& Luv::operator=(const CIEXYZ& w) { y = w.y; z = w.z; - // convert CIEXYZ to Luv + // convert CIE_XYZ to Luv ur = (4 * Xn) / (Xn + 15 * Yn + 3 * Zn); yr = w.y / Yn; vr = (9 * Yn) / (Xn + 15 * Yn + 3 * Zn); @@ -301,27 +305,27 @@ HCLuv& HCLuv::operator=(const Luv& w) { } // RGB operators -RGB& RGB::operator=(const Lab& v) { return *this = CIEXYZ(v); } +RGB& RGB::operator=(const Lab& v) { return *this = CIE_XYZ(v); } RGB& RGB::operator=(const HCLab& v) { return *this = Lab(v); } -RGB& RGB::operator=(const Luv& v) { return *this = CIEXYZ(v); } +RGB& RGB::operator=(const Luv& v) { return *this = CIE_XYZ(v); } RGB& RGB::operator=(const HCLuv& v) { return *this = Luv(v); } // HSV operators -HSV& HSV::operator=(const CIEXYZ& v) { return *this = RGB(v); } +HSV& HSV::operator=(const CIE_XYZ& v) { return *this = RGB(v); } -HSV& HSV::operator=(const Lab& v) { return *this = CIEXYZ(v); } +HSV& HSV::operator=(const Lab& v) { return *this = CIE_XYZ(v); } HSV& HSV::operator=(const HCLab& v) { return *this = Lab(v); } -HSV& HSV::operator=(const Luv& v) { return *this = CIEXYZ(v); } +HSV& HSV::operator=(const Luv& v) { return *this = CIE_XYZ(v); } HSV& HSV::operator=(const HCLuv& v) { return *this = Luv(v); } // Color operators -Color& Color::operator=(const CIEXYZ& v) { return *this = RGB(v); } +Color& Color::operator=(const CIE_XYZ& v) { return *this = RGB(v); } Color& Color::operator=(const Lab& v) { return *this = RGB(v); } @@ -332,7 +336,7 @@ Color& Color::operator=(const Luv& v) { return *this = RGB(v); } Color& Color::operator=(const HCLuv& v) { return *this = RGB(v); } // Colori operators -Colori& Colori::operator=(const CIEXYZ& v) { return *this = RGB(v); } +Colori& Colori::operator=(const CIE_XYZ& v) { return *this = RGB(v); } Colori& Colori::operator=(const Lab& v) { return *this = RGB(v); } diff --git a/src/ui/al_Composition.cpp b/src/ui/al_Composition.cpp index 50e8b725..6aacd49f 100644 --- a/src/ui/al_Composition.cpp +++ b/src/ui/al_Composition.cpp @@ -1,10 +1,11 @@ +#include "al/ui/al_Composition.hpp" + #include #include #include #include "al/io/al_File.hpp" #include "al/system/al_Time.hpp" -#include "al/ui/al_Composition.hpp" using namespace al; @@ -78,83 +79,84 @@ bool Composition::playArchive(std::string archiveName) { std::string Composition::getName() { return mCompositionName; } -bool Composition::archiveComposition() { - std::string path = getCurrentPath(); - std::string compositionName = mCompositionName; - bool ok = true; - - std::string fullPath = path; - fullPath += compositionName + ".composition"; - - std::string archivePath = fullPath; - if (archivePath.size() > 8 && - archivePath.substr(archivePath.size() - 8) != "_archive") { - archivePath += "_archive"; - } - - if (File::isDirectory(archivePath)) { - if (!Dir::removeRecursively(archivePath)) { - std::cerr << "Error removing directory: " << fullPath - << " aborting composition archiving." << std::endl; - return false; - } - if (!Dir::make(archivePath)) { - std::cerr << "Error creating composition archive directory " << fullPath - << std::endl; - return false; - } - - } else if (!File::exists(archivePath)) { - if (!Dir::make(archivePath)) { - std::cerr << "Error creating compostion archive directory " << archivePath - << std::endl; - return false; - } - - } else { - // TODO generate new name instead of error - std::cerr << "compostion directory name taken by file: " << archivePath - << std::endl; - return false; - } - - auto steps = loadCompositionSteps(compositionName); - - for (auto step : steps) { - std::cout << step.sequenceName << ":" << step.deltaTime << std::endl; - PresetSequencer sequencer; - sequencer.setDirectory(path); - std::queue sequenceSteps = - sequencer.loadSequence(step.sequenceName); - if (!File::copy(path + step.sequenceName + ".sequence", - archivePath + "/" + step.sequenceName + ".sequence")) { - std::cerr << "Error copying sequence: " << step.sequenceName - << " when archiving composition." << std::endl; - ok = false; - } - while (sequenceSteps.size() > 0) { - PresetSequencer::Step &sequenceStep = sequenceSteps.front(); - if (!File::exists(archivePath + "/" + sequenceStep.presetName + - ".preset") && - !File::copy( - path + sequenceStep.presetName + ".preset", - archivePath + "/" + sequenceStep.presetName + ".preset")) { - std::cerr << "Error copying preset: " << sequenceStep.presetName - << " when archiving composition." << std::endl; - ok = false; - } - sequenceSteps.pop(); - } - } - if (!File::copy(fullPath, - archivePath + "/" + compositionName + ".composition")) { - std::cerr << "Error copying composition: " << compositionName - << " when archiving composition." << std::endl; - ok = false; - } - - return ok; -} +// bool Composition::archiveComposition() { +// std::string path = getCurrentPath(); +// std::string compositionName = mCompositionName; +// bool ok = true; + +// std::string fullPath = path; +// fullPath += compositionName + ".composition"; + +// std::string archivePath = fullPath; +// if (archivePath.size() > 8 && +// archivePath.substr(archivePath.size() - 8) != "_archive") { +// archivePath += "_archive"; +// } + +// if (File::isDirectory(archivePath)) { +// if (!Dir::removeRecursively(archivePath)) { +// std::cerr << "Error removing directory: " << fullPath +// << " aborting composition archiving." << std::endl; +// return false; +// } +// if (!Dir::make(archivePath)) { +// std::cerr << "Error creating composition archive directory " << fullPath +// << std::endl; +// return false; +// } + +// } else if (!File::exists(archivePath)) { +// if (!Dir::make(archivePath)) { +// std::cerr << "Error creating compostion archive directory " << +// archivePath +// << std::endl; +// return false; +// } + +// } else { +// // TODO generate new name instead of error +// std::cerr << "compostion directory name taken by file: " << archivePath +// << std::endl; +// return false; +// } + +// auto steps = loadCompositionSteps(compositionName); + +// for (auto step : steps) { +// std::cout << step.sequenceName << ":" << step.deltaTime << std::endl; +// PresetSequencer sequencer; +// sequencer.setDirectory(path); +// std::queue sequenceSteps = +// sequencer.loadSequence(step.sequenceName); +// if (!File::copy(path + step.sequenceName + ".sequence", +// archivePath + "/" + step.sequenceName + ".sequence")) { +// std::cerr << "Error copying sequence: " << step.sequenceName +// << " when archiving composition." << std::endl; +// ok = false; +// } +// while (sequenceSteps.size() > 0) { +// PresetSequencer::Step &sequenceStep = sequenceSteps.front(); +// if (!File::exists(archivePath + "/" + sequenceStep.presetName + +// ".preset") && +// !File::copy( +// path + sequenceStep.presetName + ".preset", +// archivePath + "/" + sequenceStep.presetName + ".preset")) { +// std::cerr << "Error copying preset: " << sequenceStep.presetName +// << " when archiving composition." << std::endl; +// ok = false; +// } +// sequenceSteps.pop(); +// } +// } +// if (!File::copy(fullPath, +// archivePath + "/" + compositionName + ".composition")) { +// std::cerr << "Error copying composition: " << compositionName +// << " when archiving composition." << std::endl; +// ok = false; +// } + +// return ok; +//} bool Composition::archive(std::string compositionName, std::string path, bool overwrite) { @@ -320,17 +322,6 @@ std::string Composition::getCurrentPath() { return path; } -void Composition::waitForSequencerCallback(bool finished, PresetSequencer *, - void *userData) { - Composition *comp = static_cast(userData); - comp->mSequencer->mEndCallback = comp->mSequencerEndCallbackCache; - comp->mSequencer->mEndCallbackData = comp->mSequencerEndCallbackDataCache; - - if (comp->mEndCallbackEnabled && comp->mEndCallback != nullptr) { - comp->mEndCallback(finished, comp, comp->mEndCallbackData); - } -} - void Composition::playbackThread(Composition *composition) { size_t index = 0; if (composition->mCompositionSteps.size() == 0) { @@ -357,8 +348,6 @@ void Composition::playbackThread(Composition *composition) { composition->mPlayerLock.lock(); composition->mSequencerEndCallbackCache = composition->mSequencer->mEndCallback; - composition->mSequencerEndCallbackDataCache = - composition->mSequencer->mEndCallbackData; auto sequenceStart = std::chrono::high_resolution_clock::now(); auto targetTime = sequenceStart; while (composition->mPlaying) { @@ -399,7 +388,16 @@ void Composition::playbackThread(Composition *composition) { // here if playback stops between the check and the // branches composition->mSequencer->registerEndCallback( - Composition::waitForSequencerCallback, composition); + [&](bool finished, PresetSequencer *) { + composition->mSequencer->mEndCallback = + composition->mSequencerEndCallbackCache; + + if (composition->mEndCallbackEnabled && + composition->mEndCallback != nullptr) { + composition->mEndCallback(finished, composition, + composition->mEndCallbackData); + } + }); } else { if (composition->mEndCallbackEnabled && composition->mEndCallback != nullptr) { diff --git a/src/ui/al_ControlGUI.cpp b/src/ui/al_ControlGUI.cpp index 5f27ecd5..adf5098e 100644 --- a/src/ui/al_ControlGUI.cpp +++ b/src/ui/al_ControlGUI.cpp @@ -1,12 +1,12 @@ #include "al/ui/al_ControlGUI.hpp" -#include "al/graphics/al_Graphics.hpp" +#include "al/graphics/al_Graphics.hpp" #include "al/ui/al_SequenceRecorder.hpp" using namespace al; using namespace std; -void ControlGUI::draw(Graphics &g) { +void ControlGUI::draw(Graphics & /*g*/) { auto separatorAnchor = mSeparatorAnchors.begin(); auto groupBeginAnchor = mGroupBeginAnchors.begin(); auto groupNamesIt = mGroupNames.begin(); diff --git a/src/ui/al_FileSelector.cpp b/src/ui/al_FileSelector.cpp index 18ed671c..ca7a8722 100644 --- a/src/ui/al_FileSelector.cpp +++ b/src/ui/al_FileSelector.cpp @@ -1,20 +1,23 @@ #include "al/ui/al_FileSelector.hpp" + #include "al/io/al_Imgui.hpp" using namespace al; FileSelector::FileSelector(std::string globalRoot, - std::function function) { - if (function) { - mFilterFunc = function; + std::function filterfunction) { + mFilterFunc = filterfunction; + if (globalRoot.size() > 0) { + mGlobalRoot = File::conformPathToOS(globalRoot); } else { - mFilterFunc = [](std::string) { return true; }; } - mGlobalRoot = File::conformPathToOS(globalRoot); } void FileSelector::start(std::string currentDir) { + if (currentDir.size() == 0) { + currentDir = File::currentPath(); + } mCurrentDir = currentDir; mSelectedItem = ""; auto boundFunc = std::bind(&FileSelector::filteringFunctionWrapper, this, @@ -30,6 +33,7 @@ bool FileSelector::drawFileSelector() { std::string rootButtonText = mGlobalRoot; + ImGui::PushID((void *)this); if (rootButtonText.size() != 0) { // Global root set. ImGui::Text("Global root:%s", rootButtonText.c_str()); } @@ -64,7 +68,8 @@ bool FileSelector::drawFileSelector() { // return File::isDirectory(rootButtonText + mCurrentDir + "/" + // fp.file());}); // } - bool itemClicked = false; + bool enterDirectory = false; + bool applyItem = false; #ifdef AL_WINDOWS // show drive names for windows. if (items.count() == 0 && rootButtonText + mCurrentDir == "") { @@ -93,24 +98,38 @@ bool FileSelector::drawFileSelector() { } if (File::isDirectory(newPath)) { mCurrentDir = newPath; + enterDirectory = true; + mSelectedItem = ""; +#ifdef AL_WINDOWS + } else if (newPath.size() == 2 && newPath[1] == ':') { + // For Windows drives + mCurrentDir = newPath; + enterDirectory = true; + mSelectedItem = ""; +#endif + } else { + applyItem = true; } - mSelectedItem = ""; - itemClicked = true; } } - if (itemClicked) { + if (enterDirectory) { auto boundFunc = std::bind(&FileSelector::filteringFunctionWrapper, this, std::placeholders::_1); items = filterInDir(rootButtonText + mCurrentDir, boundFunc); } - // FIXME push unique ids for these - if (ImGui::Button("Select##Directory")) { + if (ImGui::Button("Select##Directory") || applyItem) { + mActive = false; + ImGui::PopID(); + if (mSelectedItem == "") { + return false; + } return true; } ImGui::SameLine(); if (ImGui::Button("Cancel##Directory")) { mActive = false; } + ImGui::PopID(); return false; } diff --git a/src/ui/al_Parameter.cpp b/src/ui/al_Parameter.cpp index b84a901b..c28da25d 100644 --- a/src/ui/al_Parameter.cpp +++ b/src/ui/al_Parameter.cpp @@ -1,11 +1,12 @@ +#include "al/ui/al_Parameter.hpp" + #include #include #include #include #include "al/io/al_File.hpp" -#include "al/ui/al_Parameter.hpp" using namespace al; @@ -33,9 +34,7 @@ void Parameter::setNoCalls(float value, void *blockReceiver) { value = (*mProcessCallback)(value); //, mProcessUdata); } if (blockReceiver) { - for (auto cb : mCallbacks) { - (*cb)(value); - } + runChangeCallbacksSynchronous(value); } mFloatValue = value; @@ -47,9 +46,8 @@ void Parameter::set(float value) { if (mProcessCallback) { value = (*mProcessCallback)(value); //, mProcessUdata); } - for (auto cb : mCallbacks) { - (*cb)(value); - } + + runChangeCallbacksSynchronous(value); mFloatValue = value; } @@ -72,9 +70,7 @@ void ParameterInt::setNoCalls(int32_t value, void *blockReceiver) { value = (*mProcessCallback)(value); //, mProcessUdata); } if (blockReceiver) { - for (auto cb : mCallbacks) { - (*cb)(value); - } + runChangeCallbacksSynchronous(value); } mIntValue = value; @@ -86,9 +82,8 @@ void ParameterInt::set(int32_t value) { if (mProcessCallback) { value = (*mProcessCallback)(value); //, mProcessUdata); } - for (auto cb : mCallbacks) { - (*cb)(value); - } + + runChangeCallbacksSynchronous(value); mIntValue = value; } diff --git a/src/ui/al_ParameterBundle.cpp b/src/ui/al_ParameterBundle.cpp index 87e455cc..88ed4697 100644 --- a/src/ui/al_ParameterBundle.cpp +++ b/src/ui/al_ParameterBundle.cpp @@ -1,9 +1,10 @@ +#include "al/ui/al_ParameterBundle.hpp" + #include #include #include "al/protocol/al_OSC.hpp" -#include "al/ui/al_ParameterBundle.hpp" #include "al/ui/al_ParameterServer.hpp" using namespace al; @@ -153,9 +154,9 @@ void ParameterBundle::addBundle(ParameterBundle &bundle, std::string id) { id = std::to_string(mBundles.size()); } if (mBundles.find(id) != mBundles.end()) { - std::cerr << "ERROR: Overwriting bundle id: " << id << std::endl; + mBundles[id] = std::vector(); } - mBundles[id] = &bundle; + mBundles[id].push_back(&bundle); bundle.mBundleId = id; bundle.mParentPrefix = bundlePrefix(); } @@ -173,6 +174,8 @@ ParameterBundle &ParameterBundle::operator<<(ParameterMeta ¶meter) { void ParameterBundle::addNotifier(OSCNotifier *notifier) { mNotifiers.push_back(notifier); for (auto subBundleGroup : bundles()) { - subBundleGroup.second->addNotifier(notifier); + for (auto *bundle : subBundleGroup.second) { + bundle->addNotifier(notifier); + } } } diff --git a/src/ui/al_ParameterGUI.cpp b/src/ui/al_ParameterGUI.cpp index 421b0bbe..6e5a2b4a 100644 --- a/src/ui/al_ParameterGUI.cpp +++ b/src/ui/al_ParameterGUI.cpp @@ -1,7 +1,7 @@ -#include - #include "al/ui/al_ParameterGUI.hpp" +#include + using namespace al; using namespace std; @@ -679,6 +679,7 @@ void ParameterGUI::drawPresetSequencer(PresetSequencer *presetSequencer, struct SequencerState { float currentTime; float totalDuration; + float startOffset = 0.0f; std::vector seqList; }; static std::map stateMap; @@ -686,12 +687,11 @@ void ParameterGUI::drawPresetSequencer(PresetSequencer *presetSequencer, stateMap[presetSequencer] = SequencerState{0.0, 0.0, {}}; float *currentTime = &(stateMap[presetSequencer].currentTime); presetSequencer->registerTimeChangeCallback( - [currentTime](float currTime) { *currentTime = currTime; }, 0.1f); - presetSequencer->registerBeginCallback( - [&](PresetSequencer *sender, void * /*userData*/) { - stateMap[presetSequencer].totalDuration = - sender->getSequenceTotalDuration(sender->currentSequence()); - }); + [currentTime](float currTime) { *currentTime = currTime; }, 0.05f); + presetSequencer->registerBeginCallback([&](PresetSequencer *sender) { + stateMap[presetSequencer].totalDuration = + sender->getSequenceTotalDuration(sender->currentSequence()); + }); stateMap[presetSequencer].seqList = presetSequencer->getSequenceList(); if (stateMap[presetSequencer].seqList.size() > 64) { stateMap[presetSequencer].seqList.resize(64); @@ -705,6 +705,10 @@ void ParameterGUI::drawPresetSequencer(PresetSequencer *presetSequencer, << std::endl; presetSequencer->loadSequence( stateMap[presetSequencer].seqList[currentPresetSequencerItem]); + + stateMap[presetSequencer].startOffset = + presetSequencer->getSequenceStartOffset( + stateMap[presetSequencer].seqList[currentPresetSequencerItem]); stateMap[presetSequencer].totalDuration = presetSequencer->getSequenceTotalDuration( stateMap[presetSequencer].seqList[currentPresetSequencerItem]); @@ -729,6 +733,8 @@ void ParameterGUI::drawPresetSequencer(PresetSequencer *presetSequencer, (int)stateMap[presetSequencer].seqList.size()) { presetSequencer->loadSequence( stateMap[presetSequencer].seqList[currentPresetSequencerItem]); + state.startOffset = presetSequencer->getSequenceStartOffset( + stateMap[presetSequencer].seqList[currentPresetSequencerItem]); state.totalDuration = presetSequencer->getSequenceTotalDuration( stateMap[presetSequencer].seqList[currentPresetSequencerItem]); } @@ -736,32 +742,37 @@ void ParameterGUI::drawPresetSequencer(PresetSequencer *presetSequencer, // presetSequencer->getSequenceTotalDuration(seqList[currentPresetSequencerItem]); } if (ImGui::Button("Play")) { - presetSequencer->stopSequence(); - if (presetSequencer->playbackFinished()) { - presetSequencer->setTime(0.0); - } - if (currentPresetSequencerItem >= 0) { - double sliderTime = state.currentTime; - presetSequencer->playSequence( - stateMap[presetSequencer].seqList[currentPresetSequencerItem]); - presetSequencer->setTime(sliderTime); - } else { - std::cout << "No sequence selected for playback." << std::endl; + if (!presetSequencer->running()) { + presetSequencer->stopSequence(); + if (presetSequencer->playbackFinished()) { + if (state.currentTime != 0) { + presetSequencer->setTime(-state.startOffset); + } + } + if (currentPresetSequencerItem >= 0) { + double sliderTime = state.currentTime; + presetSequencer->playSequence( + stateMap[presetSequencer].seqList[currentPresetSequencerItem], + 1.0, sliderTime - state.startOffset); + } else { + std::cout << "No sequence selected for playback." << std::endl; + } } } ImGui::SameLine(); if (ImGui::Button("Pause")) { - presetSequencer->stopSequence(); + presetSequencer->stopSequence(false); } ImGui::SameLine(); if (ImGui::Button("Stop")) { presetSequencer->stopSequence(); - presetSequencer->rewind(); + presetSequencer->setTime(stateMap[presetSequencer].totalDuration); } float time = state.currentTime; // std::cout << time << std::endl; - if (ImGui::SliderFloat("Position", &time, 0.0f, state.totalDuration)) { - // std::cout << "Requested time:" << time << std::endl; + if (ImGui::SliderFloat("Position", &time, -state.startOffset, + state.totalDuration - state.startOffset)) { + // std::cout << "Requested time:" << time << std::endl; presetSequencer->setTime(time); } } else { @@ -1169,13 +1180,15 @@ void ParameterGUI::drawBundleGroup(std::vector bundleGroup, for (ParameterMeta *param : bundleGroup[currentBundle]->parameters()) { drawParameterMeta(param, suffix); } - for (auto bundle : bundleGroup[currentBundle]->bundles()) { - std::string subBundleName = bundle.first; + for (auto subbundleGroup : bundleGroup[currentBundle]->bundles()) { + std::string subBundleName = subbundleGroup.first; if (ImGui::CollapsingHeader( (subBundleName + "##" + name + subBundleName).c_str(), ImGuiTreeNodeFlags_CollapsingHeader)) { - for (auto *param : bundle.second->parameters()) { - drawParameterMeta({param}, suffix + subBundleName, 0); + for (auto *bundle : subbundleGroup.second) { + for (auto *param : bundle->parameters()) { + drawParameterMeta({param}, suffix + subBundleName, 0); + } } } } @@ -1198,8 +1211,10 @@ void ParameterGUI::drawBundle(ParameterBundle *bundle) { ImGui::PushID(subBundleName.c_str()); if (ImGui::CollapsingHeader(subBundleName.c_str(), ImGuiTreeNodeFlags_CollapsingHeader)) { - for (auto *param : innerBundle.second->parameters()) { - drawParameterMeta({param}, subBundleName, 0); + for (auto *bundle : innerBundle.second) { + for (auto *param : bundle->parameters()) { + drawParameterMeta({param}, subBundleName, 0); + } } } ImGui::PopID(); diff --git a/src/ui/al_ParameterServer.cpp b/src/ui/al_ParameterServer.cpp index 8b3ccf99..0bdd7f6c 100644 --- a/src/ui/al_ParameterServer.cpp +++ b/src/ui/al_ParameterServer.cpp @@ -1,10 +1,10 @@ +#include "al/ui/al_ParameterServer.hpp" + #include #include #include -#include "al/ui/al_ParameterServer.hpp" - using namespace al; // OSCNotifier implementation ------------------------------------------------- @@ -691,7 +691,10 @@ void ParameterServer::printBundleInfo(ParameterBundle *bundle, std::string id, printParameterInfo(p); } for (auto bundleGroup : bundle->bundles()) { - printBundleInfo(bundleGroup.second, bundleGroup.first, depth); + std::cout << " --- Subbundle: " << bundleGroup.first << std::endl; + for (auto *bundle : bundleGroup.second) { + printBundleInfo(bundle, bundleGroup.first, depth); + } } for (int i = 0; i < depth - 1; i++) std::cout << " "; std::cout << "--- End Bundle: " << bundle->name() << " id: " << id diff --git a/src/ui/al_PresetHandler.cpp b/src/ui/al_PresetHandler.cpp index ad09a03b..1ac6403b 100644 --- a/src/ui/al_PresetHandler.cpp +++ b/src/ui/al_PresetHandler.cpp @@ -1,4 +1,6 @@ +#include "al/ui/al_PresetHandler.hpp" + #include #include #include @@ -7,37 +9,36 @@ #include #include "al/io/al_File.hpp" -#include "al/ui/al_PresetHandler.hpp" using namespace al; // PresetHandler -------------------------------------------------------------- PresetHandler::PresetHandler(std::string rootDirectory, bool verbose) + : mVerbose(verbose), mRootDir(rootDirectory) { + setCurrentPresetMap("default"); + setRootPath(rootDirectory); + + if (mTimeMasterMode == TimeMasterMode::TIME_MASTER_CPU) { + mCpuThreadRunning = true; + mMorphingThread = + std::make_unique(PresetHandler::morphingFunction, this); + } +} + +PresetHandler::PresetHandler(TimeMasterMode timeMasterMode, + std::string rootDirectory, bool verbose) : mVerbose(verbose), - mUseCallbacks(true), mRootDir(rootDirectory), - mRunning(true), - mMorphRemainingSteps(-1), - mMorphInterval(0.05f), - mMorphTime("morphTime", "", 0.0, "", 0.0, 20.0), - mMorphingThread(PresetHandler::morphingFunction, this) { - if (!File::exists(rootDirectory)) { - if (!Dir::make(rootDirectory)) { - std::cout << "Error creating directory: " << rootDirectory << std::endl; - } - } + mTimeMasterMode(timeMasterMode) { setCurrentPresetMap("default"); + setRootPath(rootDirectory); + if (mTimeMasterMode == TimeMasterMode::TIME_MASTER_CPU) { + startCpuThread(); + } } -PresetHandler::~PresetHandler() { - stopMorph(); - mRunning = false; - mMorphConditionVar.notify_all(); - // mMorphLock.lock(); - mMorphingThread.join(); - // mMorphLock.unlock(); -} +PresetHandler::~PresetHandler() { stopCpuThread(); } void PresetHandler::setSubDirectory(std::string directory) { std::string path = getRootPath(); @@ -179,10 +180,14 @@ void PresetHandler::storePreset(int index, std::string name, bool overwrite) { p->get(fields); values[bundlePrefix + p->getFullAddress()] = fields; } - for (auto subBundle : bundleGroup.second.at(i)->bundles()) { - auto bundleStates = getBundleStates(subBundle.second, subBundle.first); - for (auto &bundleValues : bundleStates) { - values[bundlePrefix + "/" + bundleValues.first] = bundleValues.second; + // FIXME enable recursive nesting for bundles + for (auto subBundleGroup : bundleGroup.second.at(i)->bundles()) { + for (auto *bundle : subBundleGroup.second) { + auto bundleStates = getBundleStates(bundle, subBundleGroup.first); + for (auto &bundleValues : bundleStates) { + values[bundlePrefix + "/" + bundleValues.first] = + bundleValues.second; + } } } } @@ -204,15 +209,48 @@ void PresetHandler::storePreset(int index, std::string name, bool overwrite) { } void PresetHandler::recallPreset(std::string name) { - { - if (mMorphRemainingSteps.load() >= 0) { - mMorphRemainingSteps.store(-1); - std::lock_guard lk(mTargetLock); - } - mTargetValues = loadPresetValues(name); - mMorphRemainingSteps.store(1.0f + ceilf(mMorphTime.get() / mMorphInterval)); - } - mMorphConditionVar.notify_one(); + morphTo(name, mMorphTime.get()); + // { + // if (mTotalSteps.load() >= 0) { + // mTotalSteps.store(1); + + // } + // std::lock_guard lk(mTargetLock); + // mTargetValues = loadPresetValues(name); + // mStartValues.clear(); + // // FIXME morph recursively inside bundles + // for (auto targetValue : mTargetValues) { + // bool valueSet = false; + // for (auto *param : mParameters) { + // if (param->getFullAddress() == targetValue.first) { + // mStartValues[param->getFullAddress()] = + // std::vector(); + // param->get(mStartValues[param->getFullAddress()]); + // valueSet = true; + // break; + // } + // } + // for (auto bundleGroup : mBundles) { + // for (size_t i = 0; i < bundleGroup.second.size(); i++) { + // std::string bundlePrefix = + // "/" + bundleGroup.first + "/" + std::to_string(i); + // for (auto *param : bundleGroup.second.at(i)->parameters()) { + // if (bundlePrefix + param->getFullAddress() == targetValue.first) + // { + // mStartValues[bundlePrefix + param->getFullAddress()] = + // std::vector(); + // param->get(mStartValues[bundlePrefix + + // param->getFullAddress()]); valueSet = true; break; + // } + // } + // } + // } + // if (!valueSet) { + // mStartValues[targetValue.first] = targetValue.second; + // } + // } + // } + int index = -1; for (auto preset : mPresetsMap) { if (preset.second == name) { @@ -232,64 +270,20 @@ void PresetHandler::recallPreset(std::string name) { void PresetHandler::setInterpolatedPreset(std::string presetName1, std::string presetName2, - double factor, bool synchronous) { + double factor) { ParameterStates values1 = loadPresetValues(presetName1); ParameterStates values2 = loadPresetValues(presetName2); - if (synchronous) { - for (auto value : values1) { - if (values2.count(value.first) > - 0) { // if para std::cout << meter name match exists - mTargetValues[value.first] = value.second; - for (ParameterMeta *param : mParameters) { - if (param->getFullAddress() == value.first) { - std::vector newValues; - for (unsigned int index = 0; index < value.second.size(); index++) { - if (value.second[index].type() == ParameterField::FLOAT) { - newValues.push_back(value.second[index].get() + - (values2[value.first][index].get() - - value.second[index].get()) * - factor); - } else { - newValues.push_back(value.second[index]); - } - } - setParameterValues(param, newValues, factor); - } - } - } - } - } else { - if (mMorphRemainingSteps.load() >= 0) { - mMorphRemainingSteps.store(-1); - std::lock_guard lk( - mTargetLock); // Wait for morph function loop to process - } - mTargetValues.clear(); - for (auto value : values1) { - if (values2.count(value.first) > - 0) { // if para std::cout << meter name match exists - mTargetValues[value.first] = value.second; - for (unsigned int index = 0; index < value.second.size(); index++) { - mTargetValues[value.first][index] = - value.second[index].get() + - (values2[value.first][index].get() - - value.second[index].get()) * - factor; - } - } - } - } - mMorphConditionVar.notify_one(); + // if (synchronous) { + setInterpolatedValues(values1, values2, factor); } -void PresetHandler::setInterpolatedPreset(int index1, int index2, double factor, - bool synchronous) { +void PresetHandler::setInterpolatedPreset(int index1, int index2, + double factor) { auto presetNameIt1 = mPresetsMap.find(index1); auto presetNameIt2 = mPresetsMap.find(index2); if (presetNameIt1 != mPresetsMap.end() && presetNameIt2 != mPresetsMap.end()) { - setInterpolatedPreset(presetNameIt1->second, presetNameIt2->second, factor, - synchronous); + setInterpolatedPreset(presetNameIt1->second, presetNameIt2->second, factor); } else { std::cout << "Invalid indeces for preset interpolation: " << index1 << "," << index2 << std::endl; @@ -298,30 +292,57 @@ void PresetHandler::setInterpolatedPreset(int index1, int index2, double factor, void PresetHandler::morphTo(ParameterStates ¶meterStates, float morphTime) { { - if (mMorphRemainingSteps.load() >= 0) { - mMorphRemainingSteps.store(-1); - std::lock_guard lk(mTargetLock); - } + std::lock_guard lk(mTargetLock); mMorphTime.set(morphTime); mTargetValues = parameterStates; - mMorphRemainingSteps.store(1 + ceil(mMorphTime.get() / mMorphInterval)); + for (ParameterMeta *param : mParameters) { + if (mTargetValues.find(param->getFullAddress()) != mTargetValues.end()) { + std::vector params; + param->get(params); + mStartValues[param->getFullAddress()] = params; + } + } + + std::function, std::string)> + processBundleGroup = [&](std::vector bundles, + std::string prefix) { + for (unsigned int i = 0; i < bundles.size(); i++) { + std::string bundlePrefix = prefix + std::to_string(i); + for (auto *param : bundles.at(i)->parameters()) { + if (mTargetValues.find(bundlePrefix + param->getFullAddress()) != + mTargetValues.end()) { + mStartValues[bundlePrefix + param->getFullAddress()] = + std::vector(); + param->get( + mStartValues[bundlePrefix + param->getFullAddress()]); + } + } + for (auto bundleGroup : bundles.at(i)->bundles()) { + prefix += "/" + bundleGroup.first + "/"; + processBundleGroup({bundleGroup.second}, prefix); + } + } + }; + + for (auto bundleGroup : mBundles) { + std::string prefix = "/" + bundleGroup.first + "/"; + processBundleGroup(bundleGroup.second, prefix); + } + + mMorphStepCount = 0; + if (mMorphTime.get() <= 0.0) { + mTotalSteps.store(1); + } else { + mTotalSteps.store(ceilf(mMorphTime.get() / mMorphInterval)); + } } - mMorphConditionVar.notify_one(); - // int index = -1; - // for (auto preset: mPresetsMap) { - // if (preset.second == name) { - // index = preset.first; - // break; - // } - // } + mCurrentPresetName = ""; - // if (mUseCallbacks) { - // for(int i = 0; i < mCallbacks.size(); ++i) { - // if (mCallbacks[i]) { - // mCallbacks[i](index, this, mCallbackUdata[i]); - // } - // } - // } +} + +void PresetHandler::morphTo(std::string presetName, float morphTime) { + auto parameterStates = loadPresetValues(presetName); + morphTo(parameterStates, morphTime); } std::string PresetHandler::recallPreset(int index) { @@ -337,15 +358,19 @@ std::string PresetHandler::recallPreset(int index) { void PresetHandler::recallPresetSynchronous(std::string name) { { - if (mMorphRemainingSteps.load() >= 0) { - mMorphRemainingSteps.store(-1); - std::lock_guard lk(mTargetLock); - } + // if (mMorphRemainingSteps.load() > 0) { + // mMorphRemainingSteps.store(0); + // mTotalSteps.store(0); + // } + mTotalSteps = 0; + mMorphStepCount = 0; + std::lock_guard lk(mTargetLock); mTargetValues = loadPresetValues(name); } + // FIXME recall bundles for (ParameterMeta *param : mParameters) { if (mTargetValues.find(param->getFullAddress()) != mTargetValues.end()) { - setParameterValues(param, mTargetValues[param->getFullAddress()]); + param->set(mTargetValues[param->getFullAddress()]); } } int index = -1; @@ -417,16 +442,21 @@ float PresetHandler::getMorphTime() { return mMorphTime.get(); } void PresetHandler::setMorphTime(float time) { mMorphTime.set(time); } -void PresetHandler::stopMorph() { - { - if (mMorphRemainingSteps.load() >= 0) { - mMorphRemainingSteps.store(-1); - } - } - { - std::lock_guard lk(mTargetLock); - mMorphConditionVar.notify_all(); +// void PresetHandler::stopMorph() { + +// mCurrentMorphIndex = 0.0; +// if (mMorphRemainingSteps.load() >= 0) { +// mMorphRemainingSteps.store(0); +// mTotalSteps.store(0); +// } +//} + +void PresetHandler::stepMorphing(double stepTime) { + double drift = mMorphInterval - stepTime; + if (drift > 0.01) { + std::cout << "Time drift = " << drift << std::endl; } + stepMorphing(); } std::string PresetHandler::getCurrentPath() { @@ -505,7 +535,7 @@ std::map PresetHandler::readPresetMap(std::string mapName) { if (f.bad()) { if (mVerbose) { std::cout << "Error while opening preset map file for reading: " - << mFileName << std::endl; + << mapFullPath << std::endl; } } return presetsMap; @@ -574,6 +604,139 @@ void PresetHandler::changeParameterValue(std::string presetName, savePresetValues(parameters, presetName, true); } +int PresetHandler::asciiToPresetIndex(int ascii, int offset) { + int index = -1; + + switch (ascii) { + case '1': + index = 0; + break; + case '2': + index = 1; + break; + case '3': + index = 2; + break; + case '4': + index = 3; + break; + case '5': + index = 4; + break; + case '6': + index = 5; + break; + case '7': + index = 6; + break; + case '8': + index = 7; + break; + case '9': + index = 8; + break; + case '0': + index = 9; + break; + case 'q': + index = 10; + break; + case 'w': + index = 11; + break; + case 'e': + index = 12; + break; + case 'r': + index = 13; + break; + case 't': + index = 14; + break; + case 'y': + index = 15; + break; + case 'u': + index = 16; + break; + case 'i': + index = 17; + break; + case 'o': + index = 18; + break; + case 'p': + index = 19; + break; + case 'a': + index = 20; + break; + case 's': + index = 21; + break; + case 'd': + index = 22; + break; + case 'f': + index = 23; + break; + case 'g': + index = 24; + break; + case 'h': + index = 25; + break; + case 'j': + index = 26; + break; + case 'k': + index = 27; + break; + case 'l': + index = 28; + break; + case ';': + index = 29; + break; + ; + case 'z': + index = 30; + break; + case 'x': + index = 31; + break; + case 'c': + index = 32; + break; + case 'v': + index = 33; + break; + case 'b': + index = 34; + break; + case 'n': + index = 35; + break; + case 'm': + index = 36; + break; + case ',': + index = 37; + break; + case '.': + index = 38; + break; + case '/': + index = 39; + break; + } + if (index >= 0) { + index += offset; + } + + return index; +} + void PresetHandler::storeCurrentPresetMap(std::string mapName, bool useSubDirectory) { if (mapName.size() > 0) { @@ -595,229 +758,267 @@ void PresetHandler::storeCurrentPresetMap(std::string mapName, f << "::" << std::endl; if (f.bad()) { if (mVerbose) { - std::cout << "Error while writing preset map file: " << mFileName + std::cout << "Error while writing preset map file: " << mapFullPath << std::endl; } } f.close(); } -void PresetHandler::setParameterValues(ParameterMeta *p, - std::vector &values, - double factor) { - // We do a runtime check to determine the type of the parameter to determine - // how to draw it. - if (factor == 1.0f) { - p->set(values); - } else { - // TODO this is a fallback for now. What would be a good way of doing it? - if (strcmp(typeid(*p).name(), typeid(ParameterBool).name()) == - 0) { // ParameterBool - ParameterBool *param = dynamic_cast(p); - // No interpolation for parameter bool. Should we change exactly in the - // middle? - param->set(values[0].get()); - } else if (strcmp(typeid(*p).name(), typeid(Parameter).name()) == - 0) { // Parameter - Parameter *param = dynamic_cast(p); - float paramValue = param->get(); - float difference = values[0].get() - paramValue; - // int steps = handler->mMorphRemainingSteps.load(); // factor = 1.0/steps - if (factor > 0) { - difference = difference * factor; - } - if (difference != 0.0) { - float newVal = paramValue + difference; - param->set(newVal); - } - } else if (strcmp(typeid(*p).name(), typeid(ParameterInt).name()) == - 0) { // ParameterInt - ParameterInt *param = dynamic_cast(p); - // int32_t paramValue = param->get(); - // double difference = values[0] - paramValue; - // //int steps = handler->mMorphRemainingSteps.load(); // factor - // = 1.0/steps if (factor > 0) { - // difference = difference * factor; - // } - // if (difference != 0.0) { - // int32_t newVal = std::round(paramValue + difference); - // param->set(newVal); - // } - // The interpolation above is broken, no easy way to fix for things as - // they are now... - param->set(values[0].get()); - } else if (strcmp(typeid(*p).name(), typeid(ParameterPose).name()) == - 0) { // Parameter pose - ParameterPose *param = dynamic_cast(p); - if (values.size() == 7) { - Pose paramValue = param->get(); - Pose difference; - // TODO better interpolation of quaternion - Vec3d differenceVec = - Vec3d(values[0].get(), values[1].get(), - values[2].get()) - - paramValue.vec(); - Quatd differenceQuat = - Quatd(values[3].get(), values[4].get(), - values[5].get(), values[6].get()) - - paramValue.quat(); - // int steps = handler->mMorphRemainingSteps.load(); // factor - // = 1.0/steps - if (factor > 0) { - differenceVec = differenceVec * factor; - differenceQuat = differenceQuat * factor; - } - if (differenceVec != Vec4f() && differenceQuat != Quatd()) { - param->set(Pose(paramValue.vec() + differenceVec, - paramValue.quat() + differenceQuat)); - } - } else { - std::cout << "Unexpected number of values for " - << param->getFullAddress() << std::endl; - } - } else if (strcmp(typeid(*p).name(), typeid(ParameterMenu).name()) == - 0) { // Parameter - ParameterMenu *param = dynamic_cast(p); - if (factor == 0) { - param->setCurrent(values[0].get()); - } - } else if (strcmp(typeid(*p).name(), typeid(ParameterChoice).name()) == - 0) { // Parameter - ParameterChoice *param = dynamic_cast(p); - if (factor == 0) { - param->set((uint16_t)values[0].get()); - } - } else if (strcmp(typeid(*p).name(), typeid(ParameterVec3).name()) == - 0) { // Parameter - ParameterVec3 *param = dynamic_cast(p); - if (values.size() == 3) { - Vec3f paramValue = param->get(); - Vec3f difference = Vec3f((float *)values.data()) - paramValue; - // int steps = handler->mMorphRemainingSteps.load(); // factor - // = 1.0/steps - if (factor > 0) { - difference = difference * factor; +// void PresetHandler::setParameterValues(ParameterMeta *p, +// std::vector &values) { +// if (strcmp(typeid(*p).name(), typeid(ParameterBool).name()) == +// 0) { // ParameterBool +// ParameterBool *param = dynamic_cast(p); +// // No interpolation for parameter bool. Should we change exactly in the +// // middle? +// param->set(values[0].get()); +// } else if (strcmp(typeid(*p).name(), typeid(Parameter).name()) == +// 0) { // Parameter +// Parameter *param = dynamic_cast(p); +// float paramValue = param->get(); +// float difference = values[0].get() - paramValue; +// // int steps = handler->mMorphRemainingSteps.load(); // factor = 1.0/steps +// difference = difference * factor; +// if (difference != 0.0) { +// float newVal = paramValue + difference; +// param->set(newVal); +// } +// } else if (strcmp(typeid(*p).name(), typeid(ParameterInt).name()) == +// 0) { // ParameterInt +// ParameterInt *param = dynamic_cast(p); +// // int32_t paramValue = param->get(); +// // double difference = values[0] - paramValue; +// // //int steps = handler->mMorphRemainingSteps.load(); // factor +// // = 1.0/steps if (factor > 0) { +// // difference = difference * factor; +// // } +// // if (difference != 0.0) { +// // int32_t newVal = std::round(paramValue + difference); +// // param->set(newVal); +// // } +// // The interpolation above is broken, no easy way to fix for things as +// // they are now... +// param->set(values[0].get()); +// } else if (strcmp(typeid(*p).name(), typeid(ParameterPose).name()) == +// 0) { // Parameter pose +// ParameterPose *param = dynamic_cast(p); +// if (values.size() == 7) { +// Pose paramValue = param->get(); +// Pose difference; +// // TODO better interpolation of quaternion +// Vec3d differenceVec = +// Vec3d(values[0].get(), values[1].get(), +// values[2].get()) - +// paramValue.vec(); +// Quatd differenceQuat = +// Quatd(values[3].get(), values[4].get(), +// values[5].get(), values[6].get()) - +// paramValue.quat(); +// // int steps = handler->mMorphRemainingSteps.load(); // factor +// // = 1.0/steps +// if (factor > 0) { +// differenceVec = differenceVec * factor; +// differenceQuat = differenceQuat * factor; +// } +// if (differenceVec != Vec4f() && differenceQuat != Quatd()) { +// param->set(Pose(paramValue.vec() + differenceVec, +// paramValue.quat() + differenceQuat)); +// } +// } else { +// std::cout << "Unexpected number of values for " << +// param->getFullAddress() +// << std::endl; +// } +// } else if (strcmp(typeid(*p).name(), typeid(ParameterMenu).name()) == +// 0) { // Parameter +// ParameterMenu *param = dynamic_cast(p); +// if (factor == 0) { +// param->setCurrent(values[0].get()); +// } +// } else if (strcmp(typeid(*p).name(), typeid(ParameterChoice).name()) == +// 0) { // Parameter +// ParameterChoice *param = dynamic_cast(p); +// if (factor == 0) { +// param->set((uint16_t)values[0].get()); +// } +// } else if (strcmp(typeid(*p).name(), typeid(ParameterVec3).name()) == +// 0) { // Parameter +// ParameterVec3 *param = dynamic_cast(p); +// if (values.size() == 3) { +// Vec3f paramValue = param->get(); +// Vec3f difference = Vec3f((float *)values.data()) - paramValue; +// // int steps = handler->mMorphRemainingSteps.load(); // factor +// // = 1.0/steps +// if (factor > 0) { +// difference = difference * factor; +// } +// param->set(paramValue + difference); +// } else { +// std::cout << "Unexpected number of values for " << +// param->getFullAddress() +// << std::endl; +// } +// } else if (strcmp(typeid(*p).name(), typeid(ParameterVec4).name()) == +// 0) { // Parameter +// ParameterVec4 *param = dynamic_cast(p); +// if (values.size() == 4) { +// Vec4f paramValue = param->get(); +// Vec4f difference = Vec4f(values[0].get(), values[1].get(), +// values[2].get()) - +// paramValue; +// // int steps = handler->mMorphRemainingSteps.load(); // factor +// // = 1.0/steps +// if (factor > 0) { +// difference = difference * factor; +// } +// param->set(paramValue + difference); +// } else { +// std::cout << "Unexpected number of values for " << +// param->getFullAddress() +// << std::endl; +// } +// } else if (strcmp(typeid(*p).name(), typeid(ParameterChoice).name()) == +// 0) { // Parameter +// ParameterChoice *param = dynamic_cast(p); +// if (factor == 0) { +// param->set(values[0].get()); +// } +// } else if (strcmp(typeid(*p).name(), typeid(ParameterColor).name()) == +// 0) { // Parameter +// ParameterColor *param = dynamic_cast(p); +// if (values.size() == 4) { +// Color paramValue = param->get(); +// Color difference = Color(values[0].get(), values[1].get(), +// values[2].get(), values[3].get()) +// - +// paramValue; +// // int steps = handler->mMorphRemainingSteps.load(); // factor +// // = 1.0/steps +// if (factor > 0) { +// difference = difference * factor; +// } +// param->set(paramValue + difference); +// } else { +// std::cout << "Unexpected number of values for " << +// param->getFullAddress() +// << std::endl; +// } +// } else { +// std::cout << "Unsupported Parameter " << p->getFullAddress() << std::endl; +// } +//} + +void PresetHandler::setInterpolatedValues(ParameterStates &startValues, + ParameterStates &endValues, + double factor) { + for (auto startValue : startValues) { + std::vector interpValues; + for (auto endValue : endValues) { + if (startValue.first == endValue.first) { + assert(startValue.second.size() == endValue.second.size()); + if (factor != 1.0) { + for (size_t i = 0; i < endValue.second.size(); i++) { + assert(startValue.second[i].type() == endValue.second[i].type()); + if (startValue.second[i].type() == ParameterField::FLOAT) { + interpValues.push_back(ParameterField( + startValue.second[i].get() + + (factor * (endValue.second[i].get() - + startValue.second[i].get())))); + } else if (startValue.second[i].type() == ParameterField::INT32) { + float value = + startValue.second[i].get() + + (factor * (endValue.second[i].get() - + (float)startValue.second[i].get())); + interpValues.push_back(ParameterField((int32_t)value)); + } else if (startValue.second[i].type() == ParameterField::STRING) { + interpValues.push_back(endValue.second[i]); + } + } + } else { + interpValues = endValue.second; } - param->set(paramValue + difference); - } else { - std::cout << "Unexpected number of values for " - << param->getFullAddress() << std::endl; - } - } else if (strcmp(typeid(*p).name(), typeid(ParameterVec4).name()) == - 0) { // Parameter - ParameterVec4 *param = dynamic_cast(p); - if (values.size() == 4) { - Vec4f paramValue = param->get(); - Vec4f difference = Vec4f(values[0].get(), values[1].get(), - values[2].get()) - - paramValue; - // int steps = handler->mMorphRemainingSteps.load(); // factor - // = 1.0/steps - if (factor > 0) { - difference = difference * factor; + + for (auto *param : mParameters) { + if (param->getFullAddress() == startValue.first && + interpValues.size() > 0) { + param->set(interpValues); + break; + } } - param->set(paramValue + difference); - } else { - std::cout << "Unexpected number of values for " - << param->getFullAddress() << std::endl; - } - } else if (strcmp(typeid(*p).name(), typeid(ParameterChoice).name()) == - 0) { // Parameter - ParameterChoice *param = dynamic_cast(p); - if (factor == 0) { - param->set(values[0].get()); - } - } else if (strcmp(typeid(*p).name(), typeid(ParameterColor).name()) == - 0) { // Parameter - ParameterColor *param = dynamic_cast(p); - if (values.size() == 4) { - Color paramValue = param->get(); - Color difference = - Color(values[0].get(), values[1].get(), - values[2].get(), values[3].get()) - - paramValue; - // int steps = handler->mMorphRemainingSteps.load(); // factor - // = 1.0/steps - if (factor > 0) { - difference = difference * factor; + + std::function, std::string)> + processBundleGroup = [&](std::vector bundles, + std::string prefix) { + for (unsigned int i = 0; i < bundles.size(); i++) { + std::string bundlePrefix = prefix + std::to_string(i); + for (auto *param : bundles.at(i)->parameters()) { + if (bundlePrefix + param->getFullAddress() == + startValue.first && + interpValues.size() > 0) { + param->set(interpValues); + break; + } + } + for (auto bundleGroup : bundles.at(i)->bundles()) { + prefix += "/" + bundleGroup.first + "/"; + processBundleGroup({bundleGroup.second}, prefix); + } + } + }; + + for (auto bundleGroup : mBundles) { + std::string prefix = "/" + bundleGroup.first + "/"; + processBundleGroup(bundleGroup.second, prefix); } - param->set(paramValue + difference); - } else { - std::cout << "Unexpected number of values for " - << param->getFullAddress() << std::endl; + continue; } - } else { - std::cout << "Unsupported Parameter " << p->getFullAddress() << std::endl; } } } -void PresetHandler::setParametersInBundle(ParameterBundle *bundle, - std::string bundlePrefix, - PresetHandler *handler, - float factor) { - for (ParameterMeta *p : bundle->parameters()) { - // std::cout << bundlePrefix + p->getFullAddress() << - // std::endl; - if (handler->mTargetValues.find(bundlePrefix + p->getFullAddress()) != - handler->mTargetValues.end()) { - handler->setParameterValues( - p, handler->mTargetValues[bundlePrefix + p->getFullAddress()], - factor); - } else { - if (handler->mVerbose) { - std::cout << "Parameter not found " - << bundlePrefix + p->getFullAddress() << std::endl; - } - } - } - for (auto subBundle : bundle->bundles()) { - std::string subBundlePrefix = - bundlePrefix + "/" + subBundle.second->name() + "/" + subBundle.first; - handler->setParametersInBundle(subBundle.second, subBundlePrefix, handler, - factor); + +// void PresetHandler::setParametersInBundle(ParameterBundle *bundle, +// std::string bundlePrefix, +// PresetHandler *handler, +// double factor) { +// for (ParameterMeta *p : bundle->parameters()) { +// // std::cout << bundlePrefix + p->getFullAddress() +// << +// // std::endl; +// if (handler->mTargetValues.find(bundlePrefix + p->getFullAddress()) != +// handler->mTargetValues.end()) { +// handler->setParameterValues( +// p, handler->mTargetValues[bundlePrefix + p->getFullAddress()], +// factor); +// } else { +// if (handler->mVerbose) { +// std::cout << "Parameter not found " +// << bundlePrefix + p->getFullAddress() << std::endl; +// } +// } +// } +// for (auto subBundle : bundle->bundles()) { +// std::string subBundlePrefix = +// bundlePrefix + "/" + subBundle.second->name() + "/" + subBundle.first; +// handler->setParametersInBundle(subBundle.second, subBundlePrefix, handler, +// factor); +// } +//} + +void PresetHandler::stepMorphing() { + uint64_t totalSteps = mTotalSteps.load(); + uint64_t stepCount = mMorphStepCount.fetch_add(1); + if (stepCount <= totalSteps && totalSteps > 0) { + double morphPhase = double(stepCount) / totalSteps; + setInterpolatedValues(mStartValues, mTargetValues, morphPhase); } } void PresetHandler::morphingFunction(al::PresetHandler *handler) { - // handler->mMorphLock.lock(); - while (handler->mRunning) { - std::unique_lock lk(handler->mTargetLock); - handler->mMorphConditionVar.wait(lk); - int remainingSteps; - while ((remainingSteps = std::atomic_fetch_sub( - &(handler->mMorphRemainingSteps), 1)) > 0) { - for (ParameterMeta *p : handler->mParameters) { - if (handler->mTargetValues.find(p->getFullAddress()) != - handler->mTargetValues.end()) { - handler->setParameterValues( - p, handler->mTargetValues[p->getFullAddress()], - 1.0 / remainingSteps); - } else { - if (handler->mVerbose) { - std::cout << "Parameter not found " << p->getFullAddress() - << std::endl; - } - } - } - for (auto bundleGroup : handler->mBundles) { - const std::string &bundleName = bundleGroup.first; - for (unsigned int i = 0; i < bundleGroup.second.size(); i++) { - std::string bundlePrefix = "/" + bundleName + "/" + std::to_string(i); - ParameterBundle *bundle = bundleGroup.second[i]; - handler->setParametersInBundle(bundle, bundlePrefix, handler, - 1.0 / remainingSteps); - } - } - al::wait(handler->mMorphInterval); - } - // // Set final values - // for (Parameter param: mParameters) { - // if (preset.find(param.getName()) != preset.end()) { - // param.set(preset[param.getName()]); - // } - // } + while (handler->mCpuThreadRunning) { + handler->stepMorphing(); + al::wait(handler->mMorphInterval); } - // handler->mMorphLock.unlock(); } PresetHandler::ParameterStates PresetHandler::getBundleStates( @@ -829,9 +1030,11 @@ PresetHandler::ParameterStates PresetHandler::getBundleStates( p->get(values[bundlePrefix + p->getFullAddress()]); } for (auto b : bundle->bundles()) { - auto subBundleValues = getBundleStates(b.second, b.first); - for (auto bundleValue : subBundleValues) { - values[bundlePrefix + "/" + bundleValue.first] = bundleValue.second; + for (auto *bundle : b.second) { + auto subBundleValues = getBundleStates(bundle, b.first); + for (auto bundleValue : subBundleValues) { + values[bundlePrefix + "/" + bundleValue.first] = bundleValue.second; + } } } return values; @@ -850,8 +1053,8 @@ PresetHandler::ParameterStates PresetHandler::loadPresetValues( std::ifstream f(path + name + ".preset"); if (!f.is_open()) { if (mVerbose) { - std::cout << "Error while opening preset file: " << mFileName - << std::endl; + std::cout << "Error while opening preset file: " + << path + name + ".preset" << std::endl; } } while (getline(f, line)) { @@ -905,8 +1108,8 @@ PresetHandler::ParameterStates PresetHandler::loadPresetValues( } if (f.bad()) { if (mVerbose) { - std::cout << "Error while writing preset file: " << mFileName - << std::endl; + std::cout << "Error while writing preset file: " + << path + name + ".preset" << std::endl; } } f.close(); @@ -930,7 +1133,7 @@ bool PresetHandler::savePresetValues(const ParameterStates &values, std::ofstream f(fileName); if (!f.is_open()) { if (mVerbose) { - std::cout << "Error while opening preset file: " << mFileName + std::cout << "Error while opening preset file fro write: " << fileName << std::endl; } return false; @@ -938,16 +1141,16 @@ bool PresetHandler::savePresetValues(const ParameterStates &values, f << "::" + presetName << std::endl; for (auto value : values) { std::string types, valueString; - for (auto &value : value.second) { - if (value.type() == ParameterField::FLOAT) { + for (auto &value2 : value.second) { + if (value2.type() == ParameterField::FLOAT) { types += "f"; - valueString += std::to_string(value.get()) + " "; - } else if (value.type() == ParameterField::STRING) { + valueString += std::to_string(value2.get()) + " "; + } else if (value2.type() == ParameterField::STRING) { types += "s"; - valueString += value.get() + " "; - } else if (value.type() == ParameterField::INT32) { + valueString += value2.get() + " "; + } else if (value2.type() == ParameterField::INT32) { types += "i"; - valueString += std::to_string(value.get()) + " "; + valueString += std::to_string(value2.get()) + " "; } } // TODO chop last blank space @@ -957,8 +1160,7 @@ bool PresetHandler::savePresetValues(const ParameterStates &values, f << "::" << std::endl; if (f.bad()) { if (mVerbose) { - std::cout << "Error while writing preset file: " << mFileName - << std::endl; + std::cout << "Error while writing preset file: " << fileName << std::endl; } ok = false; } @@ -966,59 +1168,85 @@ bool PresetHandler::savePresetValues(const ParameterStates &values, return ok; } -std::vector PresetHandler::getParameterValue(ParameterMeta *p) { - // We do a runtime check to determine the type of the parameter to determine - // how to draw it. - if (strcmp(typeid(*p).name(), typeid(ParameterBool).name()) == - 0) { // ParameterBool - ParameterBool *param = dynamic_cast(p); - return std::vector{param->get()}; - } else if (strcmp(typeid(*p).name(), typeid(Parameter).name()) == - 0) { // Parameter - Parameter *param = dynamic_cast(p); - return std::vector{param->get()}; - } else if (strcmp(typeid(*p).name(), typeid(ParameterInt).name()) == - 0) { // ParameterInt - ParameterInt *param = dynamic_cast(p); - return std::vector{float(param->get())}; - } else if (strcmp(typeid(*p).name(), typeid(ParameterPose).name()) == - 0) { // Parameter pose - ParameterPose *param = dynamic_cast(p); - Pose value = param->get(); - return std::vector{(float)value.pos()[0], (float)value.pos()[1], - (float)value.pos()[2], (float)value.quat().w, - (float)value.quat().x, (float)value.quat().y, - (float)value.quat().z}; - } else if (strcmp(typeid(*p).name(), typeid(ParameterMenu).name()) == - 0) { // Parameter menu - ParameterMenu *param = dynamic_cast(p); - // TODO we should store the original int value, but float will do for now - return std::vector{(float)param->get()}; - } else if (strcmp(typeid(*p).name(), typeid(ParameterChoice).name()) == - 0) { // Parameter choice - ParameterChoice *param = dynamic_cast(p); - // TODO we should store the original int value, but float will do for now - return std::vector{(float)param->get()}; - } else if (strcmp(typeid(*p).name(), typeid(ParameterVec3).name()) == - 0) { // Parameter vec3 - ParameterVec3 *param = dynamic_cast(p); - Vec3f value = param->get(); - return std::vector{value.x, value.y, value.z}; - } else if (strcmp(typeid(*p).name(), typeid(ParameterVec4).name()) == - 0) { // Parameter vec4 - ParameterVec4 *param = dynamic_cast(p); - Vec4f value = param->get(); - return std::vector{value.x, value.y, value.z, value.w}; - } else if (strcmp(typeid(*p).name(), typeid(ParameterColor).name()) == - 0) { // Parameter choice - ParameterColor *param = dynamic_cast(p); - // TODO we should store the original int value, but float will do for now - return std::vector{param->get().r, param->get().g, param->get().b, - param->get().a}; - } else { - // TODO this check should be performed on registration - std::cout << "Unsupported Parameter type for storage for " - << p->getFullAddress() << std::endl; +void PresetHandler::setTimeMaster(TimeMasterMode masterMode) { + stopCpuThread(); + mTimeMasterMode = masterMode; + if (masterMode == TimeMasterMode::TIME_MASTER_CPU) { + mCpuThreadRunning = true; + mMorphingThread = + std::make_unique(PresetHandler::morphingFunction, this); } - return std::vector(); } + +void PresetHandler::startCpuThread() { + mCpuThreadRunning = true; + mMorphingThread = + std::make_unique(PresetHandler::morphingFunction, this); +} + +void PresetHandler::stopCpuThread() { + mCpuThreadRunning = false; + // mMorphConditionVar.notify_all(); + // mMorphLock.lock(); + if (mMorphingThread) { + mMorphingThread->join(); + mMorphingThread = nullptr; + } +} + +// std::vector PresetHandler::getParameterValue(ParameterMeta *p) { +// // We do a runtime check to determine the type of the parameter to determine +// // how to draw it. +// if (strcmp(typeid(*p).name(), typeid(ParameterBool).name()) == +// 0) { // ParameterBool +// ParameterBool *param = dynamic_cast(p); +// return std::vector{param->get()}; +// } else if (strcmp(typeid(*p).name(), typeid(Parameter).name()) == +// 0) { // Parameter +// Parameter *param = dynamic_cast(p); +// return std::vector{param->get()}; +// } else if (strcmp(typeid(*p).name(), typeid(ParameterInt).name()) == +// 0) { // ParameterInt +// ParameterInt *param = dynamic_cast(p); +// return std::vector{float(param->get())}; +// } else if (strcmp(typeid(*p).name(), typeid(ParameterPose).name()) == +// 0) { // Parameter pose +// ParameterPose *param = dynamic_cast(p); +// Pose value = param->get(); +// return std::vector{(float)value.pos()[0], (float)value.pos()[1], +// (float)value.pos()[2], (float)value.quat().w, +// (float)value.quat().x, (float)value.quat().y, +// (float)value.quat().z}; +// } else if (strcmp(typeid(*p).name(), typeid(ParameterMenu).name()) == +// 0) { // Parameter menu +// ParameterMenu *param = dynamic_cast(p); +// // TODO we should store the original int value, but float will do for now +// return std::vector{(float)param->get()}; +// } else if (strcmp(typeid(*p).name(), typeid(ParameterChoice).name()) == +// 0) { // Parameter choice +// ParameterChoice *param = dynamic_cast(p); +// // TODO we should store the original int value, but float will do for now +// return std::vector{(float)param->get()}; +// } else if (strcmp(typeid(*p).name(), typeid(ParameterVec3).name()) == +// 0) { // Parameter vec3 +// ParameterVec3 *param = dynamic_cast(p); +// Vec3f value = param->get(); +// return std::vector{value.x, value.y, value.z}; +// } else if (strcmp(typeid(*p).name(), typeid(ParameterVec4).name()) == +// 0) { // Parameter vec4 +// ParameterVec4 *param = dynamic_cast(p); +// Vec4f value = param->get(); +// return std::vector{value.x, value.y, value.z, value.w}; +// } else if (strcmp(typeid(*p).name(), typeid(ParameterColor).name()) == +// 0) { // Parameter choice +// ParameterColor *param = dynamic_cast(p); +// // TODO we should store the original int value, but float will do for now +// return std::vector{param->get().r, param->get().g, param->get().b, +// param->get().a}; +// } else { +// // TODO this check should be performed on registration +// std::cout << "Unsupported Parameter type for storage for " +// << p->getFullAddress() << std::endl; +// } +// return std::vector(); +//} diff --git a/src/ui/al_PresetSequencer.cpp b/src/ui/al_PresetSequencer.cpp index 878d5287..08517121 100644 --- a/src/ui/al_PresetSequencer.cpp +++ b/src/ui/al_PresetSequencer.cpp @@ -1,4 +1,6 @@ +#include "al/ui/al_PresetSequencer.hpp" + #include #include #include @@ -6,77 +8,85 @@ #include "al/io/al_File.hpp" #include "al/ui/al_Composition.hpp" -#include "al/ui/al_PresetSequencer.hpp" #include "al/ui/al_SequenceRecorder.hpp" using namespace al; -PresetSequencer::PresetSequencer() - : mSequencerActive(true), - mRunning(false), - mStartingRun(false), - mSequencerThread(nullptr), +PresetSequencer::PresetSequencer(TimeMasterMode timeMasterMode) + : mRunning(false), mBeginCallbackEnabled(false), - mEndCallbackEnabled(false) { - mSequencerThread = - std::make_unique(PresetSequencer::sequencerFunction, this); + mEndCallbackEnabled(false), + mSequencerThread(nullptr) { + mTimeMasterMode = timeMasterMode; + if (mTimeMasterMode == TimeMasterMode::TIME_MASTER_CPU) { + startCpuThread(); + } } -PresetSequencer::~PresetSequencer() { - mSequencerActive = false; - stopSequence(false); - if (mPresetHandler) { - mPresetHandler->stopMorph(); - } - this->enableBeginCallback( - false); // To vaoid triggering callback on thread wake up - mStartingRun = - true; // The conditional variable will only stop waiting if this is true - this->mPlayWaitVariable.notify_all(); - mSequencerThread->join(); -} +PresetSequencer::~PresetSequencer() { stopCpuThread(); } -void PresetSequencer::playSequence(std::string sequenceName, double timeScale) { +void PresetSequencer::playSequence(std::string sequenceName, double timeScale, + double timeOffset) { stopSequence(); mSequenceLock.lock(); - // while (!mSteps.empty()) { - // mSteps.pop(); - // } + if (sequenceName.size() > 0) { - std::queue steps = loadSequence(sequenceName, timeScale); + auto steps = loadSequence(sequenceName, timeScale); + mCurrentSequence = sequenceName; mSteps = steps; } - { - std::unique_lock lk(mPlayWaitLock); - mStartingRun = true; - mPlayWaitVariable.notify_one(); - } mSequenceLock.unlock(); - { - std::unique_lock lk(mPlayWaitLock); - mPlayWaitVariable.wait(lk, [&] { return mRunning; }); + + // Initialize counters + mLastTimeUpdate = 0.0; + mParameterTargetTime = 0.0; + mStartRunning = true; + mRunning = false; + setTime(0.0); + if (mSteps.size() > 0 && mSteps.size() > mCurrentStep) { + if (mPresetHandler && mSteps[mCurrentStep].type == PRESET) { + mPresetHandler->morphTo(mSteps[mCurrentStep].name, + mSteps[mCurrentStep].morphTime); + } } + setTime(timeOffset); - // std::thread::id seq_thread_id = mSequencerThread->get_id(); - // std::cout << "Preset Sequencer thread id: " << std::hex << seq_thread_id - //<< std::endl; + if (mTimeMasterMode == TimeMasterMode::TIME_MASTER_CPU && mSequencerThread) { + { + mPlayPromiseObj = std::make_shared>(); + auto playFuture = mPlayPromiseObj->get_future(); + { + std::unique_lock lk(mPlayWaitLock); + mPlayWaitVariable.notify_all(); + } + playFuture.get(); + } + + } else { + mStartRunning = false; + mRunning = true; + } } void PresetSequencer::stopSequence(bool triggerCallbacks) { if (mRunning == true) { - bool mCallbackStatus = false; + bool previousCallbackStatus = false; if (!triggerCallbacks) { - mCallbackStatus = mEndCallbackEnabled; + previousCallbackStatus = mEndCallbackEnabled; enableEndCallback(false); } + mRunning = false; + if (mTimeMasterMode == TimeMasterMode::TIME_MASTER_CPU) { + std::unique_lock lk(mPlayWaitLock); + } + + if (mPresetHandler) { + mPresetHandler->stopMorphing(); + } + // Wait until CPU thread is waiting to start again. if (!triggerCallbacks) { - enableEndCallback(mCallbackStatus); + enableEndCallback(previousCallbackStatus); } - mRunning = false; - // Is waiting on this lock reliable? Will it hang? - mPlayWaitLock.lock(); // This waits for the sequencer thread function to - // wait on the condition variable. - mPlayWaitLock.unlock(); } } @@ -84,65 +94,12 @@ void PresetSequencer::setTime(double time) { if (running()) { mTimeRequest = time; } else { - mSteps = mMostRecentSequence; - double currentTime = 0.0; - auto sequencer = this; - auto timeRequest = time; - if (mSteps.size() > 0) { - Step step = sequencer->mSteps.front(); - std::string previousPreset = mSteps.front().presetName; - while (currentTime < timeRequest && sequencer->mSteps.size() > 0) { - step = sequencer->mSteps.front(); - currentTime += step.morphTime + step.waitTime; - // std::cout << "Skipping: " << step.presetName << " " << - // step.morphTime << ":" << step.waitTime << std::endl; - if (currentTime < timeRequest) { - previousPreset = sequencer->mSteps.front().presetName; - } - sequencer->mSteps.pop(); - } - if (timeRequest > - (currentTime - - step.waitTime)) { // We only need to wait, morphing is done - // sequencer->mPresetHandler->setMorphTime(0); - sequencer->mPresetHandler->recallPresetSynchronous(step.presetName); - sequencer->mPresetHandler->setMorphTime( - step.morphTime); // Just set it so it has the expected last value - // targetTime = now + std::chrono::microseconds(int(1.0e6 - // * (currentTime - timeRequest))); sequenceStart = now - - // std::chrono::microseconds(int(1.0e6 * (timeRequest))); - } else { // We need to finish the morphing - float remainingMorphTime = currentTime - timeRequest - step.waitTime; - if (previousPreset.size() > 0) { - // sequencer->mPresetHandler->recallPresetSynchronous(previousPreset); - - sequencer->mPresetHandler->setInterpolatedPreset( - previousPreset, step.presetName, - 1.0 - (remainingMorphTime / step.morphTime)); - // std::cout << "Interpolating: " << previousPreset - // << " " << step.presetName << " " << 1.0 - - // (remainingMorphTime/step.morphTime) << - // std::endl; - } - // sequencer->mPresetHandler->setMorphTime(remainingMorphTime); - // sequencer->mPresetHandler->recallPreset(step.presetName); - // targetTime = now + std::chrono::microseconds(int(1.0e6 - // * (currentTime - timeRequest))); sequenceStart = now - - // std::chrono::microseconds(int(1.0e6 * (timeRequest))); - } - if (sequencer->mTimeChangeCallback) { - // std::cout << 1.0e-9 * - // std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - // - sequenceStart).count() <mTimeChangeCallback(time); - } - } + updateTime(time); } } void PresetSequencer::rewind() { if (mSequenceLock.try_lock()) { - mSteps = mMostRecentSequence; setTime(0.0); mSequenceLock.unlock(); } else { // If lock can't be acquired, request time change @@ -150,72 +107,6 @@ void PresetSequencer::rewind() { } } -bool PresetSequencer::archiveSequence(std::string sequenceName, - bool overwrite) { - bool ok = true; - std::string fullPath = buildFullPath(sequenceName) + "_archive"; - if (mPresetHandler == nullptr) { - std::cerr - << "A Preset Handler must be registered to store sequences. Aborting." - << std::endl; - return false; - } - if (overwrite) { - if (File::isDirectory(fullPath)) { - if (!Dir::removeRecursively(fullPath)) { - std::cout << "Error removing directory: " << fullPath - << " aborting sequence archiving." << std::endl; - return false; - } - } else { - if (File::remove(fullPath) != 0) { - std::cout << "Error removing file: " << fullPath - << " aborting sequence archiving." << std::endl; - return false; - } - } - if (!Dir::make(fullPath)) { - std::cout << "Error creating sequence archive directory " << fullPath - << std::endl; - return false; - } - } else { - int counter = 0; - while (File::isDirectory(fullPath)) { - std::string newName = sequenceName + "_" + std::to_string(counter++); - fullPath = buildFullPath(newName) + "_archive"; - if (counter == 0) { // We've wrapped and run out of names... - std::cout << "Out of names for sequence archive." << std::endl; - return false; - } - } - if (!Dir::make(fullPath)) { - std::cout << "Error creating sequence archive directory " << fullPath - << std::endl; - return false; - } - } - std::queue steps = loadSequence(sequenceName); - while (steps.size() > 0) { - Step &step = steps.front(); - std::string presetFilename = - mPresetHandler->getCurrentPath() + step.presetName + ".preset"; - if (!File::copy(presetFilename, fullPath)) { - std::cout << "Error copying preset " << presetFilename - << " when archiving." << std::endl; - ok = false; - } - steps.pop(); - } - if (!File::copy(buildFullPath(sequenceName), fullPath)) { - std::cout << "Error copying sequence " << sequenceName << " when archiving." - << std::endl; - ok = false; - } - - return ok; -} - std::vector al::PresetSequencer::getSequenceList() { std::vector sequenceList; std::string path = mDirectory; @@ -248,215 +139,37 @@ std::vector al::PresetSequencer::getSequenceList() { void PresetSequencer::sequencerFunction(al::PresetSequencer *sequencer) { while (sequencer->mSequencerActive) { - { - std::unique_lock lk(sequencer->mPlayWaitLock); - if (sequencer->mBeginCallbackEnabled && - sequencer->mBeginCallback != nullptr) { - sequencer->mBeginCallback(sequencer, sequencer->mBeginCallbackData); - } - sequencer->mPlayWaitVariable.wait( - lk, [&] { return sequencer->mStartingRun; }); - sequencer->mRunning = true; - sequencer->mStartingRun = false; + // { + std::unique_lock lk(sequencer->mPlayWaitLock); + sequencer->mPlayWaitVariable.wait(lk); + if (!sequencer->mStartRunning) { + sequencer->mPlayPromiseObj->set_value(); + return; } - sequencer->mPlayWaitVariable - .notify_one(); // to have the calling function know play has started - // and callbacks have been called. - sequencer->mSequenceLock.lock(); - - int granularity = sequencer->mGranularity; - auto sequenceStart = std::chrono::high_resolution_clock::now(); - auto targetTime = sequenceStart; - auto paramTargetTime = sequenceStart; - float timeAccumulator = 0.0f; - std::queue parameterList; - bool playStandaloneParameters = false; - float playStandaloneTime = 0.0f; - while (sequencer->mSteps.size() > 0 && - sequencer->mSteps.front().type == PARAMETER) { - parameterList.push(sequencer->mSteps.front()); - // std::cout << "queued " << step.presetName << ":" << - // step.params[0] <mSteps.front().waitTime; - sequencer->mSteps.pop(); - playStandaloneParameters = true; + if (sequencer->mBeginCallbackEnabled && + sequencer->mBeginCallback != nullptr) { + sequencer->mBeginCallback(sequencer); } - while (sequencer->running() && sequencer->mSteps.size() > 0) { - Step step = sequencer->mSteps.front(); - auto now = std::chrono::high_resolution_clock::now(); - if (step.type == PRESET && !playStandaloneParameters) { - if (sequencer->mPresetHandler) { - sequencer->mPresetHandler->setMorphTime(step.morphTime); - sequencer->mPresetHandler->recallPreset(step.presetName); - // std::cout << "recalling " << step.presetName << - // std::endl; - } else { - std::cerr << "No preset handler registered with PresetSequencer. " - "Ignoring preset change." - << std::endl; - } - sequencer->mSteps.pop(); - while (!parameterList.empty()) { - parameterList.pop(); - } - while (sequencer->mSteps.size() > 0 && - sequencer->mSteps.front().type == PARAMETER) { - parameterList.push(sequencer->mSteps.front()); - // std::cout << "queued " << - // sequencer->mSteps.front().presetName << ":" << - // sequencer->mSteps.front().params[0] <mSteps.pop(); - } - float totalWaitTime = step.morphTime + step.waitTime; - targetTime += std::chrono::microseconds( - (int)(totalWaitTime * 1.0e6 - (granularity * 1.5 * 1.0e3))); - } - if (parameterList.size() > 0) { - paramTargetTime = - now + std::chrono::microseconds( - (int)(parameterList.front().waitTime * 1.0e6)); - // std::cout << "setting param wait " << - // parameterList.front().waitTime << std::endl; - } - if (playStandaloneParameters) { - targetTime = - now + std::chrono::microseconds((int)(playStandaloneTime * 1e06)); - } - - while (now < targetTime || - parameterList.size() > - 0) { // Granularity to allow more responsive stopping of - // composition playback - // std::cout << - // std::chrono::high_resolution_clock::to_time_t(targetTime) - // << "---" << - //std::chrono::high_resolution_clock::to_time_t(std::chrono::high_resolution_clock::now()) - //<< std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(granularity)); - timeAccumulator += granularity; - if (now > paramTargetTime && parameterList.size() > 0) { - if (sequencer->mRunning) { - std::string name = parameterList.front().presetName; - for (auto *param : sequencer->mParameters) { - if (param->getFullAddress() == name) { - param->fromFloat(parameterList.front().params[0]); - } - } - } - parameterList.pop(); - if (parameterList.size() > 0) { - paramTargetTime += std::chrono::microseconds( - (int)(parameterList.front().waitTime * 1.0e6)); - } - } - if (timeAccumulator >= sequencer->mTimeChangeMinTimeDelta * 1000) { - timeAccumulator -= sequencer->mTimeChangeMinTimeDelta * 1000; - if (sequencer->mTimeChangeCallback) { - // std::cout << 1.0e-9 * - // std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - // - sequenceStart).count() <mTimeChangeCallback( - 1.0e-9 * - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - sequenceStart) - .count()); - } - } - double timeRequest = sequencer->mTimeRequest.exchange(-1.0f); - if (timeRequest > 0.0) { - double currentTime = - 1.0e-9 * std::chrono::duration_cast( - now - sequenceStart) - .count(); - // Bring back previous steps - sequencer->mSteps = sequencer->mMostRecentSequence; - currentTime = 0; - sequenceStart = now; - // paramTargetTime = now; - targetTime = now; - if (currentTime < timeRequest) { - std::string previousPreset = sequencer->mSteps.front().presetName; - while (currentTime < timeRequest && sequencer->mSteps.size() > 0) { - step = sequencer->mSteps.front(); - if (step.type == PRESET) { - currentTime += step.morphTime + step.waitTime; - std::cout << "Skipping: " << step.presetName << " " - << step.morphTime << ":" << step.waitTime - << std::endl; - if (currentTime < timeRequest) { - previousPreset = sequencer->mSteps.front().presetName; - } - } - sequencer->mSteps.pop(); - } - if (timeRequest > - (currentTime - - step.waitTime)) { // We only need to wait, morphing is done - // sequencer->mPresetHandler->setMorphTime(0); - sequencer->mPresetHandler->recallPresetSynchronous( - step.presetName); - sequencer->mPresetHandler->setMorphTime( - step.morphTime); // Just set it so it has the expected last - // value - targetTime = now + std::chrono::microseconds( - int(1.0e6 * (currentTime - timeRequest))); - sequenceStart = - now - std::chrono::microseconds(int(1.0e6 * (timeRequest))); - } else { // We need to finish the morphing - float remainingMorphTime = - currentTime - timeRequest - step.waitTime; - if (previousPreset.size() > 0) { - // sequencer->mPresetHandler->recallPresetSynchronous(previousPreset); - - sequencer->mPresetHandler->setInterpolatedPreset( - previousPreset, step.presetName, - 1.0 - (remainingMorphTime / step.morphTime)); - std::cout << "Interpolating: " << previousPreset << " " - << step.presetName << " " - << 1.0 - (remainingMorphTime / step.morphTime) - << std::endl; - } - sequencer->mPresetHandler->setMorphTime(remainingMorphTime); - sequencer->mPresetHandler->recallPreset(step.presetName); - targetTime = now + std::chrono::microseconds( - int(1.0e6 * (currentTime - timeRequest))); - sequenceStart = - now - std::chrono::microseconds(int(1.0e6 * (timeRequest))); - } - } - } - if (sequencer->mRunning == false) { - targetTime = now; - break; - } - now = std::chrono::high_resolution_clock::now(); - } - playStandaloneParameters = false; - if (step.type == EVENT) { // After event is triggered, call callback - if (sequencer->mRunning) { - for (auto eventCallback : sequencer->mEventCallbacks) { - if (eventCallback.eventName == step.presetName) { - eventCallback.callback(eventCallback.callbackData, step.params); - break; - } - } - } - } - - // std::this_thread::sleep_until(targetTime); - // std::this_thread::sleep_for(std::chrono::duration(totalWaitTime)); + // std::cout << "After begin callback" << std::endl; + sequencer->mRunning = true; + sequencer->mStartRunning = false; + sequencer->mPlayPromiseObj->set_value(); + // } + + while (sequencer->running()) { + sequencer->stepSequencer(sequencer->mGranularity * 1.0e-9); + std::this_thread::sleep_for( + std::chrono::nanoseconds(sequencer->mGranularity)); } + // std::cout << "Sequence finished." << std::endl; if (sequencer->mPresetHandler) { std::this_thread::sleep_for(std::chrono::milliseconds( 100)); // Give a little time to process any pending preset changes. - sequencer->mPresetHandler->stopMorph(); + sequencer->mPresetHandler->stopMorphing(); } - // std::cout << "Sequence finished." << std::endl; - sequencer->mRunning = false; - sequencer->mSequenceLock.unlock(); if (sequencer->mEndCallbackEnabled && sequencer->mEndCallback != nullptr) { - bool finished = sequencer->mSteps.size() == 0; - sequencer->mEndCallback(finished, sequencer, sequencer->mEndCallbackData); + bool finished = sequencer->mSteps.size() == sequencer->mCurrentStep; + sequencer->mEndCallback(finished, sequencer); } } } @@ -471,9 +184,19 @@ void PresetSequencer::setHandlerSubDirectory(std::string subDir) { } } -std::queue PresetSequencer::loadSequence( +PresetSequencer &PresetSequencer::registerPresetHandler( + PresetHandler &presetHandler) { + mPresetHandler = &presetHandler; + // We need to take over the preset handler timing. + // mPresetHandler->setTimeMaster(TimeMasterMode::TIME_MASTER_CPU); + mDirectory = mPresetHandler->getCurrentPath(); + // std::cout << "Path set to:" << mDirectory << std::endl; + return *this; +} + +std::vector PresetSequencer::loadSequence( std::string sequenceName, double timeScale) { - std::queue steps; + std::vector steps; std::string fullName = buildFullPath(sequenceName); std::ifstream f(fullName); if (!f.is_open()) { @@ -495,7 +218,7 @@ std::queue PresetSequencer::loadSequence( if (name.size() > 0 && name[0] == '@') { Step step; step.type = EVENT; - step.presetName = name.substr(1); // chop initial '@' + step.name = name.substr(1); // chop initial '@' step.morphTime = std::stof(delta) * timeScale; step.waitTime = std::stof(duration) * timeScale; @@ -503,41 +226,36 @@ std::queue PresetSequencer::loadSequence( std::string next; std::getline(ss, next, ':'); step.params.push_back(std::stof(next)); - steps.push(step); + steps.push_back(step); // std::cout << name << ":" << delta << ":" << - //duration << std::endl; + // duration << std::endl; } else if (name.size() > 0 && name[0] == '+') { Step step; step.type = PARAMETER; - step.presetName = delta; + step.name = delta; step.waitTime = std::stof(name.substr(1)) * timeScale; step.params = {float(std::stod(duration) * timeScale)}; - steps.push(step); + steps.push_back(step); // std::cout << name << ":" << delta << ":" << duration << std::endl; } else if (name.size() > 0 && name[0] != '#' && name[0] != '\r') { Step step; step.type = PRESET; - step.presetName = name; + step.name = name; step.morphTime = std::stof(delta) * timeScale; step.waitTime = std::stof(duration) * timeScale; - steps.push(step); + steps.push_back(step); // std::cout << name << ":" << delta << ":" << duration << std::endl; } } if (f.bad()) { std::cout << "Error reading:" << sequenceName << std::endl; } - - if (!steps.empty()) { - mCurrentSequence = sequenceName; - mMostRecentSequence = steps; - } return steps; } void PresetSequencer::registerEventCommand( std::string eventName, - std::function ¶ms)> callback, + std::function &)> callback, void *data) { EventCallback cb; cb.eventName = eventName; @@ -548,27 +266,23 @@ void PresetSequencer::registerEventCommand( } void PresetSequencer::registerBeginCallback( - std::function beginCallback, - void *userData) { + std::function beginCallback) { mBeginCallback = beginCallback; - mBeginCallbackData = userData; mBeginCallbackEnabled = true; } void PresetSequencer::registerEndCallback( - std::function endCallback, - void *userData) { + std::function endCallback) { // FIXME this data needs to be protected with a mutex mEndCallback = endCallback; - mEndCallbackData = userData; mEndCallbackEnabled = true; } void PresetSequencer::registerTimeChangeCallback( std::function func, float minTimeDeltaSec) { - if (mSequenceLock.try_lock()) { + if (!mRunning && mSequenceLock.try_lock()) { mTimeChangeMinTimeDelta = minTimeDeltaSec; - mTimeChangeCallback = func; + mTimeChangeCallbacks.push_back(func); mSequenceLock.unlock(); } else { std::cerr << "ERROR: Failed to set time change callback. Sequencer running" @@ -576,37 +290,217 @@ void PresetSequencer::registerTimeChangeCallback( } } +float PresetSequencer::getSequenceStartOffset(std::string sequenceName) { + std::vector steps; + if (sequenceName == mCurrentSequence) { + mSequenceLock.lock(); + steps = mSteps; + mSequenceLock.unlock(); + } else { + steps = loadSequence(sequenceName); + } + float additionalDuration = 0.0f; + for (auto &step : steps) { + if (step.type == PRESET) { + break; + } else { + additionalDuration += step.waitTime; + } + } + return additionalDuration; +} + float PresetSequencer::getSequenceTotalDuration(std::string sequenceName) { - float duration = 0.0f; - std::queue steps; + std::vector steps; if (sequenceName == mCurrentSequence) { - steps = mMostRecentSequence; + mSequenceLock.lock(); + steps = mSteps; + mSequenceLock.unlock(); } else { steps = loadSequence(sequenceName); } - while (steps.size() > 0) { - const Step &step = steps.front(); - duration += step.morphTime + step.waitTime; - steps.pop(); + float duration = 0.0f; + float additionalDuration = 0.0f; + for (auto &step : steps) { + if (step.type == PRESET) { + duration += step.morphTime + step.waitTime; + additionalDuration = 0.0; + } else { + additionalDuration += step.waitTime; + } } - return duration; + return duration + additionalDuration; } void PresetSequencer::clearSteps() { stopSequence(); mSequenceLock.lock(); - while (!mSteps.empty()) { - mSteps.pop(); - } + mSteps.clear(); + mCurrentSequence = ""; mSequenceLock.unlock(); } void PresetSequencer::appendStep(PresetSequencer::Step &newStep) { mSequenceLock.lock(); - mSteps.push(newStep); + mSteps.push_back(newStep); + mCurrentSequence = ""; + mSequenceLock.unlock(); +} + +void PresetSequencer::setTimeMaster(TimeMasterMode masterMode) { + if (masterMode == TimeMasterMode::TIME_MASTER_CPU) { + startCpuThread(); + } else { + if (mTimeMasterMode == TimeMasterMode::TIME_MASTER_CPU) { + stopCpuThread(); + } + } + mTimeMasterMode = masterMode; +} + +void PresetSequencer::processTimeChangeRequest() { + // Process time change request + double timeRequest = double(mTimeRequest.exchange(-1.0f)); + if (timeRequest >= 0.0) { + updateTime(timeRequest); + } +} + +void PresetSequencer::updateTime(double time) { + // mSteps = mMostRecentSequence; + if (mSteps.size() > 0) { + mCurrentStep = 0; + mCurrentTime = time; + mTargetTime = 0; + mLastPresetTime = 0.0; + mLastTimeUpdate = 0.0; + while (!mParameterList.empty()) { + mParameterList.pop(); + } + + // Queue parameter and event steps before first preset. + while ((mSteps.size() > mCurrentStep) && + (mSteps[mCurrentStep].type != PRESET)) { + mParameterList.push(mSteps[mCurrentStep]); + // Move current time back to accomodate these steps + mCurrentTime -= mSteps[mCurrentStep].waitTime; + mLastPresetTime = mCurrentTime; + mParameterTargetTime = mLastPresetTime; + mLastTimeUpdate = mParameterTargetTime; + mCurrentStep++; + } + + updateSequencer(); + if (mSteps.size() > (mCurrentStep - 1) && mCurrentStep > 1) { + Step step = mSteps[mCurrentStep - 1]; + std::string previousPreset = mSteps[mCurrentStep - 2].name; + if (time > (mTargetTime - step.waitTime)) { + // We only need to wait, morphing is done + if (mPresetHandler) { + mPresetHandler->recallPresetSynchronous(step.name); + // Just set morph time so it has the expected last value + mPresetHandler->setMorphTime(step.morphTime); + } + } else { + // In the middle of morphing + if (mPresetHandler) { + double remainingMorphTime = + mTargetTime - time - double(step.waitTime); + if (previousPreset.size() > 0) { + mPresetHandler->setInterpolatedPreset( + previousPreset, step.name, + 1.0 - (remainingMorphTime / step.morphTime)); + } + if (mRunning || mStartRunning) { + mPresetHandler->morphTo(step.name, remainingMorphTime); + } + } + } + } + + for (auto cb : mTimeChangeCallbacks) { + cb(time); + } + } +} + +void PresetSequencer::updateSequencer() { + mSequenceLock.lock(); + + while (mTargetTime <= mCurrentTime && (mSteps.size() > mCurrentStep)) { + // Reached target time. Process step + Step step = mSteps[mCurrentStep]; + assert(step.type == PRESET); + + if (mPresetHandler && (mRunning || mStartRunning)) { + mPresetHandler->morphTo(step.name, step.morphTime); + // std::cout << "recalling " << step.presetName << + // std::endl; + } + mLastPresetTime = mTargetTime; + mParameterTargetTime = mTargetTime; + mTargetTime += step.morphTime + step.waitTime; + mCurrentStep++; + // Now gather all parameter and event steps until next preset + while ((mSteps.size() > mCurrentStep) && + mSteps[mCurrentStep].type != PRESET) { + mParameterList.push(mSteps[mCurrentStep]); + mCurrentStep++; + } + } + // Apply pending parameter changes + while (!mParameterList.empty()) { + auto &step = mParameterList.front(); + if (mParameterTargetTime <= mCurrentTime) { + if (step.type == PARAMETER) { + // std::cout << "set parameter " << step.presetName << + // std::endl; + for (auto *param : mParameters) { + if (param->getFullAddress() == step.name) { + param->set(step.params); + break; + } + } + } else if (step.type == EVENT) { + for (auto &eventCallback : mEventCallbacks) { + if (eventCallback.eventName == step.name) { + eventCallback.callback(eventCallback.callbackData, step.params); + } + } + } + mParameterList.pop(); + if (!mParameterList.empty()) { + mParameterTargetTime += mParameterList.front().waitTime; + } + } else { + break; + } + } + if (mTargetTime <= mCurrentTime && mParameterList.empty() && + mParameterTargetTime <= mCurrentTime) { + mRunning = false; + } mSequenceLock.unlock(); } +void PresetSequencer::stepSequencer(double dt) { + mCurrentTime += dt; + if (mRunning) { + processTimeChangeRequest(); + + // Process time callback + if ((mCurrentTime - mLastTimeUpdate) >= mTimeChangeMinTimeDelta) { + mLastTimeUpdate = mCurrentTime + mTimeChangeMinTimeDelta; + for (auto cb : mTimeChangeCallbacks) { + cb(float(mCurrentTime)); + } + } + updateSequencer(); + } +} + +void PresetSequencer::stepSequencer() { stepSequencer(mGranularity * 1.0e-9); } + bool PresetSequencer::consumeMessage(osc::Message &m, std::string rootOSCPath) { std::string basePath = rootOSCPath; if (mOSCsubPath.size() > 0) { @@ -627,7 +521,7 @@ bool PresetSequencer::consumeMessage(osc::Message &m, std::string rootOSCPath) { } std::string PresetSequencer::buildFullPath(std::string sequenceName) { - std::string fullName = mDirectory; + std::string fullName = File::conformDirectory(mDirectory); if (mPresetHandler) { fullName = File::conformDirectory(mPresetHandler->getCurrentPath()); } @@ -639,104 +533,32 @@ std::string PresetSequencer::buildFullPath(std::string sequenceName) { return fullName; } -// SequenceServer -// ---------------------------------------------------------------- - -SequenceServer::SequenceServer(std::string oscAddress, int oscPort) - : mServer(nullptr), - mRecorder(nullptr), - // mParamServer(nullptr), - mOSCpath("/sequence") { - mServer = new osc::Recv(oscPort, oscAddress.c_str(), - 0.001); // Is this 1ms wait OK? - if (mServer) { - mServer->handler(*this); - mServer->start(); - } else { - std::cout << "Error starting OSC server." << std::endl; +void PresetSequencer::startCpuThread() { + stopCpuThread(); + if (mTimeMasterMode == TimeMasterMode::TIME_MASTER_CPU) { + mSequencerActive = true; + mSequencerThread = + std::make_unique(PresetSequencer::sequencerFunction, this); } } -SequenceServer::SequenceServer(ParameterServer ¶mServer) - : mServer(nullptr), - // mParamServer(¶mServer), - mOSCpath("/sequence") { - paramServer.registerOSCListener(this); -} - -SequenceServer::~SequenceServer() { - // std::cout << "~SequenceServer()" << std::endl;; - if (mServer) { - mServer->stop(); - delete mServer; - mServer = nullptr; +void PresetSequencer::stopCpuThread() { + stopSequence(false); + if (mPresetHandler) { + mPresetHandler->stopCpuThread(); } -} -void SequenceServer::onMessage(osc::Message &m) { - if (m.addressPattern() == mOSCpath + "/last") { - if (mSequencer && mRecorder) { - std::cout << "start last recorder sequence " - << mRecorder->lastSequenceName() << std::endl; - mSequencer->setHandlerSubDirectory(mRecorder->lastSequenceSubDir()); - mSequencer->playSequence(mRecorder->lastSequenceName()); - } else { - std::cerr << "SequenceRecorder and PresetSequencer must be registered to " - "enable /*/last." - << std::endl; - } - } else { - for (osc::MessageConsumer *consumer : mConsumers) { - if (consumer->consumeMessage(m, mOSCpath)) { - break; - } + if (mSequencerThread) { + mSequencerActive = false; + mRunning = false; + mPlayPromiseObj = std::make_shared>(); + auto playFuture = mPlayPromiseObj->get_future(); + { + std::unique_lock lk(mPlayWaitLock); + mPlayWaitVariable.notify_all(); } + playFuture.get(); + mSequencerThread->join(); + mSequencerThread = nullptr; } } - -SequenceServer &SequenceServer::registerMessageConsumer( - osc::MessageConsumer &consumer) { - mConsumers.push_back(&consumer); - return *this; -} - -SequenceServer &SequenceServer::registerRecorder(SequenceRecorder &recorder) { - mRecorder = &recorder; - mConsumers.push_back(static_cast(&recorder)); - return *this; -} - -SequenceServer &SequenceServer::registerSequencer(PresetSequencer &sequencer) { - mSequencer = &sequencer; - mConsumers.push_back(&sequencer); - return *this; -} - -void SequenceServer::print() { - if (mServer) { - std::cout << "Sequence server listening on: " << mServer->address() << ":" - << mServer->port() << std::endl; - std::cout << "Communicating on path: " << mOSCpath << std::endl; - } - for (auto sender : mOSCSenders) { - std::cout << sender->address() << ":" << sender->port() << std::endl; - } -} - -void SequenceServer::stopServer() { - if (mServer) { - mServer->stop(); - delete mServer; - mServer = nullptr; - } -} - -void SequenceServer::setAddress(std::string address) { mOSCpath = address; } - -std::string SequenceServer::getAddress() { return mOSCpath; } - -void SequenceServer::changeCallback(int value, void *sender, void *userData) { - SequenceServer *server = static_cast(userData); - // Parameter *parameter = static_cast(sender); - server->notifyListeners(server->mOSCpath, value); -} diff --git a/src/ui/al_SequenceRecorder.cpp b/src/ui/al_SequenceRecorder.cpp index a02d6ffa..9b56fe74 100644 --- a/src/ui/al_SequenceRecorder.cpp +++ b/src/ui/al_SequenceRecorder.cpp @@ -1,4 +1,6 @@ +#include "al/ui/al_SequenceRecorder.hpp" + #include #include #include @@ -6,7 +8,6 @@ #include #include "al/io/al_File.hpp" -#include "al/ui/al_SequenceRecorder.hpp" using namespace al; @@ -45,7 +46,7 @@ SequenceRecorder::SequenceRecorder() // step.duration = std::stof(duration); // mSteps.push(step); // std::cout << name << ":" << delta << ":" << duration << -//std::endl; +// std::endl; // } // } // if (f.bad()) { @@ -60,8 +61,8 @@ SequenceRecorder::SequenceRecorder() // void SequenceRecorder::stopSequence() //{ // mRunning = false; -// // mSequenceLock.lock(); // Waits until the sequencer thread -//is done and back at the condition variable +// // mSequenceLock.lock(); // Waits until the sequencer +// thread is done and back at the condition variable //} // std::vector SequenceRecorder::getSequenceList() @@ -73,8 +74,10 @@ SequenceRecorder::SequenceRecorder() // if (info.type() == FileInfo::REG) { // std::string fileName = info.name(); // if (fileName.find(".sequence") == fileName.size() - 9) { -// // Should do better checks, what if '.sequence' is not -//at the end... sequenceList.push_back(fileName.substr(0, fileName.size() - 9)); +// // Should do better checks, what if '.sequence' +// is not +// at the end... +// sequenceList.push_back(fileName.substr(0, fileName.size() - 9)); // } // } // } @@ -107,7 +110,7 @@ void SequenceRecorder::presetChanged(int index, void *sender, void *userData) { std::lock_guard lk(recorder->mSequenceLock); // recorder->mPresetName = presets->getCurrentPresetName(); recorder->mStepToInsert.type = PresetSequencer::PRESET; - recorder->mStepToInsert.presetName = presets->getCurrentPresetName(); + recorder->mStepToInsert.name = presets->getCurrentPresetName(); recorder->mStepToInsert.morphTime = recorder->mPresetHandler->getMorphTime(); recorder->mStepToInsert.waitTime = 0.0; recorder->mSequenceConditionVar.notify_one(); @@ -125,7 +128,7 @@ void SequenceRecorder::recorderFunction(SequenceRecorder *recorder, for (auto *param : recorder->mParameters) { PresetSequencer::Step step; step.type = PresetSequencer::PARAMETER; - step.presetName = param->getFullAddress(); + step.name = param->getFullAddress(); step.waitTime = 0.0f; step.morphTime = 0.0f; step.params = {param->toFloat()}; @@ -155,7 +158,7 @@ void SequenceRecorder::recorderFunction(SequenceRecorder *recorder, if (steps.back().type == PresetSequencer::PARAMETER) { PresetSequencer::Step fillerStep; fillerStep.type = PresetSequencer::PARAMETER; - fillerStep.presetName = "_"; + fillerStep.name = "_"; fillerStep.params = {0.0f}; fillerStep.waitTime = timeDelta; steps.push_back(fillerStep); @@ -169,7 +172,7 @@ void SequenceRecorder::recorderFunction(SequenceRecorder *recorder, steps.push_back(recorder->mStepToInsert); // std::cout << recorder->mStepToInsert.presetName << ":" << - //recorder->mStepToInsert.waitTime << std::endl; + // recorder->mStepToInsert.waitTime << std::endl; } if (steps.size() < 2) { return; @@ -180,15 +183,22 @@ void SequenceRecorder::recorderFunction(SequenceRecorder *recorder, std::string fileText; for (PresetSequencer::Step step : steps) { if (step.type == PresetSequencer::PRESET) { - fileText += step.presetName + ":" + std::to_string(step.morphTime) + ":" + + fileText += step.name + ":" + std::to_string(step.morphTime) + ":" + std::to_string(step.waitTime) + "\n"; - } else { - fileText += "+" + std::to_string(step.waitTime) + ":" + step.presetName + - ":" + std::to_string(step.params[0]) + "\n"; + } else if (step.type == PresetSequencer::PARAMETER) { + if (step.params[0].type() == ParameterField::FLOAT) { + fileText += "+" + std::to_string(step.waitTime) + ":" + + step.name + ":" + + std::to_string(step.params[0].get()) + "\n"; + } else if (step.params[0].type() == ParameterField::STRING) { + fileText += "+" + std::to_string(step.waitTime) + ":" + + step.name + ":" + step.params[0].get() + + "\n"; + } } } - std::string path; + std::string path = recorder->mDirectory; if (recorder->mPresetHandler) { path = File::conformDirectory(recorder->mPresetHandler->getCurrentPath()); } @@ -228,6 +238,10 @@ std::string SequenceRecorder::lastSequenceSubDir() { return mLastSequenceSubDir; } +void SequenceRecorder::setDirectory(std::string directory) { + mDirectory = File::conformDirectory(directory); +} + void SequenceRecorder::registerParameter(ParameterMeta &p) { if (strcmp(typeid(p).name(), typeid(ParameterBool).name()) == 0) { // ParameterBool @@ -235,7 +249,7 @@ void SequenceRecorder::registerParameter(ParameterMeta &p) { param->registerChangeCallback([&, param](float value) { std::lock_guard lk(mSequenceLock); mStepToInsert.type = PresetSequencer::PARAMETER; - mStepToInsert.presetName = param->getFullAddress(); + mStepToInsert.name = param->getFullAddress(); mStepToInsert.params = {param->toFloat()}; mSequenceConditionVar.notify_one(); }); @@ -245,7 +259,7 @@ void SequenceRecorder::registerParameter(ParameterMeta &p) { param->registerChangeCallback([&, param](float value) { std::lock_guard lk(mSequenceLock); mStepToInsert.type = PresetSequencer::PARAMETER; - mStepToInsert.presetName = param->getFullAddress(); + mStepToInsert.name = param->getFullAddress(); mStepToInsert.params = {param->get()}; mSequenceConditionVar.notify_one(); }); diff --git a/src/ui/al_SequenceServer.cpp b/src/ui/al_SequenceServer.cpp new file mode 100644 index 00000000..2ab9b63a --- /dev/null +++ b/src/ui/al_SequenceServer.cpp @@ -0,0 +1,107 @@ +#include "al/ui/al_SequenceServer.hpp" + +#include + +using namespace al; + +// SequenceServer +// ---------------------------------------------------------------- + +SequenceServer::SequenceServer(std::string oscAddress, int oscPort) + : mServer(nullptr), + mRecorder(nullptr), + // mParamServer(nullptr), + mOSCpath("/sequence") { + mServer = new osc::Recv(oscPort, oscAddress.c_str(), + 0.001); // Is this 1ms wait OK? + if (mServer) { + mServer->handler(*this); + mServer->start(); + } else { + std::cout << "Error starting OSC server." << std::endl; + } +} + +SequenceServer::SequenceServer(ParameterServer ¶mServer) + : mServer(nullptr), + // mParamServer(¶mServer), + mOSCpath("/sequence") { + paramServer.registerOSCListener(this); +} + +SequenceServer::~SequenceServer() { + // std::cout << "~SequenceServer()" << std::endl;; + if (mServer) { + mServer->stop(); + delete mServer; + mServer = nullptr; + } +} + +void SequenceServer::onMessage(osc::Message &m) { + if (m.addressPattern() == mOSCpath + "/last") { + if (mSequencer && mRecorder) { + std::cout << "start last recorder sequence " + << mRecorder->lastSequenceName() << std::endl; + mSequencer->setHandlerSubDirectory(mRecorder->lastSequenceSubDir()); + mSequencer->playSequence(mRecorder->lastSequenceName()); + } else { + std::cerr << "SequenceRecorder and PresetSequencer must be registered to " + "enable /*/last." + << std::endl; + } + } else { + for (osc::MessageConsumer *consumer : mConsumers) { + if (consumer->consumeMessage(m, mOSCpath)) { + break; + } + } + } +} + +SequenceServer &SequenceServer::registerMessageConsumer( + osc::MessageConsumer &consumer) { + mConsumers.push_back(&consumer); + return *this; +} + +SequenceServer &SequenceServer::registerRecorder(SequenceRecorder &recorder) { + mRecorder = &recorder; + mConsumers.push_back(static_cast(&recorder)); + return *this; +} + +SequenceServer &SequenceServer::registerSequencer(PresetSequencer &sequencer) { + mSequencer = &sequencer; + mConsumers.push_back(&sequencer); + return *this; +} + +void SequenceServer::print() { + if (mServer) { + std::cout << "Sequence server listening on: " << mServer->address() << ":" + << mServer->port() << std::endl; + std::cout << "Communicating on path: " << mOSCpath << std::endl; + } + for (auto sender : mOSCSenders) { + std::cout << sender->address() << ":" << sender->port() << std::endl; + } +} + +void SequenceServer::stopServer() { + if (mServer) { + mServer->stop(); + delete mServer; + mServer = nullptr; + } +} + +void SequenceServer::setAddress(std::string address) { mOSCpath = address; } + +std::string SequenceServer::getAddress() { return mOSCpath; } + +void SequenceServer::changeCallback(int value, void *sender, void *userData) { + SequenceServer *server = static_cast(userData); + // Parameter *parameter = static_cast(sender); + server->notifyListeners(server->mOSCpath, value); +} diff --git a/src/util/ui/al_FileSelector.cpp b/src/util/ui/al_FileSelector.cpp deleted file mode 100644 index 0a478831..00000000 --- a/src/util/ui/al_FileSelector.cpp +++ /dev/null @@ -1,122 +0,0 @@ - -#include "al/util/ui/al_FileSelector.hpp" -#include "al/util/imgui/al_Imgui.hpp" - -using namespace al; - -FileSelector::FileSelector(std::string globalRoot, std::function function) { - if (function) { - mFilterFunc = function; - } else { - mFilterFunc = [](std::string) {return true;}; - } - mGlobalRoot = File::conformPathToOS(globalRoot); -} - -void FileSelector::start(std::string currentDir) { - mCurrentDir = currentDir; - mSelectedItem = ""; - auto boundFunc = std::bind(&FileSelector::filteringFunctionWrapper, this, std::placeholders::_1); - items = filterInDir(mGlobalRoot + mCurrentDir, boundFunc); - mActive = true; -} - -bool FileSelector::drawFileSelector() { - if (!mActive) { - return false; - } - - ImGui::PushID(this); - - std::string rootButtonText = mGlobalRoot; - - if (rootButtonText.size() != 0) { // Global root set. - ImGui::Text("Global root:%s", rootButtonText.c_str()); - } - ImGui::Text("Current Dir: %s", mCurrentDir.c_str()); - if (ImGui::Button("..")) { - if (mCurrentDir.find('/') == std::string::npos && mCurrentDir.find('\\') == std::string::npos) { - mCurrentDir = ""; - } else { - if (mCurrentDir.find('/') != std::string::npos) { - mCurrentDir = mCurrentDir.substr(0, mCurrentDir.rfind("/")); - } else { - mCurrentDir = mCurrentDir.substr(0, mCurrentDir.rfind("\\")); - } -#ifndef AL_WINDOWS - if (mGlobalRoot.size() == 0 && mCurrentDir == "") { - // On *nix stop at root. - mCurrentDir = "/"; - } -#endif - } - auto boundFunc = std::bind(&FileSelector::filteringFunctionWrapper, this, std::placeholders::_1); - items = filterInDir(rootButtonText + mCurrentDir, boundFunc); - } - // ImGui::SameLine(); - // if (ImGui::Button("Set to current")) { - // mCurrentDir = rootButtonText + File::conformPathToOS(mDataRootPath.getCurrent()) + File::conformPathToOS(mAvailableDatasets.getCurrent()); - // items = filterInDir(rootButtonText + mCurrentDir, [&](FilePath const&fp) { - // return File::isDirectory(rootButtonText + mCurrentDir + "/" + fp.file());}); - // } - bool itemClicked = false; -#ifdef AL_WINDOWS - // show drive names for windows. - if (items.count() == 0 && rootButtonText + mCurrentDir == "") { - items.add(FilePath("C:")); - items.add(FilePath("D:")); - items.add(FilePath("E:")); - items.add(FilePath("F:")); - items.add(FilePath("Z:")); - } -#endif - for(auto item: items) { - std::string itemText = item.file().c_str(); - if (File::isDirectory(item.filepath())) { - itemText = "[DIR] " + itemText; - } - - if (ImGui::Selectable(itemText.c_str(), item.file() == mSelectedItem)) { - mSelectedItem = item.file(); - } - if (ImGui::IsItemClicked(0) && ImGui::IsMouseDoubleClicked(0)) { - std::string newPath; - if (mCurrentDir.size() == 0) { - newPath = mSelectedItem; - } else { - newPath = mCurrentDir + "/" + mSelectedItem; - } - if (File::isDirectory(newPath)) { - mCurrentDir = newPath; - } - mSelectedItem = ""; - itemClicked = true; - } - } - if (itemClicked) { - auto boundFunc = std::bind(&FileSelector::filteringFunctionWrapper, this, std::placeholders::_1); - items = filterInDir(rootButtonText + mCurrentDir, boundFunc); - } - // FIXME push unique ids for these - if (ImGui::Button("Select##Directory")) { - ImGui::PopID(); - return true; - } - ImGui::SameLine(); - if (ImGui::Button("Cancel##Directory")) { - mActive = false; - } - ImGui::PopID(); - return false; -} - -FileList FileSelector::getSelection() { - FileList list; - if (mSelectedItem.size() == 0) { - list.add(FilePath(mCurrentDir)); - } else { - list.add(FilePath(mSelectedItem, mCurrentDir)); - } - return list; -} - diff --git a/test/src/test_audio.cpp b/test/src/test_audio.cpp index 58dd04ad..0159fa0d 100644 --- a/test/src/test_audio.cpp +++ b/test/src/test_audio.cpp @@ -1,79 +1,80 @@ #include -#include "catch.hpp" - -#include "al/math/al_Constants.hpp" #include "al/io/al_AudioIO.hpp" +#include "al/math/al_Constants.hpp" #include "al/system/al_Time.hpp" - +#include "catch.hpp" using namespace al; #ifndef TRAVIS_BUILD -TEST_CASE( "Audio Device Enum" ) { - AudioDevice::printAll(); -} +TEST_CASE("Audio Device Enum") { AudioDevice::printAll(); } static void callback(AudioIOData &io) { - static double phase = 0.0; - static double phaseInc = 2.0 * M_PI * 440.0 / io.framesPerSecond(); - while (io()) { - io.out(0) = std::sin(phase); - phase += phaseInc; - if (phase > 2.0 * M_PI) { - phase -= 2.0 * M_PI; - } + static double phase = 0.0; + static double phaseInc = 2.0 * M_PI * 440.0 / io.framesPerSecond(); + while (io()) { + io.out(0) = std::sin(phase); + phase += phaseInc; + if (phase > 2.0 * M_PI) { + phase -= 2.0 * M_PI; } + } } - -TEST_CASE( "Audio IO Object" ) { - int userData = 5; - AudioIO audioIO; +TEST_CASE("Audio IO Object") { + int userData = 5; + AudioIO audioIO; #ifdef AL_WINDOWS - bool use_out = true; - bool use_in = false; - audioIO.initWithDefaults(callback, &userData, use_out, use_in); + bool use_out = true; + bool use_in = false; + audioIO.initWithDefaults(callback, &userData, use_out, use_in); #else - audioIO.init(callback, &userData, 64, 44100.0, 2, 2); + audioIO.init(callback, &userData, 64, 44100.0, 2, 2); #endif - audioIO.print(); - REQUIRE(audioIO.user() == 5); - REQUIRE(audioIO.open()); - REQUIRE(audioIO.start()); - al_sleep(0.5); - REQUIRE(audioIO.stop()); - REQUIRE(audioIO.close()); - REQUIRE(audioIO.user() == 5); + audioIO.print(); + REQUIRE(audioIO.user() == 5); + REQUIRE(audioIO.open()); + REQUIRE(audioIO.start()); + al_sleep(0.5); + REQUIRE(audioIO.stop()); + REQUIRE(audioIO.close()); + REQUIRE(audioIO.user() == 5); } TEST_CASE("Audio Channels/Virtual Channels") { - AudioIO audioIO; - audioIO.init(nullptr, nullptr, 256, 44100, 1, 1); + AudioIO audioIO; + audioIO.init(nullptr, nullptr, 256, 44100, 1, 1); - // Make sure parameters match those passed to constructor - audioIO.open(); - REQUIRE(audioIO.framesPerBuffer() == 256); - REQUIRE(audioIO.fps() == 44100); - REQUIRE(audioIO.channelsOut() == 1); - REQUIRE(audioIO.channelsIn() == 1); - audioIO.close(); + // Make sure parameters match those passed to constructor + audioIO.open(); + REQUIRE(audioIO.framesPerBuffer() == 256); + REQUIRE(audioIO.fps() == 44100); + REQUIRE(audioIO.channelsOut() == 1); + REQUIRE(audioIO.channelsIn() == 1); + audioIO.close(); - // Test virtual channels - int maxChansOut = AudioDevice::defaultOutput().channelsOutMax(); - int maxChansIn = AudioDevice::defaultInput().channelsInMax(); - audioIO.channelsOut(maxChansOut + 1); - audioIO.channelsIn (maxChansIn + 1); - audioIO.open(); - REQUIRE(audioIO.channelsOutDevice() == maxChansOut); // opened all hardware channels? - REQUIRE(audioIO.channelsOut() == (maxChansOut+1)); // got our extra virtual channel? - REQUIRE(audioIO.channelsInDevice() == maxChansIn); // opened all hardware channels? - REQUIRE(audioIO.channelsIn() == (maxChansIn+1)); // got our extra virtual channel? - audioIO.close(); + // Test virtual channels + audioIO.deviceIn(AudioDevice::defaultInput()); + audioIO.deviceOut(AudioDevice::defaultOutput()); + int maxChansOut = AudioDevice::defaultOutput().channelsOutMax(); + int maxChansIn = AudioDevice::defaultInput().channelsInMax(); + audioIO.channelsOut(maxChansOut + 1); + audioIO.channelsIn(maxChansIn + 1); + audioIO.open(); + REQUIRE(audioIO.channelsOutDevice() == + maxChansOut); // opened all hardware channels? + REQUIRE(audioIO.channelsOut() == + (maxChansOut + 1)); // got our extra virtual channel? + REQUIRE(audioIO.channelsInDevice() == + maxChansIn); // opened all hardware channels? + REQUIRE(audioIO.channelsIn() == + (maxChansIn + 1)); // got our extra virtual channel? + audioIO.close(); } #else -#endif // TRAVIS_BUILD +#endif // TRAVIS_BUILD diff --git a/test/src/test_lbap.cpp b/test/src/test_lbap.cpp index ce4f5d09..feb6e1fb 100644 --- a/test/src/test_lbap.cpp +++ b/test/src/test_lbap.cpp @@ -1,18 +1,17 @@ #include -#include "catch.hpp" - #include "al/io/al_AudioIO.hpp" #include "al/math/al_Functions.hpp" #include "al/sound/al_Lbap.hpp" #include "al/sphere/al_AlloSphereSpeakerLayout.hpp" +#include "catch.hpp" using namespace al; TEST_CASE("LBAP Allosphere") { const int fpb = 16; - SpeakerLayout sl = AlloSphereSpeakerLayout(); + Speakers sl = AlloSphereSpeakerLayout(); Lbap lbapPanner(sl); lbapPanner.compile(); @@ -23,21 +22,21 @@ TEST_CASE("LBAP Allosphere") { audioData.framesPerBuffer(fpb); audioData.framesPerSecond(44100); audioData.channelsIn(0); - audioData.channelsOut(sl.numSpeakers()); + audioData.channelsOut(sl.size()); lbapPanner.prepare(audioData); float samples[fpb]; for (int i = 0; i < fpb; i++) { - samples[i] = i + 0.5; + samples[i] = i + 0.5f; } Pose pose; pose.pos(0, 0, -4); // Front Center audioData.zeroOut(); lbapPanner.renderBuffer(audioData, pose, samples, fpb); - for (int i = 0; i < fpb; i++) { - for (int chan = 0; chan < 54; chan++) { + for (unsigned int i = 0; i < fpb; i++) { + for (unsigned int chan = 0; chan < 54; chan++) { if (chan == 23) { REQUIRE(aeq(audioData.out(23, i), (i + 0.5f))); } else { diff --git a/test/src/test_midi.cpp b/test/src/test_midi.cpp index 48764e7d..d623de66 100644 --- a/test/src/test_midi.cpp +++ b/test/src/test_midi.cpp @@ -1,20 +1,14 @@ -#include "catch.hpp" - #include "al/io/al_MIDI.hpp" +#include "catch.hpp" using namespace al; #ifndef TRAVIS_BUILD -TEST_CASE( "MIDI test" ) { - RtMidiIn midiInput; - RtMidiOut midiOutput; -} - -TEST_CASE( "MIDI test-old API" ) { - MIDIIn midiInput; - MIDIOut midiOutput; +TEST_CASE("MIDI test") { + RtMidiIn midiInput; + RtMidiOut midiOutput; } #endif diff --git a/test/src/test_vbap.cpp b/test/src/test_vbap.cpp index f6f880ae..cf66dc4a 100644 --- a/test/src/test_vbap.cpp +++ b/test/src/test_vbap.cpp @@ -1,190 +1,188 @@ #include -#include "catch.hpp" - #include "al/io/al_AudioIO.hpp" #include "al/sound/al_Vbap.hpp" #include "al/sphere/al_AlloSphereSpeakerLayout.hpp" +#include "catch.hpp" using namespace al; bool almostEqual(float a, float b, float tol = 0.000001) { - return fabs(a -b) < tol; + return fabs(a - b) < tol; } -TEST_CASE ( "VBAP 8 speakers") -{ - const int fpb = 16; - - SpeakerLayout sl = OctalSpeakerLayout(); - Vbap vbapPanner(sl); +TEST_CASE("VBAP 8 speakers") { + const int fpb = 16; -// vbapPanner.print(); - vbapPanner.compile(); + Speakers sl = OctalSpeakerLayout(); + Vbap vbapPanner(sl); - AudioIOData audioData; - audioData.framesPerBuffer(fpb); - audioData.framesPerSecond(44100); - audioData.channelsIn(0); - audioData.channelsOut(sl.numSpeakers()); + // vbapPanner.print(); + vbapPanner.compile(); - float samples[fpb]; - for (int i = 0; i < fpb; i++) { - samples[i] = i + 0.5; - } + AudioIOData audioData; + audioData.framesPerBuffer(fpb); + audioData.framesPerSecond(44100); + audioData.channelsIn(0); + audioData.channelsOut(sl.size()); - Pose pose; - pose.pos(0,0,-4); // Center - audioData.zeroOut(); - vbapPanner.renderBuffer(audioData, pose, samples, fpb); + float samples[fpb]; + for (int i = 0; i < fpb; i++) { + samples[i] = i + 0.5; + } - audioData.frame(0); - for (int i = 0; i < fpb; i++) { + Pose pose; + pose.pos(0, 0, -4); // Center + audioData.zeroOut(); + vbapPanner.renderBuffer(audioData, pose, samples, fpb); - REQUIRE(audioData.out(0,i) == i + 0.5); - for (int chan = 1; chan < 8; chan ++) { - REQUIRE(audioData.out(chan,i) == 0.0f); - } + audioData.frame(0); + for (int i = 0; i < fpb; i++) { + REQUIRE(audioData.out(0, i) == i + 0.5); + for (int chan = 1; chan < 8; chan++) { + REQUIRE(audioData.out(chan, i) == 0.0f); } - - pose.pos(-2,0,0); // Hard Left - audioData.zeroOut(); - vbapPanner.renderBuffer(audioData, pose, samples, fpb); - - audioData.frame(0); - for (int i = 0; i < fpb; i++) { - - REQUIRE(audioData.out(0,i) == 0.0); - REQUIRE(audioData.out(1,i) == 0.0); - REQUIRE(audioData.out(2,i) == i + 0.5); - for (int chan = 3; chan < 8; chan ++) { - REQUIRE(almostEqual(audioData.out(chan,i),0.0f)); - } + } + + pose.pos(-2, 0, 0); // Hard Left + audioData.zeroOut(); + vbapPanner.renderBuffer(audioData, pose, samples, fpb); + + audioData.frame(0); + for (unsigned int i = 0; i < fpb; i++) { + REQUIRE(audioData.out(0, i) == 0.0f); + REQUIRE(audioData.out(1, i) == 0.0f); + REQUIRE(audioData.out(2, i) == i + 0.5f); + for (unsigned int chan = 3; chan < 8; chan++) { + REQUIRE(almostEqual(audioData.out(chan, i), 0.0f)); } - - pose.pos(1,0,-1); // 45 deg. front right - audioData.zeroOut(); - vbapPanner.renderBuffer(audioData, pose, samples, fpb); - - audioData.frame(0); - for (int i = 0; i < fpb; i++) { - REQUIRE(almostEqual(audioData.out(0,i) , 0.0f)); - REQUIRE(almostEqual(audioData.out(7,i) , i + 0.5)); - for (int chan = 1; chan < 7; chan ++) { - REQUIRE(almostEqual(audioData.out(chan,i),0.0f)); - } + } + + pose.pos(1, 0, -1); // 45 deg. front right + audioData.zeroOut(); + vbapPanner.renderBuffer(audioData, pose, samples, fpb); + + audioData.frame(0); + for (unsigned int i = 0; i < fpb; i++) { + REQUIRE(almostEqual(audioData.out(0, i), 0.0f)); + REQUIRE(almostEqual(audioData.out(7, i), i + 0.5)); + for (unsigned int chan = 1; chan < 7; chan++) { + REQUIRE(almostEqual(audioData.out(chan, i), 0.0f)); } - - pose.pos(1 * tan(M_PI * 0.125),0,-1); // 22.5 deg. front right - audioData.zeroOut(); - vbapPanner.renderBuffer(audioData, pose, samples, fpb); - - audioData.frame(0); - for (int i = 0; i < fpb; i++) { - REQUIRE(almostEqual(audioData.out(0,i) , (i + 0.5) * sin(M_PI*0.25))); - REQUIRE(almostEqual(audioData.out(7,i) , (i + 0.5) * sin(M_PI*0.25))); - for (int chan = 1; chan < 7; chan ++) { - REQUIRE(almostEqual(audioData.out(chan,i),0.0f)); - } + } + + pose.pos(1 * tan(M_PI * 0.125), 0, -1); // 22.5 deg. front right + audioData.zeroOut(); + vbapPanner.renderBuffer(audioData, pose, samples, fpb); + + audioData.frame(0); + for (unsigned int i = 0; i < fpb; i++) { + REQUIRE(almostEqual(audioData.out(0, i), (i + 0.5) * sin(M_PI * 0.25))); + REQUIRE(almostEqual(audioData.out(7, i), (i + 0.5) * sin(M_PI * 0.25))); + for (unsigned int chan = 1; chan < 7; chan++) { + REQUIRE(almostEqual(audioData.out(chan, i), 0.0f)); } + } } -TEST_CASE ( "VBAP 3D tetrahedron") -{ - const int fpb = 16; - - SpeakerLayout sl; - sl.addSpeaker(Speaker(0, 0, -30)); - sl.addSpeaker(Speaker(1, 120, -30)); - sl.addSpeaker(Speaker(2, -120, -30)); - sl.addSpeaker(Speaker(3, 0, 90)); - Vbap vbapPanner(sl); - - vbapPanner.set3D(true); - vbapPanner.setOptions(Vbap::KEEP_SAME_ELEVATION); //Curently needed to preserve tetrahedron triplets - vbapPanner.compile(); -// vbapPanner.print(); - - AudioIOData audioData; - audioData.framesPerBuffer(fpb); - audioData.framesPerSecond(44100); - audioData.channelsIn(0); - audioData.channelsOut(sl.numSpeakers()); - - float samples[fpb]; - for (int i = 0; i < fpb; i++) { - samples[i] = i + 0.5; - } - - Pose pose; - pose.pos(0,4*tan(M_PI/6.0),-4); // Center - audioData.zeroOut(); - vbapPanner.renderBuffer(audioData, pose, samples, fpb); - for (int i = 0; i < fpb; i++) { - REQUIRE(almostEqual(audioData.out(0,i) , (i + 0.5) * sin(M_PI*0.25))); - REQUIRE(almostEqual(audioData.out(3,i) , (i + 0.5) * sin(M_PI*0.25))); - for (int chan = 1; chan < 2; chan ++) { - REQUIRE(almostEqual(audioData.out(chan,i),0.0f)); - } - } - - pose.pos(cos(M_PI/6.0),0.0,-sin(M_PI/6.0)); // Middle of right face - audioData.zeroOut(); - vbapPanner.renderBuffer(audioData, pose, samples, fpb); - for (int i = 0; i < fpb; i++) { - REQUIRE(almostEqual(audioData.out(0,i) , (i + 0.5) * sqrt(1/3.0))); - REQUIRE(almostEqual(audioData.out(2,i), (i + 0.5) * sqrt(1/3.0))); // 0.225427702 - REQUIRE(almostEqual(audioData.out(3,i), (i + 0.5) * sqrt(1/3.0))); // 0.323040754 - - REQUIRE(almostEqual(audioData.out(1,i) , 0.0)); - +TEST_CASE("VBAP 3D tetrahedron") { + const int fpb = 16; + + Speakers sl; + sl.emplace_back(Speaker(0, 0, -30)); + sl.emplace_back(Speaker(1, 120, -30)); + sl.emplace_back(Speaker(2, -120, -30)); + sl.emplace_back(Speaker(3, 0, 90)); + Vbap vbapPanner(sl); + + vbapPanner.set3D(true); + vbapPanner.setOptions( + Vbap::KEEP_SAME_ELEVATION); // Curently needed to preserve tetrahedron + // triplets + vbapPanner.compile(); + // vbapPanner.print(); + + AudioIOData audioData; + audioData.framesPerBuffer(fpb); + audioData.framesPerSecond(44100); + audioData.channelsIn(0); + audioData.channelsOut(sl.size()); + + float samples[fpb]; + for (unsigned int i = 0; i < fpb; i++) { + samples[i] = i + 0.5; + } + + Pose pose; + pose.pos(0, 4 * tan(M_PI / 6.0), -4); // Center + audioData.zeroOut(); + vbapPanner.renderBuffer(audioData, pose, samples, fpb); + for (unsigned int i = 0; i < fpb; i++) { + REQUIRE(almostEqual(audioData.out(0, i), (i + 0.5) * sin(M_PI * 0.25))); + REQUIRE(almostEqual(audioData.out(3, i), (i + 0.5) * sin(M_PI * 0.25))); + for (unsigned int chan = 1; chan < 2; chan++) { + REQUIRE(almostEqual(audioData.out(chan, i), 0.0f)); } + } + + pose.pos(cos(M_PI / 6.0), 0.0, -sin(M_PI / 6.0)); // Middle of right face + audioData.zeroOut(); + vbapPanner.renderBuffer(audioData, pose, samples, fpb); + for (unsigned int i = 0; i < fpb; i++) { + REQUIRE(almostEqual(audioData.out(0, i), (i + 0.5) * sqrt(1 / 3.0))); + REQUIRE(almostEqual(audioData.out(2, i), + (i + 0.5) * sqrt(1 / 3.0f))); // 0.225427702 + REQUIRE(almostEqual(audioData.out(3, i), + (i + 0.5) * sqrt(1 / 3.0))); // 0.323040754 + + REQUIRE(almostEqual(audioData.out(1, i), 0.0)); + } } -TEST_CASE ( "VBAP 3D Allosphere") -{ - const int fpb = 16; - - SpeakerLayout sl = AlloSphereSpeakerLayout(); - Vbap vbapPanner(sl); - - vbapPanner.set3D(true); -// vbapPanner.compile(); -//// vbapPanner.print(); - -// AudioIOData audioData; -// audioData.framesPerBuffer(fpb); -// audioData.framesPerSecond(44100); -// audioData.channelsIn(0); -// audioData.channelsOut(sl.numSpeakers()); - -// float samples[fpb]; -// for (int i = 0; i < fpb; i++) { -// samples[i] = i + 0.5; -// } - -// Pose pose; -// pose.pos(0,4*tan(M_PI/6.0),-4); // Center -// audioData.zeroOut(); - -// vbapPanner.renderBuffer(audioData, pose, samples, fpb); -// for (int i = 0; i < fpb; i++) { -// REQUIRE(almostEqual(audioData.out(0,i) , (i + 0.5) * sin(M_PI*0.25))); -// REQUIRE(almostEqual(audioData.out(3,i) , (i + 0.5) * sin(M_PI*0.25))); -// for (int chan = 1; chan < 2; chan ++) { -// REQUIRE(almostEqual(audioData.out(chan,i),0.0f)); -// } -// } - -// pose.pos(cos(M_PI/6.0),0.0,-sin(M_PI/6.0)); // Middle of right face -// audioData.zeroOut(); -// vbapPanner.renderBuffer(audioData, pose, samples, fpb); -// for (int i = 0; i < fpb; i++) { -// REQUIRE(almostEqual(audioData.out(0,i) , (i + 0.5) * sqrt(1/3.0))); -// REQUIRE(almostEqual(audioData.out(2,i), (i + 0.5) * sqrt(1/3.0))); // 0.225427702 -// REQUIRE(almostEqual(audioData.out(3,i), (i + 0.5) * sqrt(1/3.0))); // 0.323040754 - -// REQUIRE(almostEqual(audioData.out(1,i) , 0.0)); - -// } +TEST_CASE("VBAP 3D Allosphere") { + const int fpb = 16; + + Speakers sl = AlloSphereSpeakerLayout(); + Vbap vbapPanner(sl); + + vbapPanner.set3D(true); + // vbapPanner.compile(); + //// vbapPanner.print(); + + // AudioIOData audioData; + // audioData.framesPerBuffer(fpb); + // audioData.framesPerSecond(44100); + // audioData.channelsIn(0); + // audioData.channelsOut(sl.numSpeakers()); + + // float samples[fpb]; + // for (int i = 0; i < fpb; i++) { + // samples[i] = i + 0.5; + // } + + // Pose pose; + // pose.pos(0,4*tan(M_PI/6.0),-4); // Center + // audioData.zeroOut(); + + // vbapPanner.renderBuffer(audioData, pose, samples, fpb); + // for (int i = 0; i < fpb; i++) { + // REQUIRE(almostEqual(audioData.out(0,i) , (i + 0.5) * + // sin(M_PI*0.25))); REQUIRE(almostEqual(audioData.out(3,i) , (i + 0.5) + // * sin(M_PI*0.25))); for (int chan = 1; chan < 2; chan ++) { + // REQUIRE(almostEqual(audioData.out(chan,i),0.0f)); + // } + // } + + // pose.pos(cos(M_PI/6.0),0.0,-sin(M_PI/6.0)); // Middle of right face + // audioData.zeroOut(); + // vbapPanner.renderBuffer(audioData, pose, samples, fpb); + // for (int i = 0; i < fpb; i++) { + // REQUIRE(almostEqual(audioData.out(0,i) , (i + 0.5) * sqrt(1/3.0))); + // REQUIRE(almostEqual(audioData.out(2,i), (i + 0.5) * sqrt(1/3.0))); + // // 0.225427702 REQUIRE(almostEqual(audioData.out(3,i), (i + 0.5) * + // sqrt(1/3.0))); // 0.323040754 + + // REQUIRE(almostEqual(audioData.out(1,i) , 0.0)); + + // } }