add some app infrastructure services
This commit is contained in:
@@ -22,14 +22,14 @@ FetchContent_Declare(
|
|||||||
GIT_REPOSITORY https://github.com/thestk/rtmidi.git
|
GIT_REPOSITORY https://github.com/thestk/rtmidi.git
|
||||||
GIT_TAG 6.0.0
|
GIT_TAG 6.0.0
|
||||||
)
|
)
|
||||||
# FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
# libconfig
|
libconfig
|
||||||
# GIT_REPOSITORY https://github.com/hyperrealm/libconfig.git
|
GIT_REPOSITORY https://github.com/hyperrealm/libconfig.git
|
||||||
# GIT_TAG v1.8.2
|
GIT_TAG v1.8.2
|
||||||
# )
|
)
|
||||||
FetchContent_MakeAvailable(rtaudio)
|
FetchContent_MakeAvailable(rtaudio)
|
||||||
FetchContent_MakeAvailable(rtmidi)
|
FetchContent_MakeAvailable(rtmidi)
|
||||||
# FetchContent_MakeAvailable(libconfig)
|
FetchContent_MakeAvailable(libconfig)
|
||||||
|
|
||||||
# needs to be preinstalled
|
# needs to be preinstalled
|
||||||
find_package(Qt6 REQUIRED COMPONENTS
|
find_package(Qt6 REQUIRED COMPONENTS
|
||||||
@@ -53,12 +53,18 @@ target_link_libraries(sonobulus_core PRIVATE
|
|||||||
Qt6::Quick
|
Qt6::Quick
|
||||||
rtaudio
|
rtaudio
|
||||||
rtmidi
|
rtmidi
|
||||||
|
libconfig++
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(sonobulus_core PRIVATE
|
target_include_directories(sonobulus_core PRIVATE
|
||||||
${CMAKE_SOURCE_DIR}/src/
|
${CMAKE_SOURCE_DIR}/src/
|
||||||
${rtaudio_SOURCE_DIR}
|
${rtaudio_SOURCE_DIR}
|
||||||
${rtmidi_SOURCE_DIR}
|
${rtmidi_SOURCE_DIR}
|
||||||
|
${libconfig_SOURCE_DIR}/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(sonobulus_core PRIVATE
|
||||||
|
BINARY_DIR="${CMAKE_BINARY_DIR}"
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_executable(sonobulus
|
qt_add_executable(sonobulus
|
||||||
@@ -74,6 +80,7 @@ qt_add_qml_module(sonobulus
|
|||||||
target_include_directories(sonobulus PRIVATE
|
target_include_directories(sonobulus PRIVATE
|
||||||
${rtaudio_SOURCE_DIR}
|
${rtaudio_SOURCE_DIR}
|
||||||
${rtmidi_SOURCE_DIR}
|
${rtmidi_SOURCE_DIR}
|
||||||
|
${libconfig_SOURCE_DIR}/lib
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(sonobulus PRIVATE
|
target_link_libraries(sonobulus PRIVATE
|
||||||
@@ -95,6 +102,7 @@ if (WIN32)
|
|||||||
TARGET sonobulus POST_BUILD
|
TARGET sonobulus POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
$<TARGET_FILE:rtaudio>
|
$<TARGET_FILE:rtaudio>
|
||||||
|
$<TARGET_FILE:libconfig++>
|
||||||
$<TARGET_FILE_DIR:sonobulus>
|
$<TARGET_FILE_DIR:sonobulus>
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
20
config/sonobulus.cfg
Normal file
20
config/sonobulus.cfg
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
Loggers = (
|
||||||
|
{
|
||||||
|
Id = "Engine";
|
||||||
|
|
||||||
|
FlagsEnabled = (
|
||||||
|
"Debug",
|
||||||
|
"Info",
|
||||||
|
"Warning",
|
||||||
|
"Error"
|
||||||
|
);
|
||||||
|
|
||||||
|
ShowTime = true;
|
||||||
|
ShowSourceTrace = false;
|
||||||
|
CoutEnabled = true;
|
||||||
|
|
||||||
|
FileEnabled = true;
|
||||||
|
FilePath = "Engine.log";
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
#include "ConfigService.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
ConfigService::ConfigService(const std::string& filePath) {
|
||||||
|
|
||||||
|
if(!loadFromFile(filePath)) {
|
||||||
|
std::cout << "Error loading file " << filePath << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigService::loadFromFile(const std::string& filePath) {
|
||||||
|
try {
|
||||||
|
config_.clear();
|
||||||
|
config_.readFile(filePath.c_str());
|
||||||
|
lastError_.clear();
|
||||||
|
return true;
|
||||||
|
} catch (const libconfig::FileIOException&) {
|
||||||
|
lastError_ = "Unable to read config file: " + filePath;
|
||||||
|
} catch (const libconfig::ParseException& error) {
|
||||||
|
lastError_ = std::string("Parse error in ") + error.getFile() + ":" +
|
||||||
|
std::to_string(error.getLine()) + " - " + error.getError();
|
||||||
|
} catch (const std::exception& error) {
|
||||||
|
lastError_ = error.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigService::getLoggerConfig(const std::string& id, LoggerConfig* loggerConfig) const {
|
||||||
|
try {
|
||||||
|
const libconfig::Setting& loggers = config_.lookup("Loggers");
|
||||||
|
for (int index = 0; index < loggers.getLength(); ++index) {
|
||||||
|
const libconfig::Setting& loggerSetting = loggers[index];
|
||||||
|
|
||||||
|
std::string loggerId;
|
||||||
|
if (!loggerSetting.lookupValue("Id", loggerId) || loggerId != id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseLoggerConfig(loggerSetting, loggerConfig);
|
||||||
|
}
|
||||||
|
} catch (const libconfig::SettingException& ex) {
|
||||||
|
std::cout << "libconfig setting exception: " << ex.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& ConfigService::lastError() const {
|
||||||
|
return lastError_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigService::parseLoggerConfig(const libconfig::Setting& loggerSetting, LoggerConfig* loggerConfig) const {
|
||||||
|
|
||||||
|
if (!loggerSetting.lookupValue("Id", loggerConfig->id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const libconfig::Setting& flags = loggerSetting.lookup("FlagsEnabled");
|
||||||
|
for (int index = 0; index < flags.getLength(); ++index) {
|
||||||
|
loggerConfig->flagsEnabled.push_back(static_cast<const char*>(flags[index]));
|
||||||
|
}
|
||||||
|
} catch (const libconfig::SettingException&) {
|
||||||
|
loggerConfig->flagsEnabled.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerSetting.lookupValue("ShowTime", loggerConfig->showTime);
|
||||||
|
loggerSetting.lookupValue("ShowSourceTrace", loggerConfig->showSourceTrace);
|
||||||
|
loggerSetting.lookupValue("CoutEnabled", loggerConfig->coutEnabled);
|
||||||
|
loggerSetting.lookupValue("FileEnabled", loggerConfig->fileEnabled);
|
||||||
|
loggerSetting.lookupValue("FilePath", loggerConfig->filePath);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,6 +1,47 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libconfig.h++>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct LoggerConfig {
|
||||||
|
std::string id;
|
||||||
|
std::vector<std::string> flagsEnabled;
|
||||||
|
bool showTime = false;
|
||||||
|
bool showSourceTrace = false;
|
||||||
|
bool coutEnabled = false;
|
||||||
|
bool fileEnabled = false;
|
||||||
|
std::string filePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AudioConfig {
|
||||||
|
uint32_t sampleRate;
|
||||||
|
uint32_t channels;
|
||||||
|
uint32_t stereoMode;
|
||||||
|
uint32_t bufferSize;
|
||||||
|
float pitchStandard;
|
||||||
|
int32_t midiHome;
|
||||||
|
int32_t notesPerOctave;
|
||||||
|
};
|
||||||
|
|
||||||
class ConfigService {
|
class ConfigService {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ConfigService(const std::string& filePath);
|
||||||
|
~ConfigService() = default;
|
||||||
|
|
||||||
|
bool loadFromFile(const std::string& filePath);
|
||||||
|
|
||||||
|
bool getLoggerConfig(const std::string& id, LoggerConfig* loggerConfig) const;
|
||||||
|
const std::string& lastError() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool parseLoggerConfig(const libconfig::Setting& loggerSetting, LoggerConfig* loggerConfig) const;
|
||||||
|
|
||||||
|
libconfig::Config config_;
|
||||||
|
std::string lastError_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
|
||||||
|
#include <chrono> // Tracking the time when the log function is called
|
||||||
|
#include <string>
|
||||||
|
#include <source_location>
|
||||||
|
#include "LoggerService.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
LoggerService::LoggerService(ConfigService* config, const std::string& loggerId) {
|
||||||
|
|
||||||
|
if(!(config->getLoggerConfig(loggerId, &configuration_))) {
|
||||||
|
std::cout << "Failed to get logger configuration fom config service" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
standardOutputEnabled_ = configuration_.coutEnabled;
|
||||||
|
fileOutputEnabled_ = configuration_.fileEnabled;
|
||||||
|
additionaldetailsEnabled_ = configuration_.showSourceTrace;
|
||||||
|
timeEnabled_ = configuration_.showTime;
|
||||||
|
id_ = configuration_.id;
|
||||||
|
|
||||||
|
for(std::string& flag : configuration_.flagsEnabled) {
|
||||||
|
bool found = false;
|
||||||
|
for(const char* validFlag : LogFlagStrings) {
|
||||||
|
if(flag == std::string(validFlag)) {
|
||||||
|
found = true;
|
||||||
|
for(int i = 0; i < LogFlag::Count; i++) {
|
||||||
|
if(LogFlagStrings[i] == flag) {
|
||||||
|
activeFlags_.emplace_back(static_cast<LogFlag>(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
std::cout << "Log flag '" << flag << "' in configuration file is not a valid log flag" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto now = std::chrono::system_clock::now();
|
||||||
|
const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
|
||||||
|
|
||||||
|
std::string finaltime = std::ctime(&t_c);
|
||||||
|
|
||||||
|
finaltime.pop_back(); // removing the newline
|
||||||
|
|
||||||
|
std::replace(finaltime.begin(),finaltime.end(), ':' , '-'); // filenames cant have dashes
|
||||||
|
std::replace(finaltime.begin(),finaltime.end(), ' ' , '_');
|
||||||
|
|
||||||
|
fs::create_directories(std::string(BINARY_DIR) + "/" + finaltime);
|
||||||
|
std::string logPath = std::string(BINARY_DIR) + "/" + finaltime + "/" + configuration_.filePath;
|
||||||
|
|
||||||
|
if(fileOutputEnabled_) outfile_.open(logPath);
|
||||||
|
|
||||||
|
log("Logger", LogFlag::Info, "Logger initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggerService::~LoggerService() {
|
||||||
|
if(outfile_) outfile_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoggerService::log(std::string component, LogFlag flag, std::string message, std::source_location Source) {
|
||||||
|
|
||||||
|
// check if flag is in the list of active flags
|
||||||
|
bool culled = true;
|
||||||
|
for(LogFlag& testFlag : activeFlags_) {
|
||||||
|
if(flag == testFlag) {
|
||||||
|
culled = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(culled) return;
|
||||||
|
|
||||||
|
std::string finalmessage = "";
|
||||||
|
|
||||||
|
if(timeEnabled_) {
|
||||||
|
finalmessage = finalmessage + "[" + "Not Implemented" + "] ";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string componentTrace = "[" + id_ + ": " + component + "] ";
|
||||||
|
finalmessage += componentTrace; // component is the section of the program (For example Mesh or Engine) that is calling the logger
|
||||||
|
|
||||||
|
std::string level = "";
|
||||||
|
|
||||||
|
level = LogFlagStrings[flag];
|
||||||
|
|
||||||
|
// level.append(7 - level.length(), ' ') pads out the level string with whitespace so every line is aligned the same
|
||||||
|
// it looked weird though
|
||||||
|
finalmessage = finalmessage + "[" + level + "] ";
|
||||||
|
finalmessage = finalmessage + message + " ";
|
||||||
|
|
||||||
|
if (additionaldetailsEnabled_) {
|
||||||
|
finalmessage = finalmessage + "[Function: " + Source.function_name() + "]" + " " + "[Line: " + std::to_string(Source.line()) + "]" + " " + "[File: " + Source.file_name() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(standardOutputEnabled_) {
|
||||||
|
std::cout << finalmessage << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fileOutputEnabled_) {
|
||||||
|
outfile_ << finalmessage << std::endl;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <source_location>
|
||||||
|
|
||||||
|
#include "ConfigService.hpp"
|
||||||
|
|
||||||
|
enum LogFlag {
|
||||||
|
Debug,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr const char* LogFlagStrings[] = {
|
||||||
|
"Debug",
|
||||||
|
"Info",
|
||||||
|
"Warning",
|
||||||
|
"Error"
|
||||||
|
};
|
||||||
|
|
||||||
|
class LoggerService {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
LoggerService(ConfigService* config, const std::string& loggerId);
|
||||||
|
~LoggerService();
|
||||||
|
|
||||||
|
void log(std::string component, LogFlag flag, std::string message, std::source_location Source = std::source_location::current()); // Using the <source_location>
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool standardOutputEnabled_;
|
||||||
|
bool fileOutputEnabled_;
|
||||||
|
bool additionaldetailsEnabled_;
|
||||||
|
bool timeEnabled_;
|
||||||
|
std::ofstream outfile_;
|
||||||
|
std::string id_;
|
||||||
|
|
||||||
|
std::vector<LogFlag> activeFlags_;
|
||||||
|
|
||||||
|
LoggerConfig configuration_;
|
||||||
|
};
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
|
|
||||||
#include "TimerComponent.hpp"
|
#include "TimerComponent.hpp"
|
||||||
|
#include "LoggerService.hpp"
|
||||||
|
#include "ConfigService.hpp"
|
||||||
#include "synth/AudioEngine.hpp"
|
#include "synth/AudioEngine.hpp"
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
@@ -28,8 +30,12 @@ int main(int argc, char* argv[]) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create app objects
|
||||||
|
ConfigService config = ConfigService("config/sonobulus.cfg");
|
||||||
|
LoggerService logger = LoggerService(&config, "Engine");
|
||||||
|
|
||||||
// audio synthesizer doohickey
|
// audio synthesizer doohickey
|
||||||
AudioEngine audioEngine = AudioEngine();
|
AudioEngine audioEngine = AudioEngine(&logger);
|
||||||
audioEngine.start();
|
audioEngine.start();
|
||||||
|
|
||||||
// execute app
|
// execute app
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
|
|
||||||
AudioEngine::AudioEngine() {
|
AudioEngine::AudioEngine(LoggerService* logger) : logger_(logger) {
|
||||||
|
|
||||||
if(audioDevice_.getDeviceCount() < 1) {
|
if(audioDevice_.getDeviceCount() < 1) {
|
||||||
std::cout << "No audio devices found" << std::endl;
|
std::cout << "No audio devices found" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(logger_ == nullptr) std::cout << "err: logger nullptr" << std::endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioEngine::~AudioEngine() {
|
AudioEngine::~AudioEngine() {
|
||||||
@@ -41,7 +43,8 @@ bool AudioEngine::start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
std::cout << "sample rate: " << sampleRate_ << " buffer frames: " << bufferFrames_ << std::endl;
|
std::string msg = "sample rate: " + std::to_string(sampleRate_) + ", buffer frames: " + std::to_string(bufferFrames_);
|
||||||
|
logger_->log("AudioEngine", LogFlag::Info, msg);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -58,7 +61,7 @@ bool AudioEngine::stop() {
|
|||||||
int32_t AudioEngine::audioCallback(void* outputBuffer, void* inputBuffer, uint32_t nFrames, double, RtAudioStreamStatus status, void* userData) {
|
int32_t AudioEngine::audioCallback(void* outputBuffer, void* inputBuffer, uint32_t nFrames, double, RtAudioStreamStatus status, void* userData) {
|
||||||
|
|
||||||
// error if the callback is late
|
// error if the callback is late
|
||||||
if(status) std::cout << "stream underflow" << std::endl;
|
if(status) static_cast<AudioEngine*>(userData)->logger_->log("AudioEngine", LogFlag::Warning, "Audio buffer underflow");
|
||||||
|
|
||||||
return static_cast<AudioEngine*>(userData)->process(static_cast<float*>(outputBuffer), static_cast<size_t>(nFrames));
|
return static_cast<AudioEngine*>(userData)->process(static_cast<float*>(outputBuffer), static_cast<size_t>(nFrames));
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
#include <RtAudio.h>
|
#include <RtAudio.h>
|
||||||
|
|
||||||
|
#include "LoggerService.hpp"
|
||||||
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#define AUDIO_API RtAudio::WINDOWS_WASAPI
|
#define AUDIO_API RtAudio::WINDOWS_WASAPI
|
||||||
#else
|
#else
|
||||||
@@ -15,7 +18,7 @@ class AudioEngine {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AudioEngine();
|
AudioEngine(LoggerService* logger);
|
||||||
~AudioEngine();
|
~AudioEngine();
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
@@ -36,4 +39,6 @@ private:
|
|||||||
|
|
||||||
float phase_ = 0.0f;
|
float phase_ = 0.0f;
|
||||||
|
|
||||||
|
LoggerService* logger_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user