#include "cinder/app/App.h" #include "cinder/app/RendererGl.h" #include "cinder/gl/gl.h" #include "cinder/gl/TextureFont.h" #include "cinder/osc/Osc.h" #include "cinder/Json.h" #include "cinder/Log.h" #include "cinder/Timeline.h" #include "Voice.h" using namespace ci; using namespace ci::app; using namespace std; int WINDOW_H = 1500; float RATIO = 0.5625; /*- NET -*/ using Receiver = osc::ReceiverUdp; using protocol = asio::ip::udp; std::string hexA(ColorA8u c) { //https://stackoverflow.com/questions/5100718/integer-to-hex-string-in-c uint32_t cu = c.a << 24 | c.r << 16 | c.g << 8 | c.b; std::stringstream hex_s; hex_s << "0x" << std::setfill ('0') << std::setw(sizeof(uint32_t)) << std::hex << cu; return hex_s.str(); } ColorA8u lerp_color(const ColorA8u &start, const ColorA8u &end, float t ) { uint8_t r = abs(floor(start.r + (end.r - start.r) * t)); uint8_t g = abs(floor(start.g + (end.g - start.g) * t)); uint8_t b = abs(floor(start.b + (end.b - start.b) * t)); uint8_t a = abs(floor(start.a + (end.a - start.a) * t)); return ColorA8u(r, g, b, a); } class VoiceMachineApp : public App { public: VoiceMachineApp(); void setup() override; void update() override; void draw() override; void cleanup() override; void keyDown( KeyEvent event ) override; void stage_config(); void save_config(); void utterance(Voice* v); void change(Voice* v); void clear(); std::map _map_voices; Voice* _current_voice = NULL; Anim _background = ColorA8u::black(); Anim _voice_color = ColorA8u::white(); // ColorA8u _background = ColorA8u::black(); // ColorA8u _voice_color = ColorA8u::white(); /*- NET -*/ std::shared_ptr _io_service; std::shared_ptr _work; std::thread _thread; std::shared_ptr _receiver; std::map _connections; std::mutex _command_mutex; // VoiceAloud audio = VoiceAloud(100, 0.5f); }; VoiceMachineApp::VoiceMachineApp() : _io_service(new asio::io_service), _work(new asio::io_service::work(*_io_service)) // _receiver(PORT, protocol::v4(), *_io_service) {} void VoiceMachineApp::stage_config() { try { const JsonTree config(loadAsset("voice.config.json")); // RECEIVER PORT int port = config["port_voicemachine"].getValue(); _receiver = std::shared_ptr(new Receiver(port, protocol::v4(), *_io_service)); //VOICES for(auto &voice: config["voices"].getChildren()){ std::string name = voice["name"].getValue(); std::string channel = voice["osc_channel"]["root"].getValue(); std::string font = voice["font"].getValue(); int size = voice["size"].getValue(); std::string hexcolor = voice["color"].getValue(); ColorA8u color = ColorA8u::hexA(std::stoul(hexcolor, nullptr, 16)); std::string hexbackground = voice["background"].getValue(); ColorA background = ColorA::hexA(std::stoul(hexbackground, nullptr, 16)); std::function utterance_cb = [=](Voice* v) { this->utterance(v); }; std::function change_cb = [=](Voice* v) { this->change(v); }; Voice* v = new Voice(name, channel, font, size, color, background, utterance_cb, change_cb); _map_voices[name] = v; // //garbage // _current_voice = v; } std::string command_channel = config["command_osc_channel"].getValue(); _receiver->setListener(command_channel, [&](const osc::Message &m){ std::lock_guard lock(_command_mutex); std::string cmd = m[0].string(); console() << "command: " << cmd << endl; if(cmd == "save") save_config(); else if (cmd == "clear") clear(); }); } catch (ci::Exception &exc) { CI_LOG_D("Failed to stage config: " << exc.what()); } } void VoiceMachineApp::save_config() { try { JsonTree config(loadAsset("voice.config.json")); JsonTree voices = config.getChild("voices"); for(int i = 0; i < voices.getNumChildren(); i++) { Voice* v = _map_voices[voices[i]["name"].getValue()]; voices[i]["font"] = JsonTree("font", v->_font.getName()); voices[i]["size"] = JsonTree("size", v->_size); voices[i]["color"] = JsonTree("color", hexA(v->_color)); voices[i]["background"] = JsonTree("background", hexA(v->_background)); } config["voices"] = voices; config.write(getAssetPath("voice.config.json")); CI_LOG_D("saved config"); } catch (ci::Exception &exc) { CI_LOG_D("Failed to save config: " << exc.what()); } } void VoiceMachineApp::utterance(Voice* v) { console() << "utterance : " << v->_name << endl; if(_current_voice != v) { _current_voice = v; timeline().apply( &_voice_color, _current_voice->_color, 4.5f, EaseInSine(), lerp_color ); timeline().apply( &_background, _current_voice->_background, 7.5f, EaseInCubic(), lerp_color ).appendTo(&_voice_color); } } void VoiceMachineApp::change(Voice* v) { console() << "change : " << v->_name << endl; _current_voice = v; _background = _current_voice->_background; _voice_color = _current_voice->_color; } void VoiceMachineApp::clear() { console() << "clearing" << endl; _current_voice = NULL; } void VoiceMachineApp::setup() { // audio.setup(); stage_config(); for(const auto& [n, v]: _map_voices) v->setup(*_receiver); try { _receiver->bind(); } catch (const osc::Exception &e) { CI_LOG_E("Error receiver bind: " << e.what() << " - " << e.value()); quit(); } _receiver->listen( [](asio::error_code e, protocol::endpoint end) -> bool { if(e){ CI_LOG_E("Error receiver listen: " << e.message() << " - " << e.value() << " - " << end); return false; } return true; }); //3. start thread _thread = std::thread(std::bind( [](std::shared_ptr &service){ service->run(); }, _io_service)); } void VoiceMachineApp::update() { for(const auto& [n, v]: _map_voices) v->update(); // audio.update(); } void VoiceMachineApp::draw() { gl::setMatricesWindow( getWindowSize() ); gl::enableAlphaBlending(); gl::clear(_background.value()); gl::color(_voice_color); if(_current_voice){ Rectf bounds( 40, _current_voice->_tex->getAscent() + 40, getWindowWidth() - 40, getWindowHeight() - 40 ); _current_voice->draw(bounds); } } void VoiceMachineApp::cleanup() { _work.reset(); _io_service->stop(); _thread.join(); } void VoiceMachineApp::keyDown( KeyEvent event ) { switch( event.getChar() ) { case 's': { CI_LOG_D("saving config..."); save_config(); break; } // case '-': // mFont = Font( mFont.getName(), mFont.getSize() - 1 ); // mTextureFont = gl::TextureFont::create( mFont ); // break; } } auto settingsFunc = [](App::Settings *settings) { settings->setMultiTouchEnabled( false ); settings->setFrameRate(25); settings->setWindowSize(int(WINDOW_H * RATIO), WINDOW_H ); // settings->setFullScreen(); //FullScreenOptions& exclusive( bool enable = true ) }; CINDER_APP( VoiceMachineApp, RendererGl, settingsFunc )