NATURESPEAK-D/src/voicemachineApp.cpp

281 lines
7.9 KiB
C++
Raw Normal View History

2022-03-20 11:57:12 +01:00
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
2022-03-20 12:25:41 +01:00
#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"
2022-03-20 11:57:12 +01:00
using namespace ci;
using namespace ci::app;
using namespace std;
2022-03-20 12:25:41 +01:00
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 {
2022-03-20 11:57:12 +01:00
public:
2022-03-20 12:25:41 +01:00
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<std::string, Voice*> _map_voices;
Voice* _current_voice = NULL;
Anim<ColorA8u> _background = ColorA8u::black();
Anim<ColorA8u> _voice_color = ColorA8u::white();
// ColorA8u _background = ColorA8u::black();
// ColorA8u _voice_color = ColorA8u::white();
/*- NET -*/
std::shared_ptr<asio::io_service> _io_service;
std::shared_ptr<asio::io_service::work> _work;
std::thread _thread;
std::shared_ptr<Receiver> _receiver;
std::map<uint64_t, protocol::endpoint> _connections;
std::mutex _command_mutex;
// VoiceAloud audio = VoiceAloud(100, 0.5f);
2022-03-20 11:57:12 +01:00
};
2022-03-20 12:25:41 +01:00
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<int>();
_receiver = std::shared_ptr<Receiver>(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<int>();
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<void(Voice*)> utterance_cb = [=](Voice* v) {
this->utterance(v);
};
std::function<void(Voice*)> 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<std::mutex> 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()
2022-03-20 11:57:12 +01:00
{
2022-03-20 12:25:41 +01:00
// 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<asio::io_service> &service){
service->run();
}, _io_service));
2022-03-20 11:57:12 +01:00
}
2022-03-20 12:25:41 +01:00
void VoiceMachineApp::update()
2022-03-20 11:57:12 +01:00
{
2022-03-20 12:25:41 +01:00
for(const auto& [n, v]: _map_voices)
v->update();
// audio.update();
2022-03-20 11:57:12 +01:00
}
2022-03-20 12:25:41 +01:00
void VoiceMachineApp::draw()
2022-03-20 11:57:12 +01:00
{
2022-03-20 12:25:41 +01:00
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);
}
2022-03-20 11:57:12 +01:00
}
2022-03-20 12:25:41 +01:00
void VoiceMachineApp::cleanup()
2022-03-20 11:57:12 +01:00
{
2022-03-20 12:25:41 +01:00
_work.reset();
_io_service->stop();
_thread.join();
2022-03-20 11:57:12 +01:00
}
2022-03-20 12:25:41 +01:00
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 )