Precached images. ToobTuner. key additions.
This commit is contained in:
+7
-3
@@ -754,7 +754,7 @@ public:
|
||||
#else
|
||||
xxx; // TODO!
|
||||
#endif
|
||||
|
||||
int underrunMessagesGiven = 0;
|
||||
try
|
||||
{
|
||||
|
||||
@@ -787,8 +787,12 @@ public:
|
||||
uint64_t underruns = this->underruns;
|
||||
if (underruns != lastUnderrunCount)
|
||||
{
|
||||
Lv2Log::info("Jack - Underrun count: %lu", (unsigned long)underruns);
|
||||
lastUnderrunCount = underruns;
|
||||
if (underrunMessagesGiven < 60) // limit how much log file clutter we generate.
|
||||
{
|
||||
Lv2Log::info("Jack - Underrun count: %lu", (unsigned long)underruns);
|
||||
lastUnderrunCount = underruns;
|
||||
++underrunMessagesGiven;
|
||||
}
|
||||
}
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += pollRateS;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "lv2/urid.lv2/urid.h"
|
||||
#include "lv2.h"
|
||||
#include "lv2/atom.lv2/atom.h"
|
||||
#include "lv2/time/time.h"
|
||||
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
|
||||
#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
|
||||
#include "lv2/lv2plug.in/ns/ext/port-props/port-props.h"
|
||||
@@ -40,8 +41,10 @@
|
||||
#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h"
|
||||
#include <fstream>
|
||||
#include "PiPedalException.hpp"
|
||||
|
||||
#include "Locale.hpp"
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
#define MAP_CHECK() \
|
||||
@@ -115,6 +118,12 @@ void Lv2Host::LilvUris::Initialize(LilvWorld*pWorld)
|
||||
symbolUri = lilv_new_uri(pWorld, LV2_CORE__symbol);
|
||||
nameUri = lilv_new_uri(pWorld, LV2_CORE__name);
|
||||
|
||||
time_Position = lilv_new_uri(pWorld, LV2_TIME__Position);
|
||||
time_barBeat = lilv_new_uri(pWorld, LV2_TIME__barBeat);
|
||||
time_beatsPerMinute = lilv_new_uri(pWorld, LV2_TIME__beatsPerMinute);
|
||||
time_speed = lilv_new_uri(pWorld, LV2_TIME__speed);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Lv2Host::LilvUris::Free()
|
||||
@@ -137,6 +146,11 @@ void Lv2Host::LilvUris::Free()
|
||||
symbolUri.Free();
|
||||
nameUri.Free();
|
||||
|
||||
time_Position.Free();
|
||||
time_barBeat.Free();
|
||||
time_beatsPerMinute.Free();
|
||||
time_speed.Free();
|
||||
|
||||
}
|
||||
|
||||
static std::string nodeAsString(const LilvNode *node)
|
||||
@@ -398,10 +412,12 @@ void Lv2Host::Load(const char *lv2Path)
|
||||
{
|
||||
Lv2Log::debug("Plugin %s (%s) skipped. (Has CV ports).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
|
||||
}
|
||||
#if !SUPPORT_MIDI
|
||||
else if (pluginInfo->plugin_class() == LV2_MIDI_PLUGIN)
|
||||
{
|
||||
Lv2Log::debug("Plugin %s (%s) skipped. (MIDI Plugin).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
|
||||
}
|
||||
#endif
|
||||
else if (!pluginInfo->is_valid())
|
||||
{
|
||||
auto &ports = pluginInfo->ports();
|
||||
@@ -439,6 +455,16 @@ void Lv2Host::Load(const char *lv2Path)
|
||||
Lv2PluginUiInfo info(this, plugin.get());
|
||||
if (plugin->is_valid())
|
||||
{
|
||||
#if SUPPORT_MIDI
|
||||
if (info.audio_inputs() == 0 && !info.has_midi_input())
|
||||
{
|
||||
Lv2Log::debug("Plugin %s (%s) skipped. No inputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||
}
|
||||
else if (info.audio_outputs() == 0 && !info.has_midi_output())
|
||||
{
|
||||
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||
}
|
||||
#else
|
||||
if (info.audio_inputs() == 0)
|
||||
{
|
||||
Lv2Log::debug("Plugin %s (%s) skipped. No audio inputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||
@@ -447,6 +473,7 @@ void Lv2Host::Load(const char *lv2Path)
|
||||
{
|
||||
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
ui_plugins_.push_back(std::move(info));
|
||||
@@ -718,6 +745,7 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
|
||||
is_cv_port_ = is_a(host, LV2_CORE__CVPort);
|
||||
|
||||
supports_midi_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.midiEventNode);
|
||||
supports_time_position_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.time_Position);
|
||||
|
||||
NodeAutoFree designationValue = lilv_port_get(plugin, pPort, host->lilvUris.designationNode);
|
||||
designation_ = nodeAsString(designationValue);
|
||||
@@ -1096,6 +1124,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
|
||||
json_map::reference("is_valid", &Lv2PortInfo::is_valid_),
|
||||
|
||||
json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_),
|
||||
json_map::reference("supports_time_position", &Lv2PortInfo::supports_time_position_),
|
||||
json_map::reference("port_group", &Lv2PortInfo::port_group_),
|
||||
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ private:
|
||||
|
||||
bool is_valid_ = false;
|
||||
bool supports_midi_ = false;
|
||||
bool supports_time_position_ = false;
|
||||
bool is_logarithmic_ = false;
|
||||
int display_priority_ = -1;
|
||||
int range_steps_ = 0;
|
||||
@@ -267,6 +268,7 @@ public:
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_cv_port);
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_valid);
|
||||
LV2_PROPERTY_GETSET_SCALAR(supports_midi);
|
||||
LV2_PROPERTY_GETSET_SCALAR(supports_time_position);
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
|
||||
LV2_PROPERTY_GETSET_SCALAR(display_priority);
|
||||
LV2_PROPERTY_GETSET_SCALAR(range_steps);
|
||||
@@ -381,6 +383,24 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool hasMidiInput() const {
|
||||
for (size_t i = 0; i < ports_.size(); ++i)
|
||||
{
|
||||
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_input())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool hasMidiOutput() const {
|
||||
for (size_t i = 0; i < ports_.size(); ++i)
|
||||
{
|
||||
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_output())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
@@ -598,6 +618,12 @@ private:
|
||||
LilvNodePtr symbolUri;
|
||||
LilvNodePtr nameUri;
|
||||
|
||||
LilvNodePtr time_Position;
|
||||
LilvNodePtr time_barBeat;
|
||||
LilvNodePtr time_beatsPerMinute;
|
||||
LilvNodePtr time_speed;
|
||||
|
||||
|
||||
};
|
||||
LilvUris lilvUris;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ class PiPedalConfiguration
|
||||
private:
|
||||
std::string lv2_path_ = "/usr/lib/lv2:/usr/local/lib/lv2";
|
||||
std::filesystem::path docRoot_;
|
||||
std::filesystem::path webRoot_;
|
||||
std::string local_storage_path_;
|
||||
bool mlock_ = true;
|
||||
std::vector<std::string> reactServerAddresses_ = {"*:5000"};
|
||||
@@ -52,7 +53,10 @@ public:
|
||||
std::filesystem::path GetConfigFilePath() const {
|
||||
return docRoot_ / "config.jason";
|
||||
}
|
||||
void Load(std::filesystem::path path) {
|
||||
const std::filesystem::path& GetWebRoot() const {
|
||||
return webRoot_;
|
||||
}
|
||||
void Load(const std::filesystem::path& path, const std::filesystem::path&webRoot) {
|
||||
std::filesystem::path configPath = path / "config.json";
|
||||
if (!std::filesystem::exists(configPath))
|
||||
{
|
||||
@@ -68,6 +72,8 @@ public:
|
||||
json_reader reader(f);
|
||||
reader.read(this);
|
||||
docRoot_ = path;
|
||||
|
||||
webRoot_ = webRoot;
|
||||
|
||||
}
|
||||
const std::filesystem::path &GetDocRoot() const { return docRoot_; }
|
||||
|
||||
@@ -113,7 +113,7 @@ void PiPedalModel::LoadLv2PluginInfo(const PiPedalConfiguration&configuration)
|
||||
|
||||
void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
||||
{
|
||||
|
||||
this->webRoot = configuration.GetWebRoot();
|
||||
this->jackServerSettings.ReadJackConfiguration();
|
||||
|
||||
|
||||
@@ -1164,4 +1164,9 @@ void PiPedalModel::cancelListenForAtomOutputs(int64_t clientId, int64_t clientHa
|
||||
std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
|
||||
{
|
||||
return this->alsaDevices.GetAlsaDevices();
|
||||
}
|
||||
}
|
||||
|
||||
const std::filesystem::path& PiPedalModel::GetWebRoot() const
|
||||
{
|
||||
return webRoot;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ private:
|
||||
std::unique_ptr<JackHost> jackHost;
|
||||
JackConfiguration jackConfiguration;
|
||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard;
|
||||
std::filesystem::path webRoot;
|
||||
|
||||
|
||||
std::vector<IPiPedalModelSubscriber*> subscribers;
|
||||
@@ -219,7 +220,7 @@ public:
|
||||
void cancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
|
||||
|
||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||
|
||||
const std::filesystem::path& GetWebRoot() const;
|
||||
};
|
||||
|
||||
} // namespace pipedal.
|
||||
+21
-1
@@ -34,6 +34,7 @@
|
||||
#include "WifiChannels.hpp"
|
||||
#include "SysExec.hpp"
|
||||
#include "PiPedalAlsa.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
using namespace pipedal;
|
||||
@@ -344,6 +345,7 @@ private:
|
||||
std::recursive_mutex writeMutex;
|
||||
PiPedalModel &model;
|
||||
static std::atomic<uint64_t> nextClientId;
|
||||
std::string imageList;
|
||||
|
||||
uint64_t clientId;
|
||||
// pedalboard is mutable and not thread-safe.
|
||||
@@ -390,6 +392,19 @@ public:
|
||||
PiPedalSocketHandler(PiPedalModel &model)
|
||||
: model(model), clientId(++nextClientId)
|
||||
{
|
||||
std::stringstream imageList;
|
||||
const std::filesystem::path& webRoot = model.GetWebRoot() / "img";
|
||||
bool firstTime = true;
|
||||
for (const auto&entry: std::filesystem::directory_iterator(webRoot))
|
||||
{
|
||||
if (!firstTime)
|
||||
{
|
||||
imageList << ";";
|
||||
}
|
||||
firstTime = false;
|
||||
imageList << entry.path().filename().string();
|
||||
}
|
||||
this->imageList = imageList.str();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1067,7 +1082,7 @@ public:
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
activeVuSubscriptions.push_back(VuSubscription{subscriptionHandle, instanceId});
|
||||
}
|
||||
this->Reply(replyTo, "addVuSubscriptin", subscriptionHandle);
|
||||
this->Reply(replyTo, "addVuSubscription", subscriptionHandle);
|
||||
}
|
||||
else if (message == "removeVuSubscription")
|
||||
{
|
||||
@@ -1087,6 +1102,11 @@ public:
|
||||
}
|
||||
model.removeVuSubscription(subscriptionHandle);
|
||||
}
|
||||
else if (message == "imageList")
|
||||
{
|
||||
|
||||
this->Reply(replyTo, "imageList", imageList);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::error("Unknown message received: %s", message.c_str());
|
||||
|
||||
+47
-13
@@ -416,6 +416,9 @@ void json_reader::skip_property()
|
||||
int c = is_.peek();
|
||||
switch (c)
|
||||
{
|
||||
case -1:
|
||||
throw_format_error("Premature end of file.");
|
||||
break;
|
||||
case '[':
|
||||
skip_array();
|
||||
break;
|
||||
@@ -442,8 +445,10 @@ void json_reader::skip_string()
|
||||
consume('"');
|
||||
while (true)
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
c = get();
|
||||
if (c == -1) throw_format_error("Premature end of file.");
|
||||
|
||||
if (c == '\"')
|
||||
{
|
||||
if (peek() == '\"')
|
||||
@@ -463,27 +468,54 @@ void json_reader::skip_string()
|
||||
void json_reader::skip_number()
|
||||
{
|
||||
skip_whitespace();
|
||||
char c;
|
||||
while (true)
|
||||
int c;
|
||||
if (is_.peek() == '-')
|
||||
{
|
||||
int ic = is_.peek();
|
||||
if (ic == -1)
|
||||
break;
|
||||
if (is_whitespace((char)ic))
|
||||
get();
|
||||
}
|
||||
if (!std::isdigit(is_.peek()))
|
||||
{
|
||||
throw_format_error("Expecting a number.");
|
||||
}
|
||||
while (std::isdigit(is_.peek()))
|
||||
{
|
||||
get();
|
||||
}
|
||||
if (is_.peek() == '.')
|
||||
{
|
||||
get();
|
||||
}
|
||||
while (std::isdigit(is_.peek()))
|
||||
{
|
||||
get();
|
||||
}
|
||||
c = is_.peek();
|
||||
if (c == 'e' || c == 'E')
|
||||
{
|
||||
get();
|
||||
c = is_.peek();
|
||||
if (c == '+' || c == 'i')
|
||||
{
|
||||
break;
|
||||
get();
|
||||
}
|
||||
c = get();
|
||||
while (std::isdigit(is_.peek()))
|
||||
{
|
||||
get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
void json_reader::skip_array()
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
consume('[');
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (peek() == ']')
|
||||
c = peek();
|
||||
if (c == -1) throw_format_error("Premature end of file.");
|
||||
|
||||
if (c == ']')
|
||||
{
|
||||
c = get();
|
||||
break;
|
||||
@@ -499,11 +531,13 @@ void json_reader::skip_array()
|
||||
|
||||
void json_reader::skip_object()
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
consume('{');
|
||||
while (true)
|
||||
{
|
||||
if (peek() == '}')
|
||||
c = peek();
|
||||
if (c == -1) throw_format_error("Premature end of file.");
|
||||
if (c == '}')
|
||||
{
|
||||
c = get();
|
||||
break;
|
||||
|
||||
+48
-5
@@ -27,6 +27,7 @@
|
||||
#include <sstream>
|
||||
#include "HtmlHelper.hpp"
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include "PiPedalException.hpp"
|
||||
|
||||
|
||||
@@ -243,6 +244,7 @@ class json_writer {
|
||||
public:
|
||||
const char*CRLF;
|
||||
private:
|
||||
bool allowNaN_ = true;
|
||||
std::ostream &os;
|
||||
int indent_level;
|
||||
bool compressed;
|
||||
@@ -276,13 +278,17 @@ public:
|
||||
os << text;
|
||||
}
|
||||
using string_view = boost::string_view;
|
||||
json_writer(std::ostream &os, bool compressed = false)
|
||||
json_writer(std::ostream &os, bool compressed = false, bool allowNaN = true)
|
||||
: os(os)
|
||||
, compressed(compressed)
|
||||
, allowNaN_(allowNaN)
|
||||
, indent_level(0)
|
||||
{
|
||||
this->CRLF = compressed? "" : "\r\n";
|
||||
}
|
||||
bool allowNaN() const { return allowNaN_; }
|
||||
void allowNaN(bool allow) { allowNaN_ = allow; }
|
||||
|
||||
void write(long long value)
|
||||
{
|
||||
os << value;
|
||||
@@ -342,11 +348,21 @@ public:
|
||||
}
|
||||
void write(float f)
|
||||
{
|
||||
os << std::setprecision(std::numeric_limits<float>::max_digits10) << f; // round-trip format
|
||||
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
||||
{
|
||||
os << "NaN";
|
||||
} else {
|
||||
os << std::setprecision(std::numeric_limits<float>::max_digits10) << f; // round-trip format
|
||||
}
|
||||
}
|
||||
void write(double f)
|
||||
{
|
||||
os << std::setprecision(std::numeric_limits<double>::max_digits10) << f; // round-trip format
|
||||
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
||||
{
|
||||
os << "NaN";
|
||||
} else {
|
||||
os << std::setprecision(std::numeric_limits<double>::max_digits10) << f; // round-trip format
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void write(const std::vector<T> &value)
|
||||
@@ -526,20 +542,26 @@ public:
|
||||
|
||||
|
||||
class json_reader {
|
||||
private:
|
||||
std::istream &is_;
|
||||
|
||||
const uint16_t UTF16_SURROGATE_1_BASE = 0xD800U;
|
||||
const uint16_t UTF16_SURROGATE_2_BASE = 0xDC00U;
|
||||
const uint16_t UTF16_SURROGATE_MASK = 0x3FFU;
|
||||
bool allowNaN_ = true;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
json_reader(std::istream &input)
|
||||
json_reader(std::istream &input, bool allowNaN = true)
|
||||
: is_(input)
|
||||
{
|
||||
|
||||
this->allowNaN_ = allowNaN;
|
||||
}
|
||||
|
||||
bool allowNaN() const { return allowNaN_;}
|
||||
void allowNaN(bool allow) { allowNaN_ = allow; }
|
||||
|
||||
private:
|
||||
void throw_format_error(const char*error);
|
||||
|
||||
@@ -719,11 +741,32 @@ public:
|
||||
|
||||
void read(float*value) {
|
||||
skip_whitespace();
|
||||
if (allowNaN_)
|
||||
{
|
||||
if (peek() == 'N')
|
||||
{
|
||||
consumeToken("NaN","Expecting a number.");
|
||||
*value = std::nanf("");
|
||||
return;
|
||||
}
|
||||
}
|
||||
is_ >> *value;
|
||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
||||
}
|
||||
void read(double*value) {
|
||||
skip_whitespace();
|
||||
if (allowNaN_)
|
||||
{
|
||||
if (peek() == 'N')
|
||||
{
|
||||
consumeToken("NaN","Expecting a number.");
|
||||
|
||||
*value = std::nan("");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
is_ >> *value;
|
||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
||||
}
|
||||
|
||||
+1
-1
@@ -480,7 +480,7 @@ int main(int argc, char *argv[])
|
||||
PiPedalConfiguration configuration;
|
||||
try
|
||||
{
|
||||
configuration.Load(doc_root);
|
||||
configuration.Load(doc_root,web_root);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
// Diagnostic type used to report calculated type T in an error message.
|
||||
template <typename T> class TypeDisplay;
|
||||
|
||||
#ifndef SUPPORT_MIDI // currently, only whether midi plugins can be loaded. No routing or handling implemented (yet).
|
||||
#define SUPPORT_MIDI 0
|
||||
#endif
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
Reference in New Issue
Block a user