pax_global_header00006660000000000000000000000064137726406100014520gustar00rootroot0000000000000052 comment=8688c5239f4eab591bddd133a0c4b1ca8a33e1e5 libeXaDrums-0.6.0/000077500000000000000000000000001377264061000137425ustar00rootroot00000000000000libeXaDrums-0.6.0/.gitignore000066400000000000000000000004211377264061000157270ustar00rootroot00000000000000*.m4 m4* *.log config.h.in Makefile.in *.o *.Po .dirstamp *~ autom4te.cache/* *.swp config.h config.status missing stamp-h1 depcomp configure install-sh Makefile *.la *.so* .libs *.lo *.Plo ar-lib compile config.guess config.sub exadrums.pc libtool ltmain.sh Api/Version.h libeXaDrums-0.6.0/Api/000077500000000000000000000000001377264061000144535ustar00rootroot00000000000000libeXaDrums-0.6.0/Api/Config/000077500000000000000000000000001377264061000156605ustar00rootroot00000000000000libeXaDrums-0.6.0/Api/Config/AlsaParams_api.h000066400000000000000000000026351377264061000207140ustar00rootroot00000000000000/* * AlsaParams_api.h * * Created on: 7 Oct 2017 * Author: jeremy */ #ifndef SOURCE_API_CONFIG_ALSAPARAMS_API_H_ #define SOURCE_API_CONFIG_ALSAPARAMS_API_H_ #include "../../Sound/Alsa/AlsaParams.h" #include namespace eXaDrumsApi { struct AlsaParamsApi { AlsaParamsApi() noexcept { *this = Sound::AlsaParams(); } explicit AlsaParamsApi(const Sound::AlsaParams& alsaparams) noexcept { *this = alsaparams; } explicit operator Sound::AlsaParams() const noexcept { Sound::AlsaParams alsaParams; alsaParams.capture = this->capture; alsaParams.sampleRate = this->sampleRate; alsaParams.nChannels = this->nChannels; alsaParams.bufferTime = this->bufferTime; alsaParams.periodTime = this->periodTime; alsaParams.device = this->device; return alsaParams; } AlsaParamsApi& operator=(const Sound::AlsaParams& alsaparams) noexcept { this->capture = alsaparams.capture; this->sampleRate = alsaparams.sampleRate; this->nChannels = alsaparams.nChannels; this->bufferTime = alsaparams.bufferTime; this->periodTime = alsaparams.periodTime; std::snprintf(this->device, sizeof this->device, "%s", alsaparams.device.data()); return *this; } bool capture; unsigned int sampleRate; unsigned int nChannels; unsigned int bufferTime; unsigned int periodTime; char device[64]; }; } #endif /* SOURCE_API_CONFIG_ALSAPARAMS_API_H_ */ libeXaDrums-0.6.0/Api/Config/Config_api.cpp000066400000000000000000000251101377264061000204210ustar00rootroot00000000000000/* * Config_api.cpp * * Created on: 2 Mar 2017 * Author: jeremy */ #include "Config_api.h" #include "TriggerParameters_api.h" #include "../../DrumKit/DrumModule/Module.h" #include "../../DrumKit/Triggers/TriggerManager.h" #include "../../Util/Enums.h" #include "../../Util/ErrorHandling.h" #include "../../Sound/Alsa/Alsa.h" #include "../../Sound/Alsa/AlsaParameters.h" #include "../eXaDrums.h" #include #include using namespace Util; namespace eXaDrumsApi { Config::Config(eXaDrums& drums) noexcept : drumKit(drums), module(*drums.drumModule.get()) { RefreshSensorsConfig(); return; } void Config::RefreshSensorsConfig() noexcept { this->sensorsConfig = module.GetSensorsConfig(); return; } // Private Methods error Config::SaveSensorsConfig_() { std::string dir; module.GetDirectory(dir); return ExceptionToError([&] { DrumKit::TriggerManager::SaveSensorsConfig(dir, sensorsConfig); RestartModule(); }); } error Config::SaveTriggersConfig_() { std::string dir; module.GetDirectory(dir); // Conversion to internal type std::vector trigsParams(triggersParameters.size()); std::transform(triggersParameters.begin(), triggersParameters.end(), trigsParams.begin(), [](auto& tp) { return static_cast(tp); }); return ExceptionToError([&] { DrumKit::TriggerManager::SaveTriggersConfig(dir, trigsParams); RestartModule(); }); } error Config::LoadTriggersConfig_() const { std::string dir; module.GetDirectory(dir); // Load sensors config first IO::SensorsConfig sensorConfig; std::vector trigsParams; return ExceptionToError([&] { DrumKit::TriggerManager::LoadSensorsConfig(dir, sensorConfig); DrumKit::TriggerManager::LoadTriggersConfig(dir, sensorConfig, trigsParams); // Conversion and copy of the triggers parameters this->triggersParameters.clear(); this->triggersParameters.resize(trigsParams.size()); std::transform(trigsParams.begin(), trigsParams.end(), triggersParameters.begin(), [](auto& tp) { return static_cast(tp); }); }); } error Config::SaveCurrentAudioDeviceConfig_() const { Sound::AlsaParams params; params.device = alsaParams.device; params.sampleRate = alsaParams.sampleRate; params.nChannels = alsaParams.nChannels; params.capture = alsaParams.capture; params.bufferTime = alsaParams.bufferTime; params.periodTime = alsaParams.periodTime; return ExceptionToError([&] { Sound::AlsaParameters::SaveAlsaParameters(drumKit.GetDataLocation() + eXaDrums::alsaConfigFile, params); }); } error Config::SaveAudioDeviceConfig_(const AlsaParamsApi& params) { auto err = SetAudioDeviceParameters_(params); if(err.type!= error_type_success) { return err; } return SaveCurrentAudioDeviceConfig_(); } error Config::ResetAudioDevice_() { return ExceptionToError([&] { this->drumKit.alsa.reset(); // Load alsa parameters Sound::AlsaParams alsaParams; Sound::AlsaParameters::LoadAlsaParameters(drumKit.GetDataLocation() + eXaDrums::alsaConfigFile, alsaParams); // Create mixer and alsa this->drumKit.alsa = std::make_unique(alsaParams, this->drumKit.mixer); }); } error Config::AddTrigger_(const TriggerParameters& params) { // Reload triggers config auto err = LoadTriggersConfig_(); if(err.type!= error_type_success) { return err; } // Add trigger this->triggersParameters.push_back(params); // Save trigger config if(update_error(err, SaveTriggersConfig_()) != error_type_success) { return err; } // Restart module RestartModule(); return err; } error Config::DeleteTrigger_(int sensorId) { // Reload triggers config auto err = LoadTriggersConfig_(); if(err.type!= error_type_success) { return err; } // Remove trigger auto it = std::remove_if(triggersParameters.begin(), triggersParameters.end(), [&](const auto& tp) { return tp.sensorId == sensorId; }); if(it != end(triggersParameters)) { triggersParameters.erase(it); } else { return make_error("Could not delete trigger, as it does not exist.", error_type_warning); } // Save triggers config err = SaveTriggersConfig_(); if(err.type!= error_type_success) { return err; } RestartModule(); return make_error("", error_type_success); } void Config::SetSensorsType_(const char* type) { sensorsConfig.sensorType = Enums::ToElement(std::string(type)); return; } void Config::SetSensorsDataFolder_(const char* folder) noexcept { sensorsConfig.hddDataFolder = std::string(folder); return; } void Config::SetTriggersParameters_(const TriggerParameters* params, unsigned int size) noexcept { std::vector trigParams(params, params + size); this->triggersParameters.clear(); this->triggersParameters = trigParams; return; } error Config::SetAudioDeviceParameters_(const AlsaParamsApi& params) { this->alsaParams = params; std::string deviceName(params.device); // Replace device name by device id auto itDev = std::find_if(audioDevices.begin(), audioDevices.end(), [&](const auto& dev) { return deviceName == dev.first; }); if(itDev != audioDevices.end()) { std::strcpy(this->alsaParams.device, itDev->second.data()); } else { return make_error(("Error: audio device " + deviceName + " not found.").data(), error_type_error); } return make_error("", error_type_success); } error Config::GetNbTriggers_(size_t& nb) const { auto error = this->LoadTriggersConfig_(); if(error.type != error_type_success) { return error; } nb = static_cast(triggersParameters.size()); return make_error("", error_type_success); } error Config::ExportConfig_(const char* configDir, const char* outputFileName) noexcept { if(ZipDir(std::string{configDir}, std::string{outputFileName})) { return make_error("", error_type_success); } return make_error("Could not export configuration. Make sure that the source and destination folders exist.", error_type_warning); } error Config::ImportConfig_(const char* configFile, const char* outputConfigDir, bool replace) noexcept { if(UnzipDir(std::string{configFile}, std::string{outputConfigDir}, replace)) { return make_error("", error_type_success); } return make_error("Cannot import configuration. Please check the file's integrity.", error_type_warning); } void Config::RestartModule() { bool isRestart = false; if(drumKit.isStarted.load()) { ErrorToException([&] { return drumKit.Stop_(); }); isRestart = true; } int kitId = module.GetKitId(); module.ReloadTriggers(); module.ReloadKits(); module.SelectKit(kitId); if(isRestart) { ErrorToException([&] { return drumKit.Start_(); }); } return; } void Config::SetTriggerParameters_(int triggerId, const TriggerParameters& params) { const auto triggerParams = static_cast(params); module.SetTriggerParameters(triggerId, triggerParams); } void Config::GetSensorsTypes_(const char** types, unsigned int& size) { if(types == nullptr) { size = Enums::GetEnumVector().size(); return; } const std::vector& vec = Enums::GetEnumVector(); this->sensorsTypes.clear(); this->sensorsTypes.resize(vec.size()); std::transform(vec.cbegin(), vec.cend(), this->sensorsTypes.begin(), [](const IO::SensorType& t) { return Enums::ToString(t); }); unsigned int numElements = std::min(size, sensorsTypes.size()); for(unsigned int i = 0; i < numElements; i++) { types[i] = sensorsTypes[i].c_str(); } return; } void Config::GetTriggersTypes_(const char** types, unsigned int& size) { if(types == nullptr) { size = Enums::GetEnumVector().size(); return; } const std::vector& vec = Enums::GetEnumVector(); this->triggersTypes.clear(); this->triggersTypes.resize(vec.size()); std::transform(vec.cbegin(), vec.cend(), this->triggersTypes.begin(), [](const DrumKit::TriggerType& t) { return Enums::ToString(t); }); unsigned int numElements = std::min(size, triggersTypes.size()); for(unsigned int i = 0; i < numElements; i++) { types[i] = triggersTypes[i].c_str(); } return; } void Config::GetTriggersResponses_(const char** responses, unsigned int& size) { if(responses == nullptr) { size = Enums::GetEnumVector().size(); return; } const std::vector& vec = Enums::GetEnumVector(); this->triggersResponses.clear(); this->triggersResponses.resize(vec.size()); std::transform(vec.cbegin(), vec.cend(), this->triggersResponses.begin(), [](const DrumKit::CurveType& r) { return Enums::ToString(r); }); unsigned int numElements = std::min(size, triggersResponses.size()); for(unsigned int i = 0; i < numElements; i++) { responses[i] = triggersResponses[i].c_str(); } return; } void Config::GetAudioDevicesNames_(const char** dev, unsigned int& size) { const auto devices = Sound::Alsa::GetDevices(); if(dev == nullptr) { size = devices.size(); return; } this->audioDevices.clear(); this->audioDevices.resize(devices.size()); std::copy(devices.begin(), devices.end(), this->audioDevices.begin()); unsigned int numElements = std::min(size, audioDevices.size()); for(unsigned int i = 0; i < numElements; i++) { dev[i] = audioDevices[i].first.data(); } } void Config::GetTriggersParameters_(TriggerParameters* const triggers, unsigned int& size) const { const std::vector& trigsParams = this->module.GetTriggersParameters(); if(triggers == nullptr) { size = trigsParams.size(); return; } if(size != trigsParams.size()) { throw -1; } std::copy(trigsParams.cbegin(), trigsParams.cend(), triggers); return; } const char* Config::GetSensorsType_() { this->sensorType = Enums::ToString(this->sensorsConfig.sensorType); return this->sensorType.c_str(); } const char* Config::GetSensorsDataFolder_() const noexcept { return this->sensorsConfig.hddDataFolder.c_str(); } const char* Config::GetAudioDeviceName_() const noexcept { this->audioDeviceName = this->drumKit.GetAudioDeviceName(); return this->audioDeviceName.data(); } AlsaParamsApi Config::GetAudioDeviceParams_() const noexcept { AlsaParamsApi alsaParameters = static_cast(this->drumKit.alsa->GetParameters()); return alsaParameters; } } libeXaDrums-0.6.0/Api/Config/Config_api.h000066400000000000000000000177471377264061000201070ustar00rootroot00000000000000/* * Config_api.h * * Created on: 2 Mar 2017 * Author: jeremy */ #ifndef SOURCE_API_CONFIG_CONFIG_API_H_ #define SOURCE_API_CONFIG_CONFIG_API_H_ #include "../../IO/SensorsConfig.h" #include "../../Util/ErrorHandling.h" #include "../../Util/Zip.h" #include "AlsaParams_api.h" #include #include namespace DrumKit { class Module; } namespace eXaDrumsApi{ class eXaDrums; struct TriggerParameters; } namespace eXaDrumsApi { class Config { public: /** * @brief Construct a new Config object * * @param drums eXaDrums instance */ explicit Config(eXaDrums& drums) noexcept; /** * @brief Destroy the Config object * */ ~Config() = default; /** * @brief Refresh sensors config internal variable * */ void RefreshSensorsConfig() noexcept; /** * @brief Save current sensors configuration (restarts the module). * */ void SaveSensorsConfig(); /** * @brief Save current triggers configuration and restart module. * */ void SaveTriggersConfig(); /** * @brief Load triggers configuration. * */ void LoadTriggersConfig() const; /** * @brief Save current audio device configuration. * */ void SaveCurrentAudioDeviceConfig() const; /** * @brief Set audio device parameters and save configuration. * * @param params Audio device parameters. */ void SaveAudioDeviceConfig(const AlsaParamsApi& params); /** * @brief Restart audio device using saved configuration. * */ void ResetAudioDevice(); // Config /** * @brief Export configuration. * * @param configDir Current configuration directory. * @param outputFileName Output configuration backup file name. */ static void ExportConfig(const std::string& configDir, const std::string& outputFileName); /** * @brief Import configuration. * * @param configFile Backup configuration file path and name. * @param outputConfigDir Output directory, where the configuration is to be extracted. * @param replace Whether to replace existing files or not. */ static void ImportConfig(const std::string& configFile, const std::string& outputConfigDir, bool replace = false); // Triggers /** * @brief Add a new trigger and save configuration. * * @param params New trigger parameters. */ void AddTrigger(const TriggerParameters& params); /** * @brief Delete a trigger and save configuration. * * @param sensorId Id of the trigger to be deleted. */ void DeleteTrigger(int sensorId); /** * @brief Get the number of triggers * * @return std::size_t */ std::size_t GetNbTriggers() const; // Mutators /** * @brief Set the sensors sampling sate (for SPI sensors). * * @param sRate Sampling frequency in Hz. */ void SetSensorsSamplingRate(int sRate) noexcept { sensorsConfig.samplingRate = sRate; } /** * @brief Set the sensors resolution (For SPI or HDD sensors). * * @param res Resolution in bits (up to 16). */ void SetSensorsResolution(int res) noexcept { sensorsConfig.resolution = res; } /** * @brief Set the sensors type. * * @param type Sensor type. */ void SetSensorsType(const std::string& type); /** * @brief Set the sensor data folder (for HDD sensor). * * @param folder Path to raw sensor data. */ void SetSensorsDataFolder(const std::string& folder) noexcept; /** * @brief Set audio device parameters. * * @param params Audio device parameters. */ void SetAudioDeviceParameters(const AlsaParamsApi& params); /** * @brief Set all triggers parameters. * * @param params Triggers parameters list. */ void SetTriggersParameters(const std::vector& params); /** * @brief Set trigger parameters. * * @param triggerId Trigger id. * @param params Trigger parameters. */ void SetTriggerParameters(int triggerId, const TriggerParameters& params); // Accessors /** * @brief Get the sensors types list. * * @return std::vector List of sensors types. */ std::vector GetSensorsTypes(); /** * @brief Get the triggers types list. * * @return std::vector List of triggers types. */ std::vector GetTriggersTypes(); /** * @brief Get the triggers response curves types. * * @return std::vector List of response curves. */ std::vector GetTriggersResponses(); /** * @brief Get the available audio devices names. * * @return std::vector List of audio devices. */ std::vector GetAudioDevicesNames(); /** * @brief Get triggers parameters list. * * @return std::vector List of triggers parameters. */ std::vector GetTriggersParameters() const; /** * @brief Get sensor type. * * @return std::string Sensor type name. */ std::string GetSensorsType(); /** * @brief Get the (HDD) sensor data folder location. * * @return std::string Data folder path. */ std::string GetSensorsDataFolder() const noexcept; /** * @brief Get the audio device name. * * @return std::string Audio device name. */ std::string GetAudioDeviceName() const noexcept; /** * @brief Get audio device parameters. * * @return AlsaParamsApi Audio device parameters. */ AlsaParamsApi GetAudioDeviceParams() const noexcept; /** * @brief Get the (SPI) sensor sampling rate. * * @return int Sampling frequency in Hz. */ int GetSensorsSamplingRate() const noexcept { return sensorsConfig.samplingRate; } /** * @brief Get the (SPI) sensor resolution. * * @return int Resolution in bits. */ int GetSensorsResolution() const noexcept { return sensorsConfig.resolution; } private: Util::error SaveSensorsConfig_(); Util::error SaveTriggersConfig_(); Util::error LoadTriggersConfig_() const; Util::error SaveCurrentAudioDeviceConfig_() const; Util::error SaveAudioDeviceConfig_(const AlsaParamsApi& params); Util::error ResetAudioDevice_(); Util::error AddTrigger_(const TriggerParameters& params); Util::error DeleteTrigger_(int sensorId); Util::error SetAudioDeviceParameters_(const AlsaParamsApi& params); Util::error GetNbTriggers_(size_t& nb) const; static Util::error ExportConfig_(const char* configDir, const char* outputFileName) noexcept; static Util::error ImportConfig_(const char* configFile, const char* outputConfigDir, bool replace) noexcept; void RestartModule(); void SetSensorsType_(const char* type); void SetSensorsDataFolder_(const char* folder) noexcept; void SetAudioDeviceParameters_(const char* name); void SetTriggersParameters_(const TriggerParameters* params, unsigned int size) noexcept; void SetTriggerParameters_(int triggerId, const TriggerParameters& params); const char* GetSensorsType_(); const char* GetSensorsDataFolder_() const noexcept; const char* GetAudioDeviceName_() const noexcept; AlsaParamsApi GetAudioDeviceParams_() const noexcept; void GetSensorsTypes_(const char** types, unsigned int& size); void GetTriggersTypes_(const char** types, unsigned int& size); void GetTriggersResponses_(const char** responses, unsigned int& size); void GetAudioDevicesNames_(const char** devices, unsigned int& size); void GetTriggersParameters_(TriggerParameters* const triggers, unsigned int& size) const; eXaDrums& drumKit; DrumKit::Module& module; // Alsa config mutable AlsaParamsApi alsaParams; // Sensors config IO::SensorsConfig sensorsConfig; // Triggers config mutable std::vector triggersParameters; // Local copies of items std::string sensorType; mutable std::string audioDeviceName; // Local copies of enums std::vector sensorsTypes; std::vector triggersTypes; std::vector triggersResponses; std::vector> audioDevices; }; } #include "Config_api.hpp" #endif /* SOURCE_API_CONFIG_CONFIG_API_H_ */ libeXaDrums-0.6.0/Api/Config/Config_api.hpp000066400000000000000000000110771377264061000204350ustar00rootroot00000000000000/* * Config_api.hpp * * Created on: 2 Mar 2017 * Author: jeremy */ #ifndef SOURCE_API_CONFIG_CONFIG_API_HPP_ #define SOURCE_API_CONFIG_CONFIG_API_HPP_ #include "Config_api.h" #include "TriggerParameters_api.h" namespace eXaDrumsApi { inline void Config::SaveSensorsConfig() { Util::ErrorToException([&] { return this->SaveSensorsConfig_(); }); } inline void Config::SaveTriggersConfig() { Util::ErrorToException([&] { return this->SaveTriggersConfig_(); }); } inline void Config::LoadTriggersConfig() const { Util::ErrorToException([&] { return this->LoadTriggersConfig_(); }); } inline void Config::SaveCurrentAudioDeviceConfig() const { Util::ErrorToException([&] { return this->SaveCurrentAudioDeviceConfig_(); }); } inline void Config::SaveAudioDeviceConfig(const AlsaParamsApi& params) { Util::ErrorToException([&] { return this->SaveAudioDeviceConfig_(params); }); } inline void Config::ResetAudioDevice() { Util::ErrorToException([&] { return this->ResetAudioDevice_(); }); } inline void Config::ExportConfig(const std::string& configDir, const std::string& outputFileName) { Util::ErrorToException([&] { return Config::ExportConfig_(configDir.c_str(), outputFileName.c_str()); }); } inline void Config::ImportConfig(const std::string& configFile, const std::string& outputConfigDir, bool replace) { Util::ErrorToException([&] { return Config::ImportConfig_(configFile.c_str(), outputConfigDir.c_str(), replace); }); } inline void Config::AddTrigger(const TriggerParameters& params) { Util::ErrorToException([&] { return this->AddTrigger_(params); }); } inline void Config::DeleteTrigger(int sensorId) { Util::ErrorToException([&] { return this->DeleteTrigger_(sensorId); }); } inline std::size_t Config::GetNbTriggers() const { size_t nb = 0; this->GetNbTriggers_(nb); return nb; } inline void Config::SetSensorsType(const std::string& type) { SetSensorsType_(type.c_str()); return; } inline void Config::SetSensorsDataFolder(const std::string& folder) noexcept { SetSensorsDataFolder_(folder.c_str()); return; } inline void Config::SetAudioDeviceParameters(const AlsaParamsApi& params) { Util::ErrorToException([&] { return SetAudioDeviceParameters_(params); }); return; } inline void Config::SetTriggersParameters(const std::vector& params) { SetTriggersParameters_(params.data(), params.size()); return; } inline void Config::SetTriggerParameters(int triggerId, const TriggerParameters& params) { SetTriggerParameters_(triggerId, params); } inline std::vector Config::GetSensorsTypes() { unsigned int size = 0; GetSensorsTypes_(nullptr, size); std::vector data(size); GetSensorsTypes_(data.data(), size); std::vector vec(size); std::copy(data.cbegin(), data.cend(), vec.begin()); return vec; } inline std::vector Config::GetTriggersTypes() { unsigned int size = 0; GetTriggersTypes_(nullptr, size); std::vector data(size); GetTriggersTypes_(data.data(), size); std::vector vec(size); std::copy(data.cbegin(), data.cend(), vec.begin()); return vec; } inline std::vector Config::GetTriggersResponses() { unsigned int size = 0; GetTriggersResponses_(nullptr, size); std::vector data(size); GetTriggersResponses_(data.data(), size); std::vector vec(size); std::copy(data.cbegin(), data.cend(), vec.begin()); return vec; } inline std::vector Config::GetAudioDevicesNames() { unsigned int size = 0; GetAudioDevicesNames_(nullptr, size); std::vector data(size); GetAudioDevicesNames_(data.data(), size); std::vector vec(size); std::copy(data.cbegin(), data.cend(), vec.begin()); return vec; } inline std::vector Config::GetTriggersParameters() const { unsigned int size = 0; GetTriggersParameters_(nullptr, size); std::vector vec(size); GetTriggersParameters_(vec.data(), size); return vec; } inline AlsaParamsApi Config::GetAudioDeviceParams() const noexcept { return GetAudioDeviceParams_(); } inline std::string Config::GetSensorsType() { return std::string(GetSensorsType_()); } inline std::string Config::GetSensorsDataFolder() const noexcept { return std::string(GetSensorsDataFolder_()); } inline std::string Config::GetAudioDeviceName() const noexcept { return std::string(GetAudioDeviceName_()); } } // namespace eXaDrumsApi #endif /* SOURCE_API_CONFIG_CONFIG_API_HPP_ */ libeXaDrums-0.6.0/Api/Config/TriggerParameters_api.cpp000066400000000000000000000031601377264061000226440ustar00rootroot00000000000000/* * TriggerParameters.cpp * * Created on: 14 Mar 2017 * Author: jeremy */ #include "TriggerParameters_api.h" #include "../../DrumKit/Triggers/TriggerParameters.h" #include "../../Util/Enums.h" #include #include using namespace Util; namespace eXaDrumsApi { TriggerParameters::TriggerParameters() noexcept { *this = DrumKit::TriggerParameters(); return; } TriggerParameters::TriggerParameters(const DrumKit::TriggerParameters& parameters) noexcept { *this = parameters; return; } TriggerParameters& TriggerParameters::operator=(const DrumKit::TriggerParameters& parameters) noexcept { std::string typeStr = Enums::ToString(parameters.type); std::string responseStr = Enums::ToString(parameters.response); std::snprintf(this->type, sizeof this->type, "%s", typeStr.data()); std::snprintf(this->response, sizeof this->response, "%s", responseStr.data()); this->maskTime = parameters.maskTime; this->scanTime = parameters.scanTime; this->sensorId = parameters.sensorId; this->threshold = parameters.threshold; this->gain = parameters.gain; return *this; } TriggerParameters::operator DrumKit::TriggerParameters() const { DrumKit::TriggerParameters parameters; parameters.type = Enums::ToElement(std::string(this->type)); parameters.response = Enums::ToElement(std::string(this->response)); parameters.maskTime = this->maskTime; parameters.scanTime = this->scanTime; parameters.sensorId = this->sensorId; parameters.threshold = this->threshold; parameters.gain = this->gain; return parameters; } } libeXaDrums-0.6.0/Api/Config/TriggerParameters_api.h000066400000000000000000000014261377264061000223140ustar00rootroot00000000000000/* * TriggerParameters.h * * Created on: 14 Mar 2017 * Author: jeremy */ #ifndef SOURCE_API_CONFIG_TRIGGERPARAMETERS_API_H_ #define SOURCE_API_CONFIG_TRIGGERPARAMETERS_API_H_ namespace DrumKit { struct TriggerParameters; } namespace eXaDrumsApi { struct TriggerParameters { TriggerParameters() noexcept; // Conversion to internal type explicit TriggerParameters(const DrumKit::TriggerParameters& parameters) noexcept; TriggerParameters& operator=(const DrumKit::TriggerParameters& parameters) noexcept; explicit operator DrumKit::TriggerParameters() const; int sensorId; unsigned int scanTime; short threshold; int maskTime; double gain = 1.; char type[64]; char response[64]; }; } #endif /* SOURCE_API_CONFIG_TRIGGERPARAMETERS_API_H_ */ libeXaDrums-0.6.0/Api/KitCreator/000077500000000000000000000000001377264061000165225ustar00rootroot00000000000000libeXaDrums-0.6.0/Api/KitCreator/KitCreator_api.cpp000066400000000000000000000206031377264061000221270ustar00rootroot00000000000000/* * KitCreator.cpp * * Created on: 22 Nov 2016 * Author: jeremy */ #include "KitCreator_api.h" #include #include using namespace Util; namespace eXaDrumsApi { KitCreator::KitCreator(const char* dataLocation) : controller(dataLocation) { return; } KitCreator::~KitCreator() { return; } // Kit void KitCreator::CreateNewKit() noexcept { controller.CreateNewKit(); return; } int KitCreator::GetNumInstruments() const noexcept { return controller.GetNumInstruments(); } void KitCreator::SetKitName(const char* name) noexcept { controller.SetKitName(std::string(name)); return; } // Instrument void KitCreator::CreateNewInstrument() noexcept { controller.CreateNewInstrument(); return; } void KitCreator::RemoveInstrument(std::size_t i) noexcept { controller.RemoveInstrument(i); return; } void KitCreator::RemoveLastInstrument() noexcept { controller.RemoveLastInstrument(); return; } void KitCreator::AddInstrumentToKit() noexcept { controller.AddInstrumentToKit(); return; } void KitCreator::SetInstrumentType(int id, const char* type) { controller.SetInstrumentType(id, std::string(type)); return; } void KitCreator::SetInstrumentType(const char* type) { controller.SetInstrumentType(std::string(type)); return; } void KitCreator::SetInstrumentVolume(const float volume) noexcept { controller.SetInstrumentVolume(volume); return; } void KitCreator::AddInstrumentSound(const char* file, const char* type) { controller.AddInstrumentSound(std::string(file), std::string(type)); return; } void KitCreator::AddInstrumentTrigger(const int id, const char* location) { controller.AddInstrumentTrigger(id, std::string(location)); return; } // Private Methods error KitCreator::CreateFromModel_(const char* loc) { return ExceptionToError([&] { controller.CreateFromModel(std::string(loc)); }); } error KitCreator::SaveKit_(const char* file) const { return ExceptionToError([&] { controller.SaveKit(std::string(file)); }); } error KitCreator::SaveKit_() const { return ExceptionToError([&] { controller.SaveKit(); }); } error KitCreator::SetInstrumentName_(const char* name) { std::string nameStr = std::string(name); if(nameStr.empty()) { return make_error("Instrument name cannot be empty.", error_type_error); } controller.SetInstrumentName(nameStr); return make_error("", error_type_success); } error KitCreator::SetInstrumentName_(int id, const char* name) { std::string nameStr = std::string(name); if(nameStr.empty()) { return make_error("Instrument name cannot be empty.", error_type_error); } controller.SetInstrumentName(id, nameStr); return make_error("", error_type_success); } void KitCreator::SetInstrumentTriggersIdsAndLocs_(int id, int* ids, const char** locs, unsigned int size) { std::vector trigsIds(ids, ids + size); std::vector trigsLocs(locs, locs + size); std::vector> trigs; for(std::size_t i = 0; i < size; i++) { trigs.push_back({trigsIds[i], trigsLocs[i]}); } controller.SetInstrumentTriggersIdsAndLocs(id, trigs); return; } void KitCreator::SetInstrumentSoundsTypesAndLocs_(int id, const char** types, const char** locs, unsigned int size) { std::vector sndTypes(types, types + size); std::vector sndLocs(locs, locs + size); std::vector> sounds; for(std::size_t i = 0; i < size; i++) { sounds.push_back({sndTypes[i], sndLocs[i]}); } controller.SetInstrumentSoundsTypesAndLocs(id, sounds); return; } const char* KitCreator::GetInstrumentType_(int i) { this->instrumentType = controller.GetInstrumentType(i); return this->instrumentType.c_str(); } void KitCreator::GetInstrumentTriggersIds_(int i, int* data, unsigned int& size) const { if(data == nullptr) { size = controller.GetInstrumentsTriggersIds(i).size(); return; } std::vector trigsIds = controller.GetInstrumentsTriggersIds(i); std::copy(trigsIds.cbegin(), trigsIds.cend(), data); size = trigsIds.size(); return; } void KitCreator::GetTriggersIds_(int* data, unsigned int& size) const { if(data == nullptr) { size = controller.GetTriggersIds().size(); return; } std::vector trigsIds = controller.GetTriggersIds(); std::copy(trigsIds.cbegin(), trigsIds.cend(), data); size = trigsIds.size(); return; } void KitCreator::GetInstrumentTriggersLocations_(int i, const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetInstrumentTriggersLocations(i).size(); return; } this->instrumentTriggersLocations.clear(); this->instrumentTriggersLocations = controller.GetInstrumentTriggersLocations(i); unsigned int numElements = std::min(size, instrumentTriggersLocations.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = instrumentTriggersLocations[i].c_str(); } return; } void KitCreator::GetInstrumentSoundsTypes_(int i, const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetInstrumentSoundsTypes(i).size(); return; } this->instrumentSoundsTypes.clear(); this->instrumentSoundsTypes = controller.GetInstrumentSoundsTypes(i); unsigned int numElements = std::min(size, instrumentSoundsTypes.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = instrumentSoundsTypes[i].c_str(); } return; } void KitCreator::GetSoundTypes_(const char* instrumentType, const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetSoundTypes(std::string(instrumentType)).size(); return; } this->soundsTypes.clear(); this->soundsTypes = controller.GetSoundTypes(std::string(instrumentType)); unsigned int numElements = std::min(size, soundsTypes.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = soundsTypes[i].c_str(); } return; } void KitCreator::GetInstrumentSoundsLocs_(int i, const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetInstrumentSoundsLocs(i).size(); return; } this->instrumentSoundsLocs.clear(); this->instrumentSoundsLocs = controller.GetInstrumentSoundsLocs(i); unsigned int numElements = std::min(size, instrumentSoundsLocs.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = instrumentSoundsLocs[i].c_str(); } return; } /*void KitCreator::GetSoundFiles_(const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetSoundFiles().size(); return; } this->soundsFiles.clear(); this->soundsFiles = controller.GetSoundFiles(); unsigned int numElements = std::min(size, soundsFiles.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = soundsFiles[i].c_str(); } return; }*/ void KitCreator::GetInstrumentsTypes_(const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetInstrumentsTypes().size(); return; } this->instrumentsTypes.clear(); this->instrumentsTypes = controller.GetInstrumentsTypes(); unsigned int numElements = std::min(size, instrumentsTypes.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = instrumentsTypes[i].c_str(); } return; } void KitCreator::GetInstrumentsNames_(const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetInstrumentsNames().size(); return; } this->instrumentsNames.clear(); this->instrumentsNames = controller.GetInstrumentsNames(); unsigned int numElements = std::min(size, instrumentsNames.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = instrumentsNames[i].c_str(); } return; } void KitCreator::GetTriggersLocations_(const char* instrumentType, const char** data, unsigned int& size) { if(data == nullptr) { size = controller.GetTriggersLocations(std::string(instrumentType)).size(); return; } this->triggersLocations.clear(); this->triggersLocations = controller.GetTriggersLocations(std::string(instrumentType)); unsigned int numElements = std::min(size, triggersLocations.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = triggersLocations[i].c_str(); } return; } } /* namespace eXaDrumsApi */ libeXaDrums-0.6.0/Api/KitCreator/KitCreator_api.h000066400000000000000000000167771377264061000216150ustar00rootroot00000000000000/* * KitCreator.h * * Created on: 22 Nov 2016 * Author: jeremy */ #ifndef SOURCE_API_KITCREATOR_KITCREATOR_API_H_ #define SOURCE_API_KITCREATOR_KITCREATOR_API_H_ #include "../../Util/ErrorHandling.h" #include "../../DrumKit/Kits/KitCreator.h" #include #include #include namespace eXaDrumsApi { class KitCreator { public: /** * @brief Construct a new Kit Creator. * * @param dataLocation eXaDrums configuration folder location. */ explicit KitCreator(const char* dataLocation); /** * @brief Destroy the Kit Creator. * */ ~KitCreator(); // Kit /** * @brief Create a New kit. * */ void CreateNewKit() noexcept; /** * @brief Create a new kit by copying an existing one. * * @param loc Existing kit file name. */ void CreateFromModel(const char* loc); /** * @brief Get the number of instruments in the current kit. * * @return int Number of instruments. */ int GetNumInstruments() const noexcept; /** * @brief Set the kit name. * * @param name Kit name. */ void SetKitName(const char* name) noexcept; /** * @brief Save current kit to a file. * * @param file Destination file. */ void SaveKit(const char* file) const; /** * @brief Save current kit configuration. * */ void SaveKit() const; // Instrument /** * @brief Create a new instrument. * */ void CreateNewInstrument() noexcept; /** * @brief Remove a given instrument. * * @param i Instrument index (id). */ void RemoveInstrument(std::size_t i) noexcept; /** * @brief Delete the last instrument. * */ void RemoveLastInstrument() noexcept; /** * @brief Add current instrument to kit. * */ void AddInstrumentToKit() noexcept; /** * @brief Set the current instrument name. * * @param name Instrument name. */ void SetInstrumentName(const char* name); /** * @brief Set the current instrument type. * * @param type Instrument type name. */ void SetInstrumentType(const char* type); /** * @brief Set the current instrument volume. * * @param volume Instrument volume (from 0 to 1). */ void SetInstrumentVolume(const float volume) noexcept; /** * @brief Add sound to current instrument. * * @param file Sound file location. * @param type Sound type. */ void AddInstrumentSound(const char* file, const char* type); /** * @brief Add trigger to current instrument. * * @param id Trigger id. * @param location Trigger location. */ void AddInstrumentTrigger(const int id, const char* location); /** * @brief Set instrument name. * * @param id Instrument id. * @param name Instrument name to be set. */ void SetInstrumentName(int id, const char* name); /** * @brief Set instrument type. * * @param id Instrument id. * @param type Instrument type name. */ void SetInstrumentType(int id, const char* type); /** * @brief Set instrument triggers ids and locations. * * @param id Instrument id. * @param trigsIdsAndLocs Triggers ids and locations. */ void SetInstrumentTriggersIdsAndLocs(int id, const std::vector>& trigsIdsAndLocs); /** * @brief Set instrument sounds types and locations. * * @param id Instrument id. * @param sndTypesAndLocs Sounds types and locations. */ void SetInstrumentSoundsTypesAndLocs(int id, const std::vector>& sndTypesAndLocs); /** * @brief Get an instrument's type. * * @param i Instrument id. * @return std::string Instrument type. */ std::string GetInstrumentType(int i); /** * @brief Get instrument triggers ids. * * @param i Instrument id. * @return std::vector Triggers ids. */ std::vector GetInstrumentTriggersIds(int i) const; /** * @brief Get instrument triggers locations. * * @param i Instrument id. * @return std::vector Triggers locations. */ std::vector GetInstrumentTriggersLocations(int i); /** * @brief Get instrument sounds types. * * @param i Instrument id. * @return std::vector Sounds types. */ std::vector GetInstrumentSoundsTypes(int i); /** * @brief Get instrument sounds locations. * * @param i Instrument id. * @return std::vector Sounds locations. */ std::vector GetInstrumentSoundsLocs(int i); // Enums // Instruments /** * @brief Get the list of instruments types. * * @return std::vector Instruments types. */ std::vector GetInstrumentsTypes(); /** * @brief Get the list of instruments names. * * @return std::vector Instruments names. */ std::vector GetInstrumentsNames(); // Triggers /** * @brief Get the list of triggers ids. * * @return std::vector List of triggers ids. */ std::vector GetTriggersIds() const; /** * @brief Get the possible triggers locations for a given instrument type. * * @param instrumentType Instrument type. * @return std::vector List of possible triggers locations. */ std::vector GetTriggersLocations(const std::string& instrumentType); // Sounds //std::vector GetSoundsFiles(); /** * @brief Get the possible sounds types for a given instrument type. * * @param instrumentType Instrument type. * @return std::vector List of possible sounds types. */ std::vector GetSoundsTypes(const std::string& instrumentType); private: Util::error CreateFromModel_(const char* loc); Util::error SaveKit_(const char* file) const; Util::error SaveKit_() const; Util::error SetInstrumentName_(const char* name); Util::error SetInstrumentName_(int id, const char* name); void SetInstrumentTriggersIdsAndLocs_(int id, int* ids, const char** locs, unsigned int size); void SetInstrumentSoundsTypesAndLocs_(int id, const char** types, const char** locs, unsigned int size); const char* GetInstrumentType_(int i); void GetInstrumentTriggersIds_(int i, int* data, unsigned int& size) const; void GetTriggersIds_(int* data, unsigned int& size) const; void GetInstrumentTriggersLocations_(int i, const char** data, unsigned int& size); void GetTriggersLocations_(const char* instrumentType, const char** data, unsigned int& size); void GetInstrumentSoundsTypes_(int i, const char** data, unsigned int& size); void GetSoundTypes_(const char* instrumentType, const char** data, unsigned int& size); void GetInstrumentSoundsLocs_(int i, const char** data, unsigned int& size); //void GetSoundFiles_(const char** data, unsigned int& size); void GetInstrumentsTypes_(const char** data, unsigned int& size); void GetInstrumentsNames_(const char** data, unsigned int& size); // Local copies of all the strings std::string instrumentType; // Local copies of all the enums std::vector soundsFiles; std::vector soundsTypes; std::vector instrumentsTypes; std::vector triggersLocations; std::vector instrumentsNames; std::vector instrumentTriggersLocations; std::vector instrumentSoundsTypes; std::vector instrumentSoundsLocs; // Controller DrumKit::KitCreator controller; }; } /* namespace eXaDrumsApi */ #include "KitCreator_api.hpp" #endif /* SOURCE_API_KITCREATOR_KITCREATOR_API_H_ */ libeXaDrums-0.6.0/Api/KitCreator/KitCreator_api.hpp000066400000000000000000000143721377264061000221420ustar00rootroot00000000000000/* * KitCreator_api.hpp * * Created on: 14 Dec 2016 * Author: jeremy */ #ifndef SOURCE_API_KITCREATOR_KITCREATOR_API_HPP_ #define SOURCE_API_KITCREATOR_KITCREATOR_API_HPP_ #include "KitCreator_api.h" #include #include namespace eXaDrumsApi { inline void KitCreator::CreateFromModel(const char* loc) { Util::ErrorToException([&] { return this->CreateFromModel_(loc); }); } inline void KitCreator::SaveKit(const char* file) const { Util::ErrorToException([&] { return this->SaveKit_(file); }); } inline void KitCreator::SaveKit() const { Util::ErrorToException([&] { return this->SaveKit_(); }); } inline void KitCreator::SetInstrumentName(const char* name) { Util::ErrorToException([&] { return this->SetInstrumentName_(name); }); } inline void KitCreator::SetInstrumentName(int id, const char* name) { Util::ErrorToException([&] { return this->SetInstrumentName_(id, name); }); } inline void KitCreator::SetInstrumentTriggersIdsAndLocs(int id, const std::vector>& trigsIdsAndLocs) { // Retrieve triggers ids std::vector ids; std::transform(trigsIdsAndLocs.cbegin(), trigsIdsAndLocs.cend(), std::back_inserter(ids), [](const std::pair& p) { return p.first; }); // Retrieve triggers locations std::vector trigsLocs; std::transform(trigsIdsAndLocs.cbegin(), trigsIdsAndLocs.cend(), std::back_inserter(trigsLocs), [](const std::pair& p) { return p.second; }); // Create trigger locations pointers std::vector locs(trigsLocs.size()); std::transform(trigsLocs.cbegin(), trigsLocs.cend(), locs.begin(), [](const std::string& s) { return s.c_str();}); SetInstrumentTriggersIdsAndLocs_(id, ids.data(), locs.data(), trigsIdsAndLocs.size()); return; } inline void KitCreator::SetInstrumentSoundsTypesAndLocs(int id, const std::vector>& sndTypesAndLocs) { // Retrieve sounds types std::vector sndTypes; std::transform(sndTypesAndLocs.cbegin(), sndTypesAndLocs.cend(), std::back_inserter(sndTypes), [](const std::pair& p){ return p.first; }); // Retrieve sounds locations std::vector sndLocs; std::transform(sndTypesAndLocs.cbegin(), sndTypesAndLocs.cend(), std::back_inserter(sndLocs), [](const std::pair& p){ return p.second; }); // Create sound types pointers std::vector types(sndTypes.size()); std::transform(sndTypes.cbegin(), sndTypes.cend(), types.begin(), [](const std::string& s) { return s.c_str(); }); // Create sound locations pointers std::vector locs(sndLocs.size()); std::transform(sndLocs.cbegin(), sndLocs.cend(), locs.begin(), [](const std::string& s) { return s.c_str(); }); SetInstrumentSoundsTypesAndLocs_(id, types.data(), locs.data(), sndTypesAndLocs.size()); return; } inline std::string KitCreator::GetInstrumentType(int i) { return std::string(GetInstrumentType_(i)); } inline std::vector KitCreator::GetInstrumentTriggersIds(int i) const { unsigned int size; GetInstrumentTriggersIds_(i, nullptr, size); std::vector trigsIds(size); GetInstrumentTriggersIds_(i, trigsIds.data(), size); return trigsIds; } inline std::vector KitCreator::GetTriggersIds() const { unsigned int size; GetTriggersIds_(nullptr, size); std::vector trigsIds(size); GetTriggersIds_(trigsIds.data(), size); return trigsIds; } inline std::vector KitCreator::GetInstrumentTriggersLocations(int i) { unsigned int size; GetInstrumentTriggersLocations_(i, nullptr, size); std::vector data(size); GetInstrumentTriggersLocations_(i, data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } inline std::vector KitCreator::GetInstrumentSoundsTypes(int i) { unsigned int size; GetInstrumentSoundsTypes_(i, nullptr, size); std::vector data(size); GetInstrumentSoundsTypes_(i, data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } inline std::vector KitCreator::GetInstrumentSoundsLocs(int i) { unsigned int size; GetInstrumentSoundsLocs_(i, nullptr, size); std::vector data(size); GetInstrumentSoundsLocs_(i, data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } /*inline std::vector KitCreator::GetSoundsFiles() { unsigned int size; GetSoundFiles_(nullptr, size); std::vector data(size); GetSoundFiles_(data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; }*/ inline std::vector KitCreator::GetSoundsTypes(const std::string& instrumentType) { unsigned int size; GetSoundTypes_(instrumentType.c_str(), nullptr, size); std::vector data(size); GetSoundTypes_(instrumentType.c_str(), data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } inline std::vector KitCreator::GetInstrumentsTypes() { unsigned int size; GetInstrumentsTypes_(nullptr, size); std::vector data(size); GetInstrumentsTypes_(data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } inline std::vector KitCreator::GetInstrumentsNames() { unsigned int size; GetInstrumentsNames_(nullptr, size); std::vector data(size); GetInstrumentsNames_(data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } inline std::vector KitCreator::GetTriggersLocations(const std::string& instrumentType) { unsigned int size; GetTriggersLocations_(instrumentType.c_str(), nullptr, size); std::vector data(size); GetTriggersLocations_(instrumentType.c_str(), data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } } #endif /* SOURCE_API_KITCREATOR_KITCREATOR_API_HPP_ */ libeXaDrums-0.6.0/Api/Version.h.in000066400000000000000000000002761377264061000166630ustar00rootroot00000000000000#ifndef LIBEXADRUMS_SOURCE_API_VERSION_H_ #define LIBEXADRUMS_SOURCE_API_VERSION_H_ namespace eXaDrumsApi { inline constexpr auto LIBEXADRUMS_VERSION = "@PACKAGE_VERSION@"; } #endif libeXaDrums-0.6.0/Api/eXaDrums.cpp000066400000000000000000000250411377264061000167110ustar00rootroot00000000000000/* * eXaDrumsApi.cpp * * Created on: 8 Sep 2015 * Author: jeremy */ #include "eXaDrums.h" #include "../Util/ErrorHandling.h" #include "../DrumKit/DrumModule/Module.h" #include "../Metronome/Metronome.h" #include "../Sound/Alsa/Alsa.h" #include "../Sound/Alsa/AlsaParameters.h" #include "../Sound/Mixer/Mixer.h" #include "../Util/Enums.h" #include #include using namespace Sound; using namespace DrumKit; using namespace Util; /** * eXaDrums API namespace */ namespace eXaDrumsApi { const std::string eXaDrums::metronomeConfigFile = "metronomeConfig.xml"; const std::string eXaDrums::alsaConfigFile = "alsaConfig.xml"; eXaDrums::eXaDrums(const char* dataLoc) noexcept : isStarted(false) { this->init_error = ExceptionToError([&] { this->dataLocation = std::string{dataLoc} + "/"; // Load alsa parameters AlsaParams alsaParams; AlsaParameters::LoadAlsaParameters(dataLocation + alsaConfigFile, alsaParams); // Create mixer and alsa this->mixer = std::make_shared(); this->alsa = std::make_unique(alsaParams, this->mixer); // Load metronome parameters MetronomeParameters metronomeParams; Metronome::LoadConfig(dataLocation + metronomeConfigFile, metronomeParams); this->metronome = std::make_shared(alsaParams, metronomeParams); // Create drum module this->drumModule = std::make_unique(dataLocation, alsaParams, this->mixer, this->metronome); }); return; } eXaDrums::~eXaDrums() { } // Module error eXaDrums::Start_() { try { this->alsa->Start(); this->drumModule->Start(); } catch(const Exception&) { return ExceptionToError([&] { throw; }); } catch(...) { return make_error("Could not start module.", error_type_error); } isStarted.store(true); return make_error("", error_type_success); } error eXaDrums::Stop_() { try { this->alsa->Stop(); this->drumModule->Stop(); } catch(const std::exception& e) { return make_error("Could not stop module.", error_type_error); } isStarted.store(false); return make_error("", error_type_success); } error eXaDrums::EnableRecording_(bool enable) { try { this->drumModule->EnableRecording(enable); } catch(const std::exception& e) { return make_error("Could not enable/disable recording.", error_type_warning); } return make_error("", error_type_success); } error eXaDrums::RecorderExport_(const char* fileName) { try { this->drumModule->RecorderExport(std::string{fileName}); } catch(const std::exception& e) { return make_error("Could not export track.", error_type_warning); } return make_error("", error_type_success); } error eXaDrums::RecorderExportPCM_(const char* fileName) { try { this->drumModule->RecorderExportPCM(std::string{fileName}); } catch(const std::exception& e) { return make_error("Could not export track.", error_type_warning); } return make_error("", error_type_success); } error eXaDrums::RecorderPurgeTempFile_() { try { this->drumModule->RecorderPurgeTempFile(); } catch(const std::exception& e) { return make_error("Unknown error.", error_type_warning); } return make_error("", error_type_success); } void eXaDrums::GetInstrumentTriggersIds_(int instrumentId, int* data, unsigned int& size) const { if(data == nullptr) { size = drumModule->GetInstrumentTriggersIds(instrumentId).size(); return; } std::vector trigsIds = drumModule->GetInstrumentTriggersIds(instrumentId); std::copy(trigsIds.cbegin(), trigsIds.cend(), data); size = trigsIds.size(); } error eXaDrums::GetTriggerValue_(size_t id, float& value) { try { value = drumModule->GetTriggerValue(id); } catch(const Exception& e) { return make_error(e.what(), e.type()); } return make_error("", error_type_success); } // Metronome void eXaDrums::EnableMetronome(bool enable) const { drumModule->EnableMetronome(enable); return; } void eXaDrums::RestartMetronome() const { drumModule->RestartMetronome(); return; } void eXaDrums::ChangeTempo(std::size_t tempo) const { drumModule->ChangeTempo(tempo); return; } void eXaDrums::ChangeClickVolume(std::size_t volume) const { drumModule->ChangeClickVolume(volume); return; } std::size_t eXaDrums::GetTempo() const noexcept { return metronome->GetTempo(); } std::size_t eXaDrums::GetClickVolume() const noexcept { return std::size_t(drumModule->GetClickVolume() * 100.0f); } void eXaDrums::SaveMetronomeConfig() const { metronome->SaveConfig(dataLocation + metronomeConfigFile, metronome->GetParameters()); return; } void eXaDrums::SetClickType(std::size_t id) { const auto types = Enums::GetEnumVector(); if(id >= types.size()) { ClickType type = types.back(); metronome->SetClickType(type); } else { ClickType type = types[id]; metronome->SetClickType(type); } return; } std::size_t eXaDrums::GetClickTypeId() const { ClickType clickType = metronome->GetClickType(); const std::vector& clickTypes = Enums::GetEnumVector(); auto it = std::find(clickTypes.cbegin(), clickTypes.cend(), clickType); std::size_t index = std::distance(clickTypes.cbegin(), it); return index; } std::size_t eXaDrums::GetRhythm() const noexcept { return this->metronome->GetRhythm(); } void eXaDrums::SetRhythm(std::size_t rhythm) noexcept { this->metronome->SetRhythm(rhythm); } std::size_t eXaDrums::GetBpmeas() const noexcept { return this->metronome->GetBpmeas(); } void eXaDrums::SetBpmeas(std::size_t bpmeas) noexcept { this->metronome->SetBpmeas(bpmeas); return; } error eXaDrums::SelectKit_(int id) { return ExceptionToError([&] { this->drumModule->SelectKit(id); }); } error eXaDrums::SaveKitConfig_(std::size_t id) const { return ExceptionToError([&] { drumModule->SaveKitConfig(id); }); } error eXaDrums::DeleteKit_(int id) { if(drumModule->DeleteKit(id)) { return make_error("", error_type_success); } else { return make_error("Could not delete kit.", error_type_error); } } void eXaDrums::ReloadKits() { drumModule->ReloadKits(); return; } std::size_t eXaDrums::GetNumKits() const noexcept { return drumModule->GetNumKits(); } error eXaDrums::SetInstrumentVolume_(std::size_t id, std::size_t volume) { float vol = float(volume) / 100.0f; return ExceptionToError([&] { drumModule->SetInstrumentVolume(id, vol); }); } std::size_t eXaDrums::GetInstrumentVolume(std::size_t id) const { auto volume = (std::size_t) std::floor( 100.0f * drumModule->GetInstrumentVolume(id)); return volume; } long long eXaDrums::GetLastTrigTime() const noexcept { return drumModule->GetLastTrigTime(); } std::size_t eXaDrums::GetLastTrigValue() const noexcept { return drumModule->GetLastTrigValue(); } /** * @brief Set external trigger's data (error handling's responsability is left to the user) * * @param id trigger id * @param channel * @param data trigger's output value */ void eXaDrums::SetTriggerSensorValue(std::size_t id, char channel, short data) { this->drumModule->SetTriggerSensorValue(id, channel, data); } std::size_t eXaDrums::GetSensorsResolution() const noexcept { return this->drumModule->GetSensorsConfig().resolution; } bool eXaDrums::IsSensorVirtual() const noexcept { return this->drumModule->GetSensorsConfig().sensorType == IO::SensorType::Virtual; } bool eXaDrums::IsSensorSpi() const noexcept { return this->drumModule->GetSensorsConfig().sensorType == IO::SensorType::Spi; } std::string eXaDrums::GetAudioDeviceName() const noexcept { return this->alsa->GetDeviceName(); } // Private Methods const char* eXaDrums::GetDataLocation_() const noexcept { return this->dataLocation.c_str(); } const char* eXaDrums::GetVersion_() noexcept { return LIBEXADRUMS_VERSION; } const char* eXaDrums::GetKitDataFileName_() { try { std::string kitLocation = this->drumModule->GetKitLocation(); std::size_t pos = kitLocation.find_last_of("/"); this->kitDataFileName = kitLocation.substr(pos + 1, std::string::npos); } catch(...) { return nullptr; } return this->kitDataFileName.c_str(); } void eXaDrums::GetClicksTypes_(const char** data, unsigned int& size) { if(data == nullptr) { size = Enums::GetEnumVector().size(); return; } this->clicksTypes.clear(); { std::vector types = Enums::GetEnumVector(); std::transform(types.cbegin(), types.cend(), std::back_inserter(clicksTypes), [](const ClickType& t) { return Enums::ToString(t); }); } unsigned int numElements = std::min(size, clicksTypes.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = clicksTypes[i].c_str(); } return; } void eXaDrums::GetRhythms_(int* data, unsigned int& size) const { if(data == nullptr) { size = this->metronome->GetRhythmList().size(); return; } std::vector rhythms = this->metronome->GetRhythmList(); std::copy(rhythms.cbegin(), rhythms.cend(), data); size = rhythms.size(); return; } void eXaDrums::GetBpms_(int* data, unsigned int& size) const { if(data == nullptr) { size = this->metronome->GetBpmeasList().size(); return; } std::vector bpms = this->metronome->GetBpmeasList(); std::copy(bpms.cbegin(), bpms.cend(), data); size = bpms.size(); return; } void eXaDrums::GetKitsNames_(const char** data, unsigned int& size) { if(data == nullptr) { size = drumModule->GetKitsNames().size(); return; } this->kitsNames.clear(); this->kitsNames = drumModule->GetKitsNames(); unsigned int numElements = std::min(size, kitsNames.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = kitsNames[i].c_str(); } return; } void eXaDrums::GetInstrumentsNames_(const char** data, unsigned int& size) { if(data == nullptr) { size = drumModule->GetInstrumentsNames().size(); return; } this->instrumentsNames.clear(); this->instrumentsNames = drumModule->GetInstrumentsNames(); unsigned int numElements = std::min(size, instrumentsNames.size()); for(unsigned int i = 0; i < numElements; i++) { data[i] = instrumentsNames[i].c_str(); } return; } double eXaDrums::GetClickPosition() const noexcept { return drumModule->GetClickPosition(); } long long eXaDrums::GetLastClickTime() const noexcept { return drumModule->GetLastClickTime(); } } libeXaDrums-0.6.0/Api/eXaDrums.h000066400000000000000000000232621377264061000163610ustar00rootroot00000000000000/* * eXaDrumsApi.h * * Created on: 8 Sep 2015 * Author: jeremy */ #ifndef LIBEXADRUMS_SOURCE_API_EXADRUMKIT_H_ #define LIBEXADRUMS_SOURCE_API_EXADRUMKIT_H_ /* The relative path helps when builddir differs from srcdir */ #include "../Api/Version.h" #include "../Util/ErrorHandling.h" #include #include #include #include #include namespace DrumKit { class Module; class Metronome; } namespace Sound { class Alsa; class Mixer;} namespace eXaDrumsApi { class eXaDrums { friend class Config; public: /** * @brief Construct a new eXaDrums object * * @param dataLocation Path to the configuration files */ explicit eXaDrums(const char* dataLocation) noexcept; /** * @brief Destroy the eXaDrums object * */ virtual ~eXaDrums(); // eXaDrums /** * @brief Return the current state of the module (playing/stopped) * * @return true The module is currently running * @return false The module is stopped */ bool IsStarted() const noexcept { return isStarted.load(); } /** * @brief Get the Data folder location * * @return std::string Data folder path */ std::string GetDataLocation() const noexcept; /** * @brief Get the current version of libexadrums * * @return std::string Version as string, e.g.: "x.y.z" */ static std::string GetVersion() noexcept; // Module /** * @brief Start the drum module * */ void Start(); /** * @brief Stop the drum module * */ void Stop(); /** * @brief Enable or disable beat recording * * @param enable Enable recording true, disable recording if false */ void EnableRecording(bool enable); /** * @brief Export raw beats data to xml format * * @param fileName The xml output file name */ void RecorderExport(const std::string& fileName); /** * @brief Export recorded data to wav (PCM) * * @param fileName The wav output file name */ void RecorderExportPCM(const std::string& fileName); /** * @brief Purge recorder temporary (csv) file * */ void RecorderPurgeTempFile(); // Metronome /** * @brief Enable or disable metronome * * @param enable Enable metronome if true, disable it if false */ void EnableMetronome(bool enable) const; /** * @brief Restart the metrnome * */ void RestartMetronome() const; /** * @brief Set metronome tempo * * @param tempo Int value: 40 to 250 bpm */ void ChangeTempo(std::size_t tempo) const; /** * @brief Set metronome volume * * @param volume Int value from 0 to 100 */ void ChangeClickVolume(std::size_t volume) const; /** * @brief Get the current tempo * * @return std::size_t current tempo */ std::size_t GetTempo() const noexcept; /** * @brief Get the click olume * * @return std::size_t click volume from 0 to 100 */ std::size_t GetClickVolume() const noexcept; /** * @brief Save the current metronome configuration * */ void SaveMetronomeConfig() const; /** * @brief Get the relative click position in the current measure * * @return double relative click position from 0 to 1 */ double GetClickPosition() const noexcept; /** * @brief Get the last time a new measure started * * @return long long timestamp */ long long GetLastClickTime() const noexcept; /** * @brief Get the clicks types * * @return std::vector clicks types names */ std::vector GetClicksTypes(); /** * @brief Set the click type * * @param id click type index */ void SetClickType(std::size_t id); /** * @brief Get the click type index * * @return std::size_t click type index */ std::size_t GetClickTypeId() const; /** * @brief Get the rhythms list * * @return std::vector Rythms: */ std::vector GetRhythms() const; /** * @brief Get the current metronome rhythm * * @return std::size_t Rhythm value */ std::size_t GetRhythm() const noexcept; /** * @brief Set the metronome rhythm (metronome plays 'rhythm' times faster) * * @param rhythm Rhythm value */ void SetRhythm(std::size_t rhythm) noexcept; /** * @brief Get the list of available number of clicks per measure * * @return std::vector List of values from 1 to 8 (number of metronome clicks per measure) */ std::vector GetBpms() const; /** * @brief Get the number of beats to be played by the metronome, per measure * * @return std::size_t number of beats per measure */ std::size_t GetBpmeas() const noexcept; /** * @brief Set the number of beats to be played by the metronome, per measure * * @param bpmeas number of beats per measure */ void SetBpmeas(std::size_t bpmeas) noexcept; // Kits /** * @brief Select drum kit to play with * * @param id Drum kit id */ void SelectKit(std::size_t id); /** * @brief Save drum kit configuration * * @param id Drum kit id */ void SaveKitConfig(std::size_t id) const; /** * @brief Delete drum kit * * @param id Drum kit id */ void DeleteKit(std::size_t id); /** * @brief Reload all drum kits configuration (from file) * */ void ReloadKits(); /** * @brief Get the number of drum kits * * @return std::size_t The number of drum kits */ std::size_t GetNumKits() const noexcept; /** * @brief Get the kit data file name * * @return std::string file name */ std::string GetKitDataFileName(); /** * @brief Get the drum kits names * * @return std::vector Drum kits names */ std::vector GetKitsNames(); // Instruments /** * @brief Set an Instrument volume * * @param id Instrument id * @param volume Volume (0, 100) */ void SetInstrumentVolume(std::size_t id, std::size_t volume); /** * @brief Get an Instrument volume * * @param id Instrument id * @return std::size_t Instrument volume (0, 100) */ std::size_t GetInstrumentVolume(std::size_t id) const; /** * @brief Get the instruments names * * @return std::vector List of instruments names */ std::vector GetInstrumentsNames(); /** * @brief Get an instrument triggers ids * * @param instrumentId instrument id * @return std::vector List of triggers ids */ std::vector GetInstrumentTriggersIds(std::size_t instrumentId) const; // Triggers /** * @brief Get the last trig timestamp * * @return long long Timestamp of the last trig event */ long long GetLastTrigTime() const noexcept; /** * @brief Get the last trig value * * @return std::size_t Get last trig event's velocity */ std::size_t GetLastTrigValue() const noexcept; /** * @brief Get the Trigger value * * @param id Trigger index * @return float Last trigger value */ float GetTriggerValue(size_t id); /** * @brief Set a given trigger sensor's value * * @param id Trigger id * @param channel Trigger channel * @param data Trig event's intensity */ void SetTriggerSensorValue(std::size_t id, char channel, short data); // Sensors /** * @brief Get the sensors resolution * * @return std::size_t Sensors resolution in bits */ std::size_t GetSensorsResolution() const noexcept; /** * @brief Get the sensor type (virtual or internal) * * @return true If the sensor is virtual * @return false If the sensor is internal */ bool IsSensorVirtual() const noexcept; /** * @brief Get the sensor's data transfer protocol (SPI of not) * * @return true If the sensor uses SPI * @return false If the sensor doesn't use SPI */ bool IsSensorSpi() const noexcept; // Sound /** * @brief Get the audio device name * * @return std::string Audio device name */ std::string GetAudioDeviceName() const noexcept; /** * @brief Get the init error * * @return Util::error Error struct (state and message) */ Util::error GetInitError() const { return this->init_error; } private: Util::error Start_(); Util::error Stop_(); Util::error EnableRecording_(bool enable); Util::error SelectKit_(int id); Util::error DeleteKit_(int id); Util::error SaveKitConfig_(std::size_t id) const; Util::error SetInstrumentVolume_(std::size_t id, std::size_t volume); const char* GetDataLocation_() const noexcept; static const char* GetVersion_() noexcept; const char* GetKitDataFileName_(); void GetClicksTypes_(const char** data, unsigned int& size); void GetRhythms_(int* data, unsigned int& size) const; void GetBpms_(int* data, unsigned int& size) const; void GetKitsNames_(const char** data, unsigned int& size); void GetInstrumentsNames_(const char** data, unsigned int& size); Util::error RecorderExport_(const char* fileName); Util::error RecorderExportPCM_(const char* fileName); Util::error RecorderPurgeTempFile_(); void GetInstrumentTriggersIds_(int instrumentId, int* data, unsigned int& size) const; Util::error GetTriggerValue_(size_t id, float& value); static const std::string metronomeConfigFile; static const std::string alsaConfigFile; std::string dataLocation; std::string kitDataFileName; std::unique_ptr drumModule; std::unique_ptr alsa; std::shared_ptr mixer; std::shared_ptr metronome; std::atomic isStarted; // Local copies of all the enums std::vector clicksTypes; std::vector kitsNames; std::vector instrumentsNames; Util::error init_error; }; } #include "eXaDrums.hpp" #endif /* LIBEXADRUMS_SOURCE_API_EXADRUMKIT_H_ */ libeXaDrums-0.6.0/Api/eXaDrums.hpp000066400000000000000000000073201377264061000167160ustar00rootroot00000000000000/* * eXaDrums.hpp * * Created on: 18 Dec 2016 * Author: jeremy */ #ifndef SOURCE_API_EXADRUMS_HPP_ #define SOURCE_API_EXADRUMS_HPP_ #include "eXaDrums.h" namespace eXaDrumsApi { inline void eXaDrums::Start() { Util::ErrorToException([&] { return this->Start_(); }); } inline void eXaDrums::Stop() { Util::ErrorToException([&] { return this->Stop_(); }); } inline void eXaDrums::EnableRecording(bool enable) { Util::ErrorToException([&] { return this->EnableRecording_(enable); }); } inline void eXaDrums::SelectKit(std::size_t id) { Util::ErrorToException([&] { return this->SelectKit_(id); }); } inline void eXaDrums::SaveKitConfig(std::size_t id) const { Util::ErrorToException([&] { return this->SaveKitConfig_(id); }); } inline void eXaDrums::DeleteKit(std::size_t id) { Util::ErrorToException([&] { return this->DeleteKit_(id); }); } inline std::string eXaDrums::GetDataLocation() const noexcept { return std::string(this->GetDataLocation_()); } inline std::string eXaDrums::GetVersion() noexcept { return std::string(eXaDrums::GetVersion_()); } inline std::string eXaDrums::GetKitDataFileName() { const char* str = this->GetKitDataFileName_(); if(str == nullptr) { throw Util::Exception("Selected kit's path could not be found.", Util::error_type_error); } return std::string(str); } inline void eXaDrums::RecorderExport(const std::string& fileName) { Util::ErrorToException([&]{ return RecorderExport_(fileName.data()); }); } inline void eXaDrums::RecorderExportPCM(const std::string& fileName) { Util::ErrorToException([&]{ return RecorderExportPCM_(fileName.data()); }); } inline void eXaDrums::RecorderPurgeTempFile() { Util::ErrorToException([&]{ return RecorderPurgeTempFile_(); }); } inline std::vector eXaDrums::GetClicksTypes() { unsigned int size; GetClicksTypes_(nullptr, size); std::vector data(size); GetClicksTypes_(data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } inline std::vector eXaDrums::GetRhythms() const { unsigned int size; GetRhythms_(nullptr, size); std::vector rhythms(size); GetRhythms_(rhythms.data(), size); return rhythms; } inline std::vector eXaDrums::GetBpms() const { unsigned int size; GetBpms_(nullptr, size); std::vector bpms(size); GetBpms_(bpms.data(), size); return bpms; } inline std::vector eXaDrums::GetKitsNames() { unsigned int size; GetKitsNames_(nullptr, size); std::vector data(size); GetKitsNames_(data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } inline std::vector eXaDrums::GetInstrumentTriggersIds(std::size_t instrumentId) const { unsigned int size; GetInstrumentTriggersIds_(instrumentId, nullptr, size); std::vector trigsIds(size); GetInstrumentTriggersIds_(instrumentId, trigsIds.data(), size); return trigsIds; } inline float eXaDrums::GetTriggerValue(size_t id) { float value = 0.f; Util::ErrorToException([&] { return this->GetTriggerValue_(id, value); }); return value; } inline void eXaDrums::SetInstrumentVolume(std::size_t id, std::size_t volume) { Util::ErrorToException([&] { return this->SetInstrumentVolume_(id, volume); }); } inline std::vector eXaDrums::GetInstrumentsNames() { unsigned int size; GetInstrumentsNames_(nullptr, size); std::vector data(size); GetInstrumentsNames_(data.data(), size); std::vector v(size); std::copy(data.cbegin(), data.cend(), v.begin()); return v; } } #endif /* SOURCE_API_EXADRUMS_HPP_ */ libeXaDrums-0.6.0/DrumKit/000077500000000000000000000000001377264061000153215ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/DrumModule/000077500000000000000000000000001377264061000173765ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/DrumModule/Module.cpp000066400000000000000000000236321377264061000213350ustar00rootroot00000000000000/* * Drum.cpp * * Created on: 6 May 2015 * Author: jeremy */ #include "Module.h" #include "../../IO/Spi.h" #include "../../Util/ErrorHandling.h" #include "../../Util/Threading.h" #include "../Triggers/TriggerManager.h" #include "../Triggers/TriggerFactory.h" #include "../Kits/KitManager.h" #include "TrigSound.h" #include using namespace Sound; using namespace Util; namespace DrumKit { Module::Module(std::string dir, const AlsaParams& alsaParams, std::shared_ptr mixer, std::shared_ptr metro) : directory{dir}, kitManager{dir + "Kits/"}, kitId{0}, isPlay{false}, soundBank{std::make_shared(dir)}, mixer{mixer}, metronome{metro}, metronomeSoundId{-1}, isMetronomeEnabled{false}, recorder{soundBank.get(), alsaParams} { mixer->SetSoundBank(soundBank); // Load Triggers LoadTriggers(); // Load all drum kits LoadKits(); // Select current kit SelectKit(kitId); // Set recorder recorder.SetDirectory(dir); recorder.SetMetronomeTimeFunc([&]{ return GetLastClickTime(); }); return; } Module::~Module() { isPlay.store(false); // Join all threads if(playThread.joinable()) { playThread.join(); } } void Module::Start() { isPlay.store(true); playThread = std::thread(&Module::Run, this); // Set high priority to the thread // int maxPriority = sched_get_priority_max(SCHED_FIFO); // // if(maxPriority > 1) // { // maxPriority--; // } // // sched_param sch_params; // sch_params.sched_priority = maxPriority; // // pthread_setschedparam(playThread.native_handle(), SCHED_FIFO, &sch_params); Util::SetThreadPriority(playThread.native_handle(), 99); // Uncomment if debugging under Linux // pthread_setname_np(playThread.native_handle(), "Module Thread\0"); return; } void Module::Stop() { isPlay.store(false); playThread.join(); mixer->Clear(); return; } void Module::EnableRecording(bool record) { if(record) { recorder.Start(); } else { recorder.Stop(); } } void Module::RecorderExport(const std::string& fileName) { recorder.Export(fileName); } void Module::RecorderExportPCM(const std::string& fileName) { recorder.ExportPCM(fileName); } void Module::RecorderPurgeTempFile() { recorder.PurgeTempFile(); } void Module::GetDirectory(std::string& dir) const noexcept { dir = this->directory; return; } void Module::SelectKit(std::size_t id) { if(id > kits.size()) { throw Util::Exception("This drum kit does not exist.", Util::errorType::error_type_error); } // Disable previous kit if it exists if(kitId < kits.size()) kits[kitId].Disable(); // Clear sound bank and mixer mixer->Clear(); soundBank->Clear(); // Update kit id kitId = id; // Enable new kit kits[kitId].Enable(); if(isMetronomeEnabled.load()) { EnableMetronome(true); } return; } std::vector Module::GetKitsNames() const noexcept { std::vector names; std::transform(kits.cbegin(), kits.cend(), std::back_inserter(names), [](const Kit& k) { return k.GetName(); }); return names; } std::vector Module::GetInstrumentsNames() const { const std::vector& instruments = kits[kitId].GetInstruments(); std::vector names; std::transform(instruments.cbegin(), instruments.cend(), std::back_inserter(names), [](const InstrumentPtr i) { return i->GetName(); }); return names; } void Module::SetInstrumentVolume(size_t id, float volume) { if(kitId >= kits.size()) { throw Exception("Could not set instrument volume: kit does not exist.", error_type_warning); } kits[kitId].SetInstrumentVolume(id, volume); } float Module::GetInstrumentVolume(int id) const { if(kitId >= kits.size()) { throw Exception("Could not get instrument volume: kit does not exist.", error_type_warning); } return kits[kitId].GetInstrumentVolume(id); } std::vector Module::GetInstrumentTriggersIds(int id) const { const std::vector& instruments = kits[kitId].GetInstruments(); auto const& instrument = instruments[id]; return instrument->GetTriggersIds(); } void Module::SaveKitConfig(std::size_t id) const { if(id < kits.size()) { kits[id].Save(); } else { throw Exception("Could not save kit configuration: this kit does not exist.", error_type_warning); } } std::string Module::GetKitLocation() const { if(kitId < kits.size()) { return kits[kitId].GetConfigFilePath(); } else { throw Exception("Selected kit's path could not be found.", error_type_error); } } bool Module::DeleteKit(const int& id) { bool isSuccess = kitManager.DeleteKit(id); if(isSuccess) { LoadKits(); } return isSuccess; } void Module::ReloadKits() { kitManager.ReScan(); LoadKits(); return; } void Module::ReloadTriggers() { LoadTriggers(); return; } float Module::GetTriggerValue(size_t id) const { if(id >= triggers.size()) { throw Exception("Trigger not found.", error_type_error); } return triggers[id]->GetLastTrigValue(); } void Module::EnableMetronome(bool enable) { if(enable) { metronome->GenerateClick(); std::vector data = metronome->GetData(); metronomeSoundId = soundBank->AddSound(data, 1.0f); soundBank->LoopSound(metronomeSoundId, true); mixer->PlaySound(metronomeSoundId, 1.0f); isMetronomeEnabled.store(true); } else { if(metronomeSoundId != -1) { // Stop metronome sound mixer->StopSound(metronomeSoundId); soundBank->DeleteSound(metronomeSoundId); } isMetronomeEnabled.store(false); } return; } void Module::ChangeTempo(int tempo) { metronome->SetTempo(tempo); RestartMetronome(); return; } void Module::ChangeClickVolume(int volume) { if(isMetronomeEnabled.load()) { soundBank->SetSoundVolume(metronomeSoundId, float(volume / 100.0f)); } return; } float Module::GetClickVolume() const noexcept { if(metronomeSoundId != -1) { return soundBank->GetSound(metronomeSoundId).GetVolume(); } else { return 0.0f; } } double Module::GetClickPosition() const noexcept { if(isMetronomeEnabled.load()) { unsigned long pos = soundBank->GetSound(metronomeSoundId).LoadIndex(); double relPos = double(pos) / double(soundBank->GetSound(metronomeSoundId).GetLength()); //fraction -= int(fraction); return relPos; } else { return 0.0f; } } int64_t Module::GetLastClickTime() const noexcept { if(isMetronomeEnabled.load()) { return soundBank->GetSound(metronomeSoundId).GetLastStartTime(); } else { return 0; } } void Module::RestartMetronome() { if(isMetronomeEnabled.load()) { EnableMetronome(false); EnableMetronome(true); } return; } // PRIVATE METHODS void Module::LoadKits() { // Clear kits list kits.clear(); // Get all kits locations std::vector kitsPaths = kitManager.GetKitsLocations(); std::transform(kitsPaths.cbegin(), kitsPaths.cend(), std::back_inserter(kits), [this](std::string const& kitPath) { KitParameters kitParams; KitManager::LoadKit(kitPath, kitParams); return Kit(kitParams, this->triggers, this->soundBank); }); return; } void Module::LoadTriggers() { // Update sensors configuration TriggerManager::LoadSensorsConfig(this->directory, this->sensorsConfig); if(sensorsConfig.sensorType == IO::SensorType::Spi) { IO::Spi::get().Close(); IO::Spi::get().Open(sensorsConfig.samplingRate, 0); } // Load triggers TriggerManager::LoadTriggersConfig(this->directory, sensorsConfig, this->triggersParameters); //TriggerManager::SaveTriggersConfig(this->directory, this->triggersParameters); // Create Triggers this->CreateTriggers(this->triggersParameters); return; } void Module::Run() { if(isMetronomeEnabled.load()) { const auto metronomeVolume = GetClickVolume(); mixer->PlaySound(metronomeSoundId, metronomeVolume); } lastTrigTime.store(0, std::memory_order_relaxed); lastTrigValue.store(0, std::memory_order_relaxed); // Skip high-pass filter transient state for(size_t i = 0; i < 100 * triggers.size(); ++i) std::for_each(triggers.begin(), triggers.end(), [](TriggerPtr& triggerPtr) { triggerPtr->Refresh(); }); while(isPlay.load()) { // Refresh triggers std::for_each(triggers.begin(), triggers.end(), [](TriggerPtr& triggerPtr) { triggerPtr->Refresh(); }); // Get most recent hit auto it = std::max_element(triggers.cbegin(), triggers.cend(), [](const TriggerPtr& t1, const TriggerPtr& t2) { return t1->GetTriggerState().trigTime < t2->GetTriggerState().trigTime; }); int64_t trigTime = (*it)->GetTriggerState().trigTime; int64_t prevTrigTime = lastTrigTime.exchange(trigTime, std::memory_order_release); if(prevTrigTime != trigTime) { lastTrigValue.store(int((*it)->GetTriggerState().value * 100.0f), std::memory_order_release); //std::cout << double(trigTime - soundBank->GetSound(metronomeSoundId).GetLastStartTime()) / 1000. << std::endl; } // Check instruments triggers and send sounds to mixer if necessary const std::vector& instruments = kits[kitId].GetInstruments(); for(const InstrumentPtr& instrumentPtr : instruments) { bool isTriggerEvent = instrumentPtr->IsTriggerEvent(); if(isTriggerEvent) { int soundId = 0; float volume = 1.0f; instrumentPtr->GetSoundProps(soundId, volume); if(recorder.IsRecording(std::memory_order_relaxed)) { recorder.Push(TrigSound{instrumentPtr->GetId(), soundId, trigTime, volume}); } mixer->PlaySound(soundId, volume); } } } return; } void Module::CreateTriggers(const std::vector& triggersParameters) { triggers.clear(); for(const TriggerParameters& triggerParameters : triggersParameters) { TriggerPtr triggerPtr = TriggerFactory::CreateTrigger(triggerParameters); triggers.push_back(triggerPtr); } return; } } libeXaDrums-0.6.0/DrumKit/DrumModule/Module.h000066400000000000000000000067111377264061000210010ustar00rootroot00000000000000/* * Drum.h * * Created on: 6 May 2015 * Author: jeremy */ #ifndef RASPIDRUMS_SOURCE_DRUMKIT_MODULE_H_ #define RASPIDRUMS_SOURCE_DRUMKIT_MODULE_H_ #include "../../Metronome/Metronome.h" #include "../../IO/SensorsConfig.h" #include "../../Sound/Mixer/Mixer.h" #include "../../Sound/Alsa/AlsaParams.h" #include "../../Sound/SoundBank/SoundBank.h" #include "../../Util/SimpleSafeQueue.h" #include "../Kits/Kit.h" #include "Recorder.h" #include #include #include #include #include namespace DrumKit { class Module { public: Module(std::string dir, const Sound::AlsaParams& alsaParams, std::shared_ptr mixer, std::shared_ptr metro); virtual ~Module(); // Kit void SaveKitConfig(std::size_t id) const; std::string GetKitLocation() const; bool DeleteKit(const int& id); void SelectKit(std::size_t id); void ReloadKits(); std::size_t GetKitId() const noexcept { return kitId; } std::size_t GetNumKits() const noexcept { return kits.size(); }; std::vector GetKitsNames() const noexcept; std::vector GetInstrumentsNames() const; // Instrument void SetInstrumentVolume(size_t id, float volume); float GetInstrumentVolume(int id) const; int GetNumInstruments() const { return kits[kitId].GetNumInstruments(); }; std::vector GetInstrumentTriggersIds(int id) const; // Triggers void ReloadTriggers(); void SetTriggerParameters(int triggerId, const TriggerParameters& params) { triggers[triggerId]->SetParameters(params);} void SetTriggerSensorValue(int id, char channel, short data) { triggers[id]->SetSensorData(channel, data); } std::vector GetTriggersParameters() const { return this->triggersParameters; } unsigned long long GetLastTrigTime() const noexcept { return lastTrigTime.load(std::memory_order_acquire); } int GetLastTrigValue() const noexcept { return lastTrigValue.load(std::memory_order_acquire); } float GetTriggerValue(size_t id) const; // Module void Start(); void Stop(); void EnableRecording(bool record); void RecorderExport(const std::string& fileName); void RecorderExportPCM(const std::string& fileName); void RecorderPurgeTempFile(); void GetDirectory(std::string& dir) const noexcept; // Metronome void EnableMetronome(bool enable); void ChangeTempo(int tempo); void ChangeClickVolume(int volume); float GetClickVolume() const noexcept; void RestartMetronome(); double GetClickPosition() const noexcept; int64_t GetLastClickTime() const noexcept; // Config IO::SensorsConfig GetSensorsConfig() const noexcept { return sensorsConfig; } private: void LoadKits(); void LoadTriggers(); void Run(); void CreateTriggers(const std::vector& trigParams); bool IsMetronomeEnabled() const; // Module std::string directory; std::thread playThread; // Kits KitManager kitManager; std::size_t kitId; std::vector kits; std::atomic isPlay; // Triggers std::vector triggersParameters; std::vector triggers; std::atomic lastTrigTime; std::atomic lastTrigValue; IO::SensorsConfig sensorsConfig; // Sound std::shared_ptr soundBank; std::shared_ptr mixer; std::shared_ptr metronome; int metronomeSoundId; std::atomic isMetronomeEnabled; Recorder recorder; }; } #endif /* RASPIDRUMS_SOURCE_DRUMKIT_MODULE_H_ */ libeXaDrums-0.6.0/DrumKit/DrumModule/Recorder.cpp000066400000000000000000000235561377264061000216620ustar00rootroot00000000000000/* * Recorder.cpp * * Created on: 24 Feb 2018 * Author: jeremy */ #include "Recorder.h" #include "../../Util/Parsing.h" #include "../../Util/Misc.h" #include "../../Util/Xml.h" #include "../../Util/Time.h" #include "../../Util/Crypt.h" #include "../../Util/ErrorHandling.h" #include "../../Sound/Util/WavUtil.h" #include #include #include #include #include #include #if __has_include() #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif using namespace Sound; using namespace Util; namespace DrumKit { Recorder::Recorder(SoundBank* sndBankPtr, const AlsaParams& alsaParams) noexcept : isRecord{false}, soundBankPtr{sndBankPtr}, alsaParameters{alsaParams} { } Recorder::~Recorder() { isRecord.store(false, std::memory_order_relaxed); if(recordThread.joinable()) { recordThread.join(); } } void Recorder::Start() { isRecord.store(true, std::memory_order_release); recordThread = std::thread(&Recorder::Record, this); } void Recorder::Stop() { isRecord.store(false, std::memory_order_release); recordThread.join(); } void Recorder::PurgeTempFile() { if(isRecord.load(std::memory_order_acquire)) return; // File is aleady being accessed, don't try to remove. const auto lastFilePath = fs::path{lastFile}; try { const auto exists = fs::exists(lastFile); if(exists) { fs::remove(lastFilePath); } } catch(...) { return; } } void Recorder::Export(const std::string& fileName) { outputFile = fileName; ConvertFile(lastFile); } void Recorder::ExportPCM(const std::string& fileName) { outputFile = fileName; ConvertToPCM(lastFile); } // PRIVATE Methods void Recorder::DumpBufferToFile() { while(!buffer.empty()) { TrigSound t = buffer.front(); buffer.pop(); file << t.instrumentId << ',' << t.soundId << ',' << t.timeStamp << ',' << t.volume << '\n'; } // Insert keyframe if the metronome is running auto lastClickTime = getLastClickTime(); if(lastClickTime > 0) { file << -1 << ',' << -1 << ',' << lastClickTime << ',' << 0.5f << '\n'; } file.flush(); }; void Recorder::Record() { using namespace Util; using namespace std::chrono_literals; using namespace std::chrono; using namespace std::this_thread; // Create new file const auto fileTimeStamp = duration_cast(system_clock::now().time_since_epoch()).count(); const std::string fileName = directory + std::to_string(fileTimeStamp) + ".csv"; file.open(fileName); while(isRecord.load(std::memory_order_acquire)) { TrigSound trigSound; while(recordQueue.Pop(trigSound)) { buffer.emplace(std::move(trigSound)); } // Write data to file if(buffer.size() >= recordQueue.Capacity() / 8) { DumpBufferToFile(); } // Wait before we try to get more sounds if(isRecord.load(std::memory_order_relaxed)) { sleep_for(500ms); } } DumpBufferToFile(); file.close(); lastFile = fileName; //ConvertFile(fileName); } /** * Converts the temporary csv file to an xml file. * @param fileLoc File location. */ void Recorder::ConvertFile(const std::string& fileLoc) { using namespace Util; using namespace tinyxml2; using namespace std::chrono; using TrigSoundTuple = std::tuple; std::ifstream file{fileLoc}; if(!file.good()) { throw Exception("Could not load record file.", error_type_error); } // Read file line by line std::vector lines{std::istream_iterator{file}, std::istream_iterator{}}; XMLDocument doc; auto root = doc.NewElement("root"); // We'll store all the sounds ids in that vector std::vector soundsIds; soundsIds.reserve(lines.size()); std::map> instrumentsSounds; auto xmlSounds = CreateXmlElement(doc, "Sounds"); root->InsertEndChild(xmlSounds); for(const auto& l : lines) { std::istringstream iss(l); std::vector tokens{std::istream_iterator>{iss}, std::istream_iterator>{}}; TrigSoundTuple tuple; VectorOfStrToTuple(tokens, tuple); TrigSound t; std::tie(t.instrumentId, t.soundId, t.timeStamp, t.volume) = tuple; soundsIds.push_back(t.soundId); instrumentsSounds[t.instrumentId].emplace(t.soundId); xmlSounds->InsertEndChild(CreateXmlElement(doc, "Sound", "", {{"Id", t.soundId}, {"TrigTime", t.timeStamp}, {"Volume", t.volume}})); } // Remove metronome from the instruments list instrumentsSounds.erase(-1); // Get all the unique sounds ids std::set uniqueSoundsIds(soundsIds.begin(), soundsIds.end()); // Remove the metronome from the set uniqueSoundsIds.erase(-1); // Create xml document // Header auto xmlHeader = CreateXmlElement(doc, "Header"); { const auto fileTimeStamp = duration_cast(system_clock::now().time_since_epoch()).count(); xmlHeader->InsertEndChild(CreateXmlElement(doc, "Date", TimeStampToStr(fileTimeStamp))); xmlHeader->InsertEndChild(CreateXmlElement(doc, "SampleRate", alsaParameters.sampleRate)); xmlHeader->InsertEndChild(CreateXmlElement(doc, "NumberOfChannels", alsaParameters.nChannels)); auto instrumentsXml = CreateXmlElement(doc, "Instruments"); xmlHeader->InsertEndChild(instrumentsXml); for(const auto& instrumentSounds : instrumentsSounds) { instrumentsXml->InsertEndChild(CreateXmlElement(doc, "Instrument", "", {{"Id", instrumentSounds.first}, {"Sounds", JoinToStr(instrumentSounds.second, ", ")}})); } root->InsertFirstChild(xmlHeader); } // Data { auto xmlData = CreateXmlElement(doc, "Data"); for(const auto& soundId : uniqueSoundsIds) { const auto& sound = soundBankPtr->GetSound(soundId); const auto& soundData = sound.GetInternalData(); xmlData->InsertEndChild(CreateXmlElement(doc, "Sound", Base64Encode(soundData), {{"Id", soundId}})); } xmlHeader->InsertEndChild(xmlData); } doc.InsertFirstChild(root); if(outputFile.empty()) { doc.SaveFile(std::string(directory + std::string("rec.xml")).data()); } else { fs::path p{outputFile}; if(p.extension() == ".xml") { doc.SaveFile(p.c_str()); } else { doc.SaveFile((outputFile + ".xml").data()); } } } void Recorder::ConvertToPCM(const std::string& inputFile) { using SoundData = std::valarray; using SoundDataF = std::valarray; using namespace tinyxml2; using namespace std::chrono; using TrigSoundTuple = std::tuple; std::ifstream file{inputFile}; if(!file.good()) { throw Exception("Could not load record file.", error_type_error); } // Read file line by line std::vector lines{std::istream_iterator{file}, std::istream_iterator{}}; // We'll store all the sounds ids in that vector std::vector sounds; sounds.reserve(lines.size()); for(const auto& l : lines) { std::istringstream iss(l); std::vector tokens{std::istream_iterator>{iss}, std::istream_iterator>{}}; TrigSoundTuple tuple; VectorOfStrToTuple(tokens, tuple); TrigSound t; std::tie(t.instrumentId, t.soundId, t.timeStamp, t.volume) = tuple; sounds.push_back(t); } std::vector soundsIds(lines.size()); std::transform(sounds.begin(), sounds.end(), soundsIds.begin(), [](const auto& s) { return s.soundId; }); // Get all the unique sounds ids std::set uniqueSoundsIds(soundsIds.begin(), soundsIds.end()); // Remove the metronome from the set uniqueSoundsIds.erase(-1); std::vector soundsData; std::vector soundsDataIds; for(const auto& soundId : uniqueSoundsIds) { const auto& sound = soundBankPtr->GetSound(soundId); const auto& soundData = sound.GetInternalData(); soundsData.emplace_back(soundData.data(), soundData.size()); soundsDataIds.push_back(soundId); } const auto tStart = sounds.front().timeStamp; const auto tEnd = sounds.back().timeStamp; const auto tDuration = tEnd - tStart; // const auto lastSoundId = sounds.back().soundId; const auto longuestSoundId = std::max_element(soundsData.begin(), soundsData.end(), [](const auto& a, const auto& b) { return a.size() < b.size(); }) - soundsData.begin(); const auto sampleRate = alsaParameters.sampleRate; const auto lastSampleStart = 2 * (tDuration / 1000000.) * sampleRate; // 2 * for stereo size_t totalLength = (int64_t)lastSampleStart + soundsData[longuestSoundId].size() + 1; if(totalLength % 2 != 0) { totalLength++; } SoundData data(int16_t{0}, totalLength); for(const auto& sound : sounds) { if(sound.soundId == -1) // Discard metronome sound continue; const auto idSound = std::find_if(soundsDataIds.begin(), soundsDataIds.end(), [&] (const auto sid) { return sid == sound.soundId; }) - soundsDataIds.begin(); const auto tSample = sound.timeStamp; const auto volume = sound.volume; auto sampleNb = (int64_t) (2 * ((tSample - tStart) / 1000000.) * sampleRate); if(sampleNb % 2 != 0) sampleNb++; SoundDataF snd(0.f, soundsData[idSound].size()); std::copy(begin(soundsData[idSound]), end(soundsData[idSound]), begin(snd)); snd *= 0.66 * volume; // TODO: smart scaling factor and use user volume SoundData sndVol(soundsData[idSound].size()); std::copy(std::begin(snd), std::end(snd), std::begin(sndVol)); std::slice slice(sampleNb, soundsData[idSound].size(), 1); data[slice] += sndVol; } std::ofstream pcmFile(outputFile, std::ios::binary); WavHeader header; header.SetDataLength(data.size() * sizeof(int16_t)); header.SetSampleRate(sampleRate); const auto bytes = header.ToBytes(); pcmFile.write((char*)bytes.data(), bytes.size()); pcmFile.write((char*)&data[0], data.size() * sizeof(int16_t)); pcmFile.close(); } } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/DrumModule/Recorder.h000066400000000000000000000033551377264061000213220ustar00rootroot00000000000000/* * Recorder.h * * Created on: 24 Feb 2018 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_DRUMMODULE_RECORDER_H_ #define SOURCE_DRUMKIT_DRUMMODULE_RECORDER_H_ #include "../../Sound/SoundBank/SoundBank.h" #include "../../Util/SimpleSafeQueue.h" #include "../../Sound/Alsa/AlsaParams.h" #include "TrigSound.h" #include #include #include #include #include #include #include namespace DrumKit { class Module; class Recorder { public: Recorder(Sound::SoundBank* sndBankPtr, const Sound::AlsaParams& alsaParams) noexcept; virtual ~Recorder(); void SetDirectory(const std::string& dir) noexcept { directory = dir + "Rec/"; } template void SetMetronomeTimeFunc(T&& f) noexcept { getLastClickTime = std::function{f}; } void Start(); void Stop(); void PurgeTempFile(); void Export(const std::string& fileName); void ExportPCM(const std::string& fileName); bool Push(TrigSound&& value) { return recordQueue.Push(value); } bool IsRecording(std::memory_order memOrder = std::memory_order_acquire) const { return isRecord.load(memOrder); } private: void Record(); void DumpBufferToFile(); void ConvertFile(const std::string& file); void ConvertToPCM(const std::string& file); std::atomic isRecord; Sound::SoundBank* soundBankPtr; Sound::AlsaParams alsaParameters; std::string directory; std::string outputFile; std::string lastFile; std::function getLastClickTime = []{return 0;}; std::ofstream file; std::thread recordThread; std::queue buffer; Util::SimpleSafeQueue recordQueue; }; } /* namespace DrumKit */ #endif /* SOURCE_DRUMKIT_DRUMMODULE_RECORDER_H_ */ libeXaDrums-0.6.0/DrumKit/DrumModule/TrigSound.h000066400000000000000000000005601377264061000214660ustar00rootroot00000000000000/* * TrigSound.h * * Created on: 3 Mar 2018 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_DRUMMODULE_TRIGSOUND_H_ #define SOURCE_DRUMKIT_DRUMMODULE_TRIGSOUND_H_ #include namespace DrumKit { struct TrigSound { int instrumentId; int soundId; int64_t timeStamp; float volume; }; } #endif /* SOURCE_DRUMKIT_DRUMMODULE_TRIGSOUND_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/000077500000000000000000000000001377264061000176545ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Instruments/Cymbals/000077500000000000000000000000001377264061000212465ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Instruments/Cymbals/TestHiHat.cpp000066400000000000000000000055101377264061000236100ustar00rootroot00000000000000/* * TestHiHat.cpp * * Created on: 2 Nov 2016 * Author: jeremy */ #include "TestHiHat.h" #include "../../../Sound/InstrumentSoundType.h" #include "../../../Sound/SoundProcessor/SoundProcessor.h" #include using namespace Sound; namespace DrumKit { TestHiHat::TestHiHat(InstrumentParameters parameters, std::shared_ptr soundBank) : Instrument(parameters, soundBank), cymbalSoundId(0) { return; } void TestHiHat::SetTriggers(const std::vector& triggers) { for(const TriggerPtr& triggerPtr : triggers) { auto triggerIdAndLocation = std::find_if(parameters.triggersIdsAndLocations.cbegin(), parameters.triggersIdsAndLocations.cend(), [triggerPtr](std::pair const& idAndLocation) { return (idAndLocation.first == triggerPtr->GetId()); }); if(triggerIdAndLocation != std::end(parameters.triggersIdsAndLocations)) { TriggerLocation triggerLocation = triggerIdAndLocation->second; switch (triggerLocation) { case TriggerLocation::DrumHead: this->cymbalTrigger = triggerPtr; break; case TriggerLocation::Rim: this->pedalTrigger = triggerPtr; break; default: break; } } } return; } void TestHiHat::SetSound(const InstrumentSoundInfo& soundInfo) { InstrumentSoundType soundType = soundInfo.type; std::string soundLocation = soundInfo.soundLocation; switch (soundType) { case InstrumentSoundType::Default: { cymbalSoundId = soundBank->LoadSound(soundLocation, parameters.volume); for(int i = 0; i < 10; i++) { const auto& cymbalSound = soundBank->GetSound(cymbalSoundId); auto&& newSound = SoundProcessor::Muffle(cymbalSound, 0.25f/float(i + 1)); int newSoundId = soundBank->AddSound(std::move(newSound), parameters.volume); hiHatSoundsIds.push_back(newSoundId); } break; } default: throw -1; break; } return; } void TestHiHat::SetVolume(float volume) { soundBank->SetSoundVolume(cymbalSoundId, volume); std::for_each(hiHatSoundsIds.cbegin(), hiHatSoundsIds.cend(), [&](const int& id) { soundBank->SetSoundVolume(id, volume); }); parameters.volume = volume; return; } bool TestHiHat::IsTriggerEvent() const { TriggerState cymbalTriggerState = cymbalTrigger->GetTriggerState(); if(cymbalTriggerState.isTrig) { return true; } else { return false; } } void TestHiHat::GetSoundProps(int& id, float& volume) const { TriggerState cymbalTriggerState = cymbalTrigger->GetTriggerState(); TriggerState pedalTriggerState = pedalTrigger->GetTriggerState(); if(cymbalTriggerState.isTrig) { float pedalPosition = pedalTriggerState.value; int hiHatSoundIndex = cymbalSoundId + std::floor(pedalPosition * 10) + 1; id = hiHatSoundIndex; volume = cymbalTriggerState.value; } return; } } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/Instruments/Cymbals/TestHiHat.h000066400000000000000000000027461377264061000232650ustar00rootroot00000000000000/* * TestHiHat.h * * Created on: 2 Nov 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_INSTRUMENTS_CYMBALS_TESTHIHAT_H_ #define SOURCE_DRUMKIT_INSTRUMENTS_CYMBALS_TESTHIHAT_H_ #include "../../../Sound/SoundBank/SoundBank.h" #include "../../../Sound/InstrumentSoundType.h" #include "../../Triggers/Triggers/Trigger.h" #include "../Instrument.h" #include #include #include namespace DrumKit { class TestHiHat: public Instrument { public: TestHiHat(InstrumentParameters parameters, std::shared_ptr soundBank); ~TestHiHat() = default; virtual void SetTriggers(std::vector const& triggers) final; virtual void SetSound(InstrumentSoundInfo const& soundInfo) final; virtual void SetVolume(float volume) final; virtual bool IsTriggerEvent() const final; virtual void GetSoundProps(int& id, float& volume) const final; virtual std::vector GetSoundTypes() const final { return {Sound::InstrumentSoundType::Default}; } virtual std::vector GetTriggersLocations() const final { return {TriggerLocation::DrumHead, TriggerLocation::Rim}; }; virtual std::vector GetTriggersIds() const final { return {cymbalTrigger->GetId(), pedalTrigger->GetId()}; }; private: TriggerPtr cymbalTrigger; TriggerPtr pedalTrigger; std::vector hiHatSoundsIds; int cymbalSoundId; }; } /* namespace DrumKit */ #endif /* SOURCE_DRUMKIT_INSTRUMENTS_CYMBALS_TESTHIHAT_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/Drums/000077500000000000000000000000001377264061000207465ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Instruments/Drums/TestDrum.cpp000066400000000000000000000062621377264061000232270ustar00rootroot00000000000000/* * Drum.cpp * * Created on: 28 Oct 2015 * Author: jeremy */ #include "TestDrum.h" #include using namespace Sound; namespace DrumKit { TestDrum::TestDrum(InstrumentParameters parameters, std::shared_ptr soundBank): Instrument(parameters, soundBank), drumHeadSoundId(0), drumRimSoundId(0) { return; } void TestDrum::SetTriggers(std::vector const& triggers) { for(const TriggerPtr& triggerPtr : triggers) { auto triggerIdAndLocation = std::find_if(parameters.triggersIdsAndLocations.cbegin(), parameters.triggersIdsAndLocations.cend(), [triggerPtr](std::pair const& idAndLocation) { return (idAndLocation.first == triggerPtr->GetId()); }); if(triggerIdAndLocation != std::end(parameters.triggersIdsAndLocations)) { TriggerLocation triggerLocation = triggerIdAndLocation->second; switch (triggerLocation) { case TriggerLocation::DrumHead: this->drumHeadTrigger = triggerPtr; break; case TriggerLocation::Rim: this->drumRimTrigger = triggerPtr; break; default: break; } } } return; } void TestDrum::SetSound(InstrumentSoundInfo const& soundInfo) { InstrumentSoundType soundType = soundInfo.type; std::string soundLocation = soundInfo.soundLocation; switch (soundType) { case InstrumentSoundType::RimShot: drumRimSoundId = soundBank->LoadSound(soundLocation, parameters.volume); break; case InstrumentSoundType::Default: drumHeadSoundId = soundBank->LoadSound(soundLocation, parameters.volume); break; default: throw -1; break; } return; } void TestDrum::SetVolume(float volume) { soundBank->SetSoundVolume(drumRimSoundId, volume); soundBank->SetSoundVolume(drumHeadSoundId, volume); parameters.volume = volume; return; } bool TestDrum::IsTriggerEvent() const { TriggerState headTriggerState = drumHeadTrigger->GetTriggerState(); TriggerState rimTriggerState = drumRimTrigger->GetTriggerState(); if(headTriggerState.isTrig || rimTriggerState.isTrig) { return true; } else { return false; } } void TestDrum::GetSoundProps(int& id, float& volume) const { TriggerState headTriggerState = drumHeadTrigger->GetTriggerState(); TriggerState rimTriggerState = drumRimTrigger->GetTriggerState(); if(headTriggerState.isTrig) { id = drumHeadSoundId; volume = headTriggerState.value; } else if(rimTriggerState.isTrig) { id = drumRimSoundId; volume = rimTriggerState.value; } return; } // PRIVATE /*void Drum::GenerateSounds() { std::function genSounds = [this](InstrumentSoundInfo soundInfo) { std::vector soundData; unsigned int soundDuration; // Load sound //Sound::SoundBank::LoadSound(soundInfo.soundLocation, soundData, soundDuration); switch (soundInfo.type) { case Sound::InstrumentSoundType::Default: int id; //soundProcessor->AddSound(id, soundData); // Internal Id = 0 for default sound soundIds.insert(std::pair(0, id)); break; default: break; } }; std::for_each(parameters.soundsInfo.cbegin(), parameters.soundsInfo.cend(), genSounds); return; } */ } libeXaDrums-0.6.0/DrumKit/Instruments/Drums/TestDrum.h000066400000000000000000000026511377264061000226720ustar00rootroot00000000000000/* * Drum.h * * Created on: 28 Oct 2015 * Author: jeremy */ #ifndef LIBEXADRUMS_SOURCE_DRUMKIT_DRUM_H_ #define LIBEXADRUMS_SOURCE_DRUMKIT_DRUM_H_ #include "../../../Sound/SoundBank/SoundBank.h" #include "../../../Sound/InstrumentSoundType.h" #include "../../Triggers/Triggers/Trigger.h" #include "../Instrument.h" #include #include namespace DrumKit { class TestDrum : public Instrument { public: TestDrum(InstrumentParameters parameters, std::shared_ptr sb); ~TestDrum() = default; virtual void SetTriggers(std::vector const& triggers) final; virtual void SetSound(InstrumentSoundInfo const& soundInfo) final; virtual void SetVolume(float volume) final; virtual bool IsTriggerEvent() const final; virtual void GetSoundProps(int& id, float& volume) const final; virtual std::vector GetSoundTypes() const final { return {Sound::InstrumentSoundType::Default, Sound::InstrumentSoundType::RimShot}; } virtual std::vector GetTriggersLocations() const final { return {TriggerLocation::DrumHead, TriggerLocation::Rim}; }; virtual std::vector GetTriggersIds() const final { return {drumHeadTrigger->GetId(), drumRimTrigger->GetId()}; }; private: TriggerPtr drumHeadTrigger; TriggerPtr drumRimTrigger; int drumHeadSoundId; int drumRimSoundId; }; } #endif /* LIBEXADRUMS_SOURCE_DRUMKIT_DRUM_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/Instrument.cpp000066400000000000000000000004631377264061000225330ustar00rootroot00000000000000/* * Instrument.cpp * * Created on: 15 Nov 2015 * Author: jeremy */ #include "Instrument.h" namespace DrumKit { Instrument::Instrument(const InstrumentParameters& parameters, std::shared_ptr sb) : parameters(parameters), soundBank(std::move(sb)) { return; } } libeXaDrums-0.6.0/DrumKit/Instruments/Instrument.h000066400000000000000000000027241377264061000222020ustar00rootroot00000000000000/* * IInstrument.h * * Created on: 15 Nov 2015 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENT_H_ #define SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENT_H_ #include "../../Sound/SoundBank/SoundBank.h" #include "../Triggers/Triggers/Trigger.h" #include "InstrumentParameters.h" #include #include namespace DrumKit { class Instrument { public: Instrument(const InstrumentParameters& parameters, std::shared_ptr sb); virtual void SetTriggers(std::vector const& triggers) = 0; virtual void SetSound(InstrumentSoundInfo const& soundInfo) = 0; virtual void SetVolume(float volume) = 0; virtual void GetSoundProps(int& id, float& volume) const = 0; virtual bool IsTriggerEvent() const = 0; virtual int GetId() const { return this->parameters.id; } virtual float GetVolume() const { return this->parameters.volume; } virtual std::string GetName() const { return this->parameters.instrumentName; } virtual std::vector GetSoundTypes() const = 0; virtual std::vector GetTriggersLocations() const = 0; virtual std::vector GetTriggersIds() const = 0; protected: virtual ~Instrument() = default; InstrumentParameters parameters; std::shared_ptr soundBank; //std::map soundIds; private: }; typedef std::shared_ptr InstrumentPtr; } #endif /* SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENT_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/InstrumentFactory.h000066400000000000000000000030651377264061000235310ustar00rootroot00000000000000/* * InstrumentFactory.h * * Created on: 15 Jun 2017 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTFACTORY_H_ #define SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTFACTORY_H_ #include "../../Util/ErrorHandling.h" #include "../../Sound/SoundBank/SoundBank.h" #include "../../Sound/InstrumentSoundType.h" #include "../Triggers/TriggerType.h" #include "Cymbals/TestHiHat.h" #include "Drums/TestDrum.h" #include "Pads/Pad.h" #include "InstrumentType.h" #include "Instrument.h" #include namespace DrumKit { class InstrumentFactory { public: static InstrumentPtr CreateInstrument(InstrumentType type, InstrumentParameters parameters, std::shared_ptr sb) { switch(type) { case InstrumentType::Pad: return std::make_shared(parameters, sb); case InstrumentType::TestDrum: return std::make_shared(parameters, sb); case InstrumentType::HiHat: return std::make_shared(parameters, sb); } throw Util::Exception("Instrument type does not exist.", Util::error_type_error); } static std::vector GetSoundsTypes(InstrumentType type) { return CreateInstrument(type, InstrumentParameters(), nullptr)->GetSoundTypes(); } static std::vector GetTriggersTypes(InstrumentType type) { return CreateInstrument(type, InstrumentParameters(), nullptr)->GetTriggersLocations(); } private: InstrumentFactory() = delete; ~InstrumentFactory() = delete; }; } #endif /* SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTFACTORY_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/InstrumentParameters.h000066400000000000000000000032301377264061000242170ustar00rootroot00000000000000/* * InstrumentParameters.h * * Created on: 15 Nov 2015 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTPARAMETERS_H_ #define SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTPARAMETERS_H_ #include "../../Util/Xml.h" #include "../../Util/Enums.h" #include "../Triggers/TriggerLocation.h" #include "InstrumentSoundInfo.h" #include "InstrumentType.h" #include #include #include #include namespace DrumKit { struct InstrumentParameters { void ToXml(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* element) { using namespace Util; element->InsertEndChild(CreateXmlElement(doc, "instrumentName", instrumentName)); // Triggers { // Create triggers element tinyxml2::XMLElement* triggers = CreateXmlElement(doc, "triggers", ""); for(const auto& trigger : triggersIdsAndLocations) { triggers->InsertEndChild(CreateXmlElement(doc, "trigger", trigger.first, {{"location", Enums::ToString(trigger.second)}})); } element->InsertEndChild(triggers); } // Sounds { // Create sounds element tinyxml2::XMLElement* sounds = CreateXmlElement(doc, "sounds", ""); for(const auto& sound : soundsInfo) { sounds->InsertEndChild(CreateXmlElement(doc, "sound", sound.soundLocation, {{"type", Enums::ToString(sound.type)}})); } element->InsertEndChild(sounds); } } InstrumentType instrumentType; int id; float volume; std::string instrumentName; std::vector soundsInfo; std::vector> triggersIdsAndLocations; }; } #endif /* SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTPARAMETERS_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/InstrumentSoundInfo.h000066400000000000000000000007311377264061000240230ustar00rootroot00000000000000/* * InstrumentSoundInfo.h * * Created on: 15 Feb 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTSOUNDINFO_H_ #define SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTSOUNDINFO_H_ #include "../../Sound/InstrumentSoundType.h" #include namespace DrumKit { struct InstrumentSoundInfo { int id; std::string soundLocation; Sound::InstrumentSoundType type; }; } #endif /* SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTSOUNDINFO_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/InstrumentType.h000066400000000000000000000027161377264061000230450ustar00rootroot00000000000000/* * InstrumentTypes.h * * Created on: 7 Feb 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTTYPE_H_ #define SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTTYPE_H_ #include #include #include namespace DrumKit { enum class InstrumentType { TestDrum, Pad, //Drum, //Cymbal, HiHat, //BassDrum, First = TestDrum, Last = HiHat }; inline std::ostream& operator<<(std::ostream& o, const InstrumentType& x) { std::string os; switch (x) { case InstrumentType::TestDrum: os = "DualZonePad"; break; case InstrumentType::Pad: os = "Pad"; break; //case InstrumentType::Drum: os = "Drum"; break; //case InstrumentType::Cymbal: os = "Cymbal"; break; case InstrumentType::HiHat: os = "HiHat"; break; //case InstrumentType::BassDrum: os = "BassDrum"; break; default: break; } return o << os; } inline InstrumentType operator++(InstrumentType& x) { return x = static_cast(std::underlying_type_t(x) + 1); }; inline InstrumentType operator*(InstrumentType i) { return i; }; inline InstrumentType begin(InstrumentType x) { return InstrumentType::First; }; inline InstrumentType end(InstrumentType x) { InstrumentType l = InstrumentType::Last; return ++l; }; inline std::istream& operator>>(std::istream& is, InstrumentType& x) { return Util::StreamToEnum(is, x); } } #endif /* SOURCE_DRUMKIT_INSTRUMENTS_INSTRUMENTTYPE_H_ */ libeXaDrums-0.6.0/DrumKit/Instruments/Pads/000077500000000000000000000000001377264061000205435ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Instruments/Pads/Pad.cpp000066400000000000000000000035041377264061000217550ustar00rootroot00000000000000/* * Pad.cpp * * Created on: 11 Jun 2017 * Author: jeremy */ #include "Pad.h" using namespace Sound; #include namespace DrumKit { Pad::Pad(InstrumentParameters parameters, std::shared_ptr soundBank): Instrument(parameters, soundBank), soundId(0) { return; } void Pad::SetTriggers(std::vector const& triggers) { for(const TriggerPtr& triggerPtr : triggers) { auto triggerIdAndLocation = std::find_if(parameters.triggersIdsAndLocations.cbegin(), parameters.triggersIdsAndLocations.cend(), [triggerPtr](std::pair const& idAndLocation) { return (idAndLocation.first == triggerPtr->GetId()); }); if(triggerIdAndLocation != std::end(parameters.triggersIdsAndLocations)) { TriggerLocation triggerLocation = triggerIdAndLocation->second; switch (triggerLocation) { case TriggerLocation::DrumHead: this->trigger = triggerPtr; break; default: break; } } } return; } void Pad::SetSound(InstrumentSoundInfo const& soundInfo) { InstrumentSoundType soundType = soundInfo.type; std::string soundLocation = soundInfo.soundLocation; switch (soundType) { case InstrumentSoundType::Default: soundId = soundBank->LoadSound(soundLocation, parameters.volume); break; default: throw -1; break; } return; } void Pad::SetVolume(float volume) { soundBank->SetSoundVolume(soundId, volume); parameters.volume = volume; return; } void Pad::GetSoundProps(int& id, float& volume) const { TriggerState triggerState = trigger->GetTriggerState(); if(triggerState.isTrig) { id = soundId; volume = triggerState.value; } return; } bool Pad::IsTriggerEvent() const { TriggerState triggerState = trigger->GetTriggerState(); return triggerState.isTrig; } } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/Instruments/Pads/Pad.h000066400000000000000000000024361377264061000214250ustar00rootroot00000000000000/* * Pad.h * * Created on: 11 Jun 2017 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_INSTRUMENTS_PADS_PAD_H_ #define SOURCE_DRUMKIT_INSTRUMENTS_PADS_PAD_H_ #include "../../../Sound/SoundBank/SoundBank.h" #include "../../../Sound/InstrumentSoundType.h" #include "../../Triggers/Triggers/Trigger.h" #include "../Instrument.h" #include #include namespace DrumKit { class Pad : public Instrument { public: Pad(InstrumentParameters parameters, std::shared_ptr sb); ~Pad() = default; virtual void SetTriggers(std::vector const& triggers) final; virtual void SetSound(InstrumentSoundInfo const& soundInfo) final; virtual void SetVolume(float volume) final; virtual void GetSoundProps(int& id, float& volume) const final; virtual bool IsTriggerEvent() const final; virtual std::vector GetSoundTypes() const final { return {Sound::InstrumentSoundType::Default}; } virtual std::vector GetTriggersLocations() const final { return {TriggerLocation::DrumHead}; }; virtual std::vector GetTriggersIds() const final { return { trigger->GetId() }; }; private: TriggerPtr trigger; int soundId; }; } /* namespace DrumKit */ #endif /* SOURCE_DRUMKIT_INSTRUMENTS_PADS_PAD_H_ */ libeXaDrums-0.6.0/DrumKit/Kits/000077500000000000000000000000001377264061000162335ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Kits/Kit.cpp000066400000000000000000000060411377264061000174670ustar00rootroot00000000000000/* * Kit.cpp * * Created on: 21 Aug 2016 * Author: jeremy */ #include "Kit.h" #include "../../Util/ErrorHandling.h" #include "../Triggers/TriggerType.h" #include "../Instruments/InstrumentFactory.h" #include using namespace Util; namespace DrumKit { Kit::Kit(const KitParameters& params, std::vector const& trigs, std::shared_ptr sb) : parameters(params), triggers(trigs), soundBank(std::move(sb)) { return; } Kit::~Kit() { return; } void Kit::Enable() { CreateInstruments(); return; } void Kit::Disable() { std::for_each(instruments.begin(), instruments.end(), [](InstrumentPtr& inst) { inst.reset(); }); instruments.clear(); return; } void Kit::SetInstrumentVolume(size_t id, float volume) { if(id >= this->instruments.size()) { throw Exception("Could not set instrument volume: instrument does not exist.", error_type_warning); } this->instruments[id]->SetVolume(volume); this->parameters.instrumentParameters[id].volume = volume; return; } std::string Kit::GetInstrumentName(std::size_t id) const { if(id > parameters.instrumentParameters.size()) { throw Exception("The instrument could not be found.", error_type_error); } std::string name = parameters.instrumentParameters[id].instrumentName; return name; } /// PRIVATE METHODS void Kit::CreateInstruments() { for(InstrumentParameters const& instrumentParameters : this->parameters.instrumentParameters) { std::vector triggersIds(triggers.size()); std::transform(triggers.begin(), triggers.end(), triggersIds.begin(), [](const auto& t){ return t->GetId(); }); // Make sure that all triggers exist for(const auto& trigIdLoc : instrumentParameters.triggersIdsAndLocations) { const auto& id = trigIdLoc.first; const auto trigIdIt = std::find(triggersIds.begin(), triggersIds.end(), id); if(trigIdIt == triggersIds.end()) { throw Exception("Could not find trigger " + std::to_string(id) + " (needed by " + instrumentParameters.instrumentName + ").", error_type_error); } } // Create instrument InstrumentPtr instrumentPtr = InstrumentFactory::CreateInstrument(instrumentParameters.instrumentType, instrumentParameters, soundBank); // Create instrument's triggers instrumentPtr->SetTriggers(this->triggers); // Set instrument sounds for(InstrumentSoundInfo const& soundInfo : instrumentParameters.soundsInfo) { instrumentPtr->SetSound(soundInfo); } // Add instrument to drum module instruments.push_back(instrumentPtr); } return; } } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/Kits/Kit.h000066400000000000000000000027011377264061000171330ustar00rootroot00000000000000/* * Kit.h * * Created on: 21 Aug 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_KITS_KIT_H_ #define SOURCE_DRUMKIT_KITS_KIT_H_ #include "KitParameters.h" #include "KitManager.h" #include "../../Sound/SoundBank/SoundBank.h" #include "../Instruments/Instrument.h" #include "../Triggers/Triggers/Trigger.h" #include #include namespace DrumKit { class Kit { public: Kit(const KitParameters& params, std::vector const& trigs, std::shared_ptr sb); virtual ~Kit(); void Enable(); void Disable(); void Save() const { KitManager::SaveKit(parameters.configFilePath, parameters);} void SetInstrumentVolume(size_t id, float volume); std::string GetConfigFilePath() const noexcept { return parameters.configFilePath; } float GetInstrumentVolume(int id) const { return instruments[id]->GetVolume(); } std::string GetInstrumentName(std::size_t id) const; std::string GetName() const noexcept { return parameters.kitName; } int GetNumInstruments() const { return (int)parameters.instrumentParameters.size(); } const std::vector& GetInstruments() const { return instruments; } private: void CreateInstruments(); KitParameters parameters; const std::vector& triggers; std::shared_ptr soundBank; std::vector instruments; }; } /* namespace DrumKit */ #endif /* SOURCE_DRUMKIT_KITS_KIT_H_ */ libeXaDrums-0.6.0/DrumKit/Kits/KitCreator.cpp000066400000000000000000000172311377264061000210120ustar00rootroot00000000000000/* * KitCreator.cpp * * Created on: 9 Nov 2016 * Author: jeremy */ #include "KitCreator.h" #include "../../Sound/SoundBank/SoundBank.h" #include "../Instruments/InstrumentFactory.h" #include using namespace Sound; using namespace Util; namespace DrumKit { KitCreator::KitCreator(const std::string& dataLoc) noexcept : dataFolder{dataLoc}, kitsDirectory(dataLoc + "Kits/"), instrument() //, soundFiles(SoundBank::GetSoundFiles(dataLoc)) { return; } KitCreator::~KitCreator() { return; } void KitCreator::CreateNewKit() noexcept { this->parameters = KitParameters(); this->instrument = InstrumentParameters(); return; } void KitCreator::CreateFromModel(const std::string& file) { KitManager::LoadKit(this->kitsDirectory + file, this->parameters); this->instrument = InstrumentParameters(); return; } void KitCreator::SaveKit(const std::string& file, bool fullPath) const { if(fullPath) { KitManager::SaveKit(file, this->parameters); } else { KitManager::SaveKit(this->kitsDirectory + file, this->parameters); } return; } void KitCreator::SaveKit() const { std::string configFilePath = this->parameters.configFilePath; if(configFilePath.empty()) { std::string file = this->parameters.kitName + ".xml"; SaveKit(file); } else { SaveKit(configFilePath, true); } return; } void KitCreator::AddInstrumentToKit() noexcept { this->parameters.instrumentParameters.push_back(this->instrument); this->instrument = InstrumentParameters(); return; } void KitCreator::CreateNewInstrument() noexcept { this->instrument = InstrumentParameters(); this->instrument.id = (int) this->parameters.instrumentParameters.size(); return; } void KitCreator::RemoveInstrument(std::size_t i) noexcept { if(parameters.instrumentParameters.size() > 1) { parameters.instrumentParameters.erase(parameters.instrumentParameters.begin() + i); } return; } void KitCreator::RemoveLastInstrument() noexcept { if(parameters.instrumentParameters.size() > 1) { parameters.instrumentParameters.erase(parameters.instrumentParameters.end() - 1); } return; } void KitCreator::SetInstrumentType(const std::string& typeStr) { InstrumentType type = Enums::ToElement(typeStr); this->instrument.instrumentType = type; return; } void KitCreator::AddInstrumentSound(const std::string& file, const std::string& typeStr) { InstrumentSoundType soundType = Enums::ToElement(typeStr); InstrumentSoundInfo instrumentSoundInfo; { instrumentSoundInfo.id = (int) this->instrument.soundsInfo.size(); instrumentSoundInfo.soundLocation = file; instrumentSoundInfo.type = soundType; } this->instrument.soundsInfo.push_back(instrumentSoundInfo); return; } void KitCreator::AddInstrumentTrigger(const int id, const std::string& location) { TriggerLocation trigLoc = Enums::ToElement(location); this->instrument.triggersIdsAndLocations.push_back(std::pair{id, trigLoc}); return; } void KitCreator::SetInstrumentTriggersIdsAndLocs(int id, const std::vector>& trigsIdsAndLocs) { InstrumentParameters& instrument = parameters.instrumentParameters[id]; instrument.triggersIdsAndLocations.clear(); instrument.triggersIdsAndLocations.resize(trigsIdsAndLocs.size()); for(std::size_t i = 0; i < trigsIdsAndLocs.size(); i++) { instrument.triggersIdsAndLocations[i].first = trigsIdsAndLocs[i].first; TriggerLocation trigLoc = Enums::ToElement(trigsIdsAndLocs[i].second); instrument.triggersIdsAndLocations[i].second = trigLoc; } return; } void KitCreator::SetInstrumentSoundsTypesAndLocs(int id, const std::vector>& sndsTypesAndLocs) { InstrumentParameters& instrument = parameters.instrumentParameters[id]; instrument.soundsInfo.clear(); instrument.soundsInfo.resize(sndsTypesAndLocs.size()); for(std::size_t i = 0; i < sndsTypesAndLocs.size(); i++) { instrument.soundsInfo[i].type = Enums::ToElement(sndsTypesAndLocs[i].first); instrument.soundsInfo[i].soundLocation = sndsTypesAndLocs[i].second; } return; } std::string KitCreator::GetInstrumentType(int i) const { InstrumentType type = parameters.instrumentParameters[i].instrumentType; std::string typeStr = Enums::ToString(type); return typeStr; } std::vector KitCreator::GetInstrumentsTriggersIds(int i) const { const auto& triggers = parameters.instrumentParameters[i].triggersIdsAndLocations; std::vector triggersIds(triggers.size()); std::transform(triggers.cbegin(), triggers.cend(), triggersIds.begin(), [](const std::pair& t){ return t.first; }); return triggersIds; } std::vector KitCreator::GetInstrumentTriggersLocations(int i) const { const auto& trigs = parameters.instrumentParameters[i].triggersIdsAndLocations; std::vector triggersLocations(trigs.size()); std::transform(trigs.cbegin(), trigs.cend(), triggersLocations.begin(), [](const std::pair& t){ return Enums::ToString(t.second); }); return triggersLocations; } std::vector KitCreator::GetInstrumentSoundsTypes(int i) const { const auto& sounds = parameters.instrumentParameters[i].soundsInfo; std::vector soundsTypes(sounds.size()); std::transform(sounds.cbegin(), sounds.cend(), soundsTypes.begin(), [](const InstrumentSoundInfo& s) { return Enums::ToString(s.type); }); return soundsTypes; } std::vector KitCreator::GetInstrumentSoundsLocs(int i) const { const auto& sounds = parameters.instrumentParameters[i].soundsInfo; std::vector soundsLocs(sounds.size()); std::transform(sounds.cbegin(), sounds.cend(), soundsLocs.begin(), [](const InstrumentSoundInfo& s) { return s.soundLocation; }); return soundsLocs; } std::vector KitCreator::GetSoundTypes(const std::string& instrumentType) const { InstrumentType type = Enums::ToElement(instrumentType); std::vector sndTypes = InstrumentFactory::GetSoundsTypes(type); std::vector soundsTypes; std::transform(sndTypes.cbegin(), sndTypes.cend(), std::back_inserter(soundsTypes), [](const InstrumentSoundType& t) { return Enums::ToString(t); }); return soundsTypes; } std::vector KitCreator::GetTriggersLocations(const std::string& instrumentType) const { InstrumentType type = Enums::ToElement(instrumentType); std::vector trigsLocs = InstrumentFactory::GetTriggersTypes(type); std::vector triggersLocations; std::transform(trigsLocs.cbegin(), trigsLocs.cend(), std::back_inserter(triggersLocations), [](const TriggerLocation& l) { return Enums::ToString(l); }); return triggersLocations; } std::vector KitCreator::GetInstrumentsTypes() const { std::vector types = Enums::GetEnumVector(); std::vector instrumentTypes; std::transform(types.cbegin(), types.cend(), std::back_inserter(instrumentTypes), [](const InstrumentType& t) { return Enums::ToString(t); }); return instrumentTypes; } std::vector KitCreator::GetInstrumentsNames() const { const std::vector& instruments = this->parameters.instrumentParameters; std::vector instrumentsNames; std::transform(instruments.cbegin(), instruments.cend(), std::back_inserter(instrumentsNames),[](const InstrumentParameters& i) { return i.instrumentName; }); return instrumentsNames; } } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/Kits/KitCreator.h000066400000000000000000000055051377264061000204600ustar00rootroot00000000000000/* * KitCreator.h * * Created on: 9 Nov 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_KITS_KITCREATOR_H_ #define SOURCE_DRUMKIT_KITS_KITCREATOR_H_ #include "../../Util/Enums.h" #include "../Triggers/TriggerManager.h" #include "KitParameters.h" #include "KitManager.h" #include #include namespace DrumKit { class KitCreator { public: explicit KitCreator(const std::string& dataLoc) noexcept; virtual ~KitCreator(); // Kit void CreateNewKit() noexcept; void CreateFromModel(const std::string& file); int GetNumInstruments() const noexcept { return (int)this->parameters.instrumentParameters.size(); } void SetKitName(const std::string& name) noexcept { parameters.kitName = name; }; void SaveKit(const std::string& file, bool fullPath = false) const; void SaveKit() const; // Instruments void CreateNewInstrument() noexcept; void RemoveInstrument(std::size_t i) noexcept; void RemoveLastInstrument() noexcept; void AddInstrumentToKit() noexcept; void SetInstrumentName(const std::string& name) { this->instrument.instrumentName = name; } void SetInstrumentType(const std::string& type); void SetInstrumentVolume(const float v) noexcept { this->instrument.volume = v; } void AddInstrumentSound(const std::string& file, const std::string& type); void AddInstrumentTrigger(const int id, const std::string& location); void SetInstrumentName(int id, const std::string& name) { this->parameters.instrumentParameters[id].instrumentName = name; } void SetInstrumentType(int id, const std::string& type) { this->parameters.instrumentParameters[id].instrumentType = Util::Enums::ToElement(type); } void SetInstrumentTriggersIdsAndLocs(int id, const std::vector>& trigsIdsAndLocs); void SetInstrumentSoundsTypesAndLocs(int id, const std::vector>& sndsTypesAndLocs); std::string GetInstrumentType(int i) const; std::vector GetInstrumentsTriggersIds(int i) const; std::vector GetInstrumentTriggersLocations(int i) const; std::vector GetInstrumentSoundsTypes(int i) const; std::vector GetInstrumentSoundsLocs(int i) const; // Enums // Instruments std::vector GetInstrumentsTypes() const; std::vector GetInstrumentsNames() const; // Triggers std::vector GetTriggersIds() const { return TriggerManager::LoadTriggersIds(dataFolder); } std::vector GetTriggersLocations(const std::string& instrumentType) const; // Sounds std::vector GetSoundTypes(const std::string& instrumentType) const; private: std::string dataFolder; std::string kitsDirectory; KitParameters parameters; InstrumentParameters instrument; }; } /* namespace DrumKit */ #endif /* SOURCE_DRUMKIT_KITS_KITCREATOR_H_ */ libeXaDrums-0.6.0/DrumKit/Kits/KitManager.cpp000066400000000000000000000146041377264061000207660ustar00rootroot00000000000000/* * KitsManager.cpp * * Created on: 8 Nov 2015 * Author: jeremy */ #include "KitManager.h" #include "../../Util/ErrorHandling.h" #include "../../Util/Enums.h" #include "../../Util/Xml.h" #include "../Instruments/InstrumentType.h" #include "../Instruments/InstrumentSoundInfo.h" #include "../Triggers/TriggerLocation.h" #include #include #include #include #include #include #include #if __has_include() #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif using namespace Sound; using namespace tinyxml2; using namespace Util; namespace DrumKit { KitManager::KitManager(const std::string& kitsPath) : kitsPath(kitsPath) { this->ScanFolder(); return; } KitManager::~KitManager() { return; } void KitManager::LoadKit(std::string file, KitParameters& parameters) { parameters.instrumentParameters.clear(); XMLDocument doc; if(doc.LoadFile(file.c_str()) != XML_SUCCESS) { throw Exception("Could not read the drum kit configuration file.", error_type_error); return; } XMLElement* root = doc.RootElement(); XMLElement* kitName = root->FirstChildElement("kitName"); parameters.kitName = kitName->GetText(); //parameters.kitFolder = kitFolder->GetText(); int instrumentId = 0; for(const auto& instrument : XmlElement{root, "Instrument"}) { InstrumentParameters instrumentParameters; instrumentParameters.instrumentName = instrument.FirstChildElement("instrumentName").GetText(); for(const auto& trigger : XmlElement{instrument.FirstChildElement("triggers")}) { int id = std::stoi(trigger.GetText()); TriggerLocation location = trigger.Attribute("location"); instrumentParameters.triggersIdsAndLocations.emplace_back(id, location); } int soundId = 0; for(const auto& sound : XmlElement{instrument.FirstChildElement("sounds")}) { InstrumentSoundInfo soundInfo; soundInfo.id = soundId; auto soundText = sound.GetText(); if(soundText != nullptr) { soundInfo.soundLocation = soundText; } else { throw Exception("Sound location is empty.", error_type_error); return; } sound.Attribute("type", soundInfo.type); instrumentParameters.soundsInfo.push_back(soundInfo); soundId++; } // Populate instrumentParameters instrument.Attribute("type", instrumentParameters.instrumentType); instrumentParameters.id = instrumentId; // Instrument volume std::string volumeStr = instrument.Attribute("volume"); instrumentParameters.volume = float(std::stoi(volumeStr)/100.0f); parameters.instrumentParameters.push_back(instrumentParameters); instrumentId++; } parameters.configFilePath = file; return; } void KitManager::SaveKit(std::string file, KitParameters parameters) { // Create document XMLDocument doc; // Add root element XMLElement* root = doc.NewElement("root"); doc.InsertFirstChild(root); // Create Elements XMLElement* kitName = doc.NewElement("kitName"); XMLElement* kitFolder = doc.NewElement("kitFolder"); // Add values and attributes to elements kitName->SetText(parameters.kitName.c_str()); //kitFolder->SetText(parameters.kitFolder.c_str()); // Add elements to document root->InsertEndChild(kitName); root->InsertEndChild(kitFolder); // Instruments for(InstrumentParameters const& instrumentParameters : parameters.instrumentParameters) { XMLElement* instrument = doc.NewElement("Instrument"); // Set type std::string instrumentType = Enums::ToString(instrumentParameters.instrumentType); instrument->SetAttribute("type", instrumentType.c_str()); // Set volume int volume = (int)std::floor(instrumentParameters.volume * 100.0f); instrument->SetAttribute("volume", volume); // Add Instrument name XMLElement* instrumentName = doc.NewElement("instrumentName"); instrumentName->SetText(instrumentParameters.instrumentName.c_str()); instrument->InsertEndChild(instrumentName); // Triggers XMLElement* triggers = doc.NewElement("triggers"); for(auto const& triggerLoc : instrumentParameters.triggersIdsAndLocations) { XMLElement* trigger = doc.NewElement("trigger"); // Set location std::string location = Enums::ToString(triggerLoc.second); trigger->SetAttribute("location", location.c_str()); // Set id trigger->SetText(triggerLoc.first); triggers->InsertEndChild(trigger); } instrument->InsertEndChild(triggers); // Sounds XMLElement* sounds = doc.NewElement("sounds"); for(InstrumentSoundInfo const& soundInfo : instrumentParameters.soundsInfo) { XMLElement* sound = doc.NewElement("sound"); // Set type std::string type = Enums::ToString(soundInfo.type); sound->SetAttribute("type", type.c_str()); // Set file path sound->SetText(soundInfo.soundLocation.c_str()); // Add sound sounds->InsertEndChild(sound); } instrument->InsertEndChild(sounds); // Insert in root root->InsertEndChild(instrument); } // Save file auto result = doc.SaveFile(file.c_str()); if(XML_SUCCESS != result) { throw Exception("Could not save drum kit parameters.", error_type_error); } return; } bool KitManager::DeleteKit(const int& id) { const std::string& filePath = filesPaths[id]; std::ifstream file(filePath.c_str()); if(file.good()) { unlink(filePath.c_str()); this->ScanFolder(); return true; } return false; } // PRIVATE METHODS void KitManager::ScanFolder() { this->filesPaths.clear(); for(const auto& p: fs::recursive_directory_iterator(kitsPath)) { if(p.path().extension() == ".xml") { this->filesPaths.push_back(p.path().string()); } } // Sort (had to be improved to take capital letters into account) std::sort(filesPaths.begin(), filesPaths.end(), [](const std::string& lhs, const std::string& rhs) { const auto result = std::mismatch(lhs.cbegin(), lhs.cend(), rhs.cbegin(), [](const char& lhs, const char& rhs){return std::tolower(lhs) == std::tolower(rhs);}); return result.second != rhs.cend() && (result.first == lhs.cend() || std::tolower(*result.first) < std::tolower(*result.second)); }); //std::sort(this->filesPaths.begin(), this->filesPaths.end()); return; } // PRIVATE } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/Kits/KitManager.h000066400000000000000000000020261377264061000204260ustar00rootroot00000000000000/* * KitsManager.h * * Created on: 8 Nov 2015 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_KITS_KITMANAGER_H_ #define SOURCE_DRUMKIT_KITS_KITMANAGER_H_ #include "KitParameters.h" #include "../../Sound/InstrumentSoundType.h" #include #include namespace DrumKit { class KitManager { public: explicit KitManager(const std::string& kitsPath); virtual ~KitManager(); static void LoadKit(std::string file, KitParameters& parameters); static void SaveKit(std::string file, KitParameters parameters); bool DeleteKit(const int& id); void ReScan() { ScanFolder(); } int GetNumKits() { return int(this->filesPaths.size()); } std::vector GetKitsLocations() const { return filesPaths; } private: static Sound::InstrumentSoundType GetSoundType(std::string type); static std::string GetSoundTypeStr(Sound::InstrumentSoundType soundType); void ScanFolder(); std::string kitsPath; std::vector filesPaths; }; } #endif /* SOURCE_DRUMKIT_KITS_KITMANAGER_H_ */ libeXaDrums-0.6.0/DrumKit/Kits/KitParameters.h000066400000000000000000000010671377264061000211630ustar00rootroot00000000000000/* * KitParameters.h * * Created on: 7 Feb 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_KITMANAGER_KITPARAMS_H_ #define SOURCE_DRUMKIT_KITMANAGER_KITPARAMS_H_ #include "../Instruments/InstrumentParameters.h" #include #include namespace DrumKit { struct KitParameters { KitParameters() noexcept : kitName(), configFilePath(), instrumentParameters() {} std::string kitName; std::string configFilePath; std::vector instrumentParameters; }; } #endif /* SOURCE_DRUMKIT_KITMANAGER_KITPARAMS_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/000077500000000000000000000000001377264061000171075ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Triggers/Curves/000077500000000000000000000000001377264061000203565ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Triggers/Curves/CurveType.h000066400000000000000000000027471377264061000224670ustar00rootroot00000000000000/* * Curves.h * * Created on: 2 Jun 2015 * Author: jeremy */ #ifndef LIBEXADRUMS_SOURCE_DRUMKIT_CURVES_CURVE_H_ #define LIBEXADRUMS_SOURCE_DRUMKIT_CURVES_CURVE_H_ #include "../../../Util/Enums.h" #include #include #include namespace DrumKit { enum class CurveType { Linear, Exponential1, Exponential2, Log1, Log2, Loud1, Loud2, Spline, First = Linear, Last = Spline }; inline std::ostream& operator<<(std::ostream& o, const CurveType& x) { std::string os; switch (x) { case CurveType::Exponential1: os = "Exponential1";break; case CurveType::Linear: os = "Linear"; break; case CurveType::Exponential2: os = "Exponential2";break; case CurveType::Log1: os = "Log1"; break; case CurveType::Log2: os = "Log2"; break; case CurveType::Loud1: os = "Loud1"; break; case CurveType::Loud2: os = "Loud2"; break; case CurveType::Spline: os = "Spline"; break; default: break; } return o << os; } inline CurveType operator++(CurveType& x) { return x = (CurveType)(std::underlying_type_t(x) + 1); }; inline CurveType operator*(CurveType x) { return x; }; inline CurveType begin(CurveType x) { return CurveType::First; }; inline CurveType end(CurveType x) { CurveType l = CurveType::Last; return ++l; }; inline std::istream& operator>>(std::istream& is, CurveType& x) { return Util::StreamToEnum(is, x); } } #endif /* LIBEXADRUMS_SOURCE_DRUMKIT_CURVES_CURVE_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/Curves/Curves.h000066400000000000000000000103341377264061000217770ustar00rootroot00000000000000/* * Curves.h * * Created on: 2 Jun 2015 * Author: jeremy */ #ifndef LIBEXADRUMS_SOURCE_DRUMKIT_CURVES_CURVES_H_ #define LIBEXADRUMS_SOURCE_DRUMKIT_CURVES_CURVES_H_ #include "../../../Util/Misc.h" #include "../../../Util/Enums.h" #include "CurveType.h" #include #include #include #include #include #include namespace DrumKit { template using curve_t = std::vector; class Curves { public: template static curve_t MakeCurve(CurveType t, size_t length) { curve_t curve; switch(t) { case CurveType::Linear: Linear(curve, length); break; case CurveType::Exponential1: Exp(curve, length); break; case CurveType::Exponential2: Exp2(curve, length); break; case CurveType::Log1: Log(curve, length); break; case CurveType::Log2: Log2(curve, length); break; case CurveType::Loud1: Loud1(curve, length); break; case CurveType::Loud2: Loud2(curve, length); break; case CurveType::Spline: Sigmoid(curve, length); break; default: Linear(curve, length); break; } return curve; } template static T Apply(const curve_t& curve, U val) { const auto index = std::min(val, curve.size()-1); return curve[index]; } template static void Linear(curve_t& curve, std::size_t length) { // Create a vector that contains values increasing from 0 to numSamples-1 std::vector linearVector(length); std::iota(linearVector.begin(), linearVector.end(), 0); // Normalise vector Normalize(linearVector); // Copy normalised vector into curve curve.swap(linearVector); return; } template static void GenerateNormalizedCurve(curve_t& curve, std::size_t length, F&& func) { // Get normalised linear vector std::vector normLin; Linear(normLin, length); // Create vector to contain the non-normalised cruve std::vector curveVector(normLin.size()); // Create non-normalised exponential vector std::transform(normLin.begin(), normLin.end(), curveVector.begin(), func); // Create normalised exponential vector Normalize(curveVector); // Copy generated vector into the curve vector curve.swap(curveVector); } template static void Exp(curve_t& curve, std::size_t length) { GenerateNormalizedCurve(curve, length, [](auto x) { return std::expm1(x); }); return; } template static void Exp2(curve_t& curve, std::size_t length) { GenerateNormalizedCurve(curve, length, [](auto x) { return std::expm1(x) * std::expm1(x); }); return; } template static void Log(curve_t& curve, std::size_t length) { GenerateNormalizedCurve(curve, length, [](auto x) { return std::log2(1 + x); }); return; } template static void Log2(curve_t& curve, std::size_t length) { GenerateNormalizedCurve(curve, length, [](auto x) { return std::log2(1 + x) * std::log2(1 + x); }); return; } template static void Loud1(curve_t& curve, std::size_t length) { GenerateNormalizedCurve(curve, length, [](auto x) { return .25 + .75 * x; }); return; } template static void Loud2(curve_t& curve, std::size_t length) { GenerateNormalizedCurve(curve, length, [](auto x) { return .75 + .25 * x; }); return; } template static void Sigmoid(curve_t& curve, std::size_t length) { GenerateNormalizedCurve(curve, length, [](auto x) { return 1. / ( 1. + std::exp(-12. * (x - .5))); }); return; } private: Curves(){}; virtual ~Curves(){}; template static void Normalize(std::vector& curve) { // Create empty vector to contain the normalised curve std::vector normalisedCurve(curve.size()); // Get maximum value of the vector auto max = *std::max_element(curve.begin(), curve.end()); // Normalise linear vector std::transform(curve.begin(), curve.end(), normalisedCurve.begin(), [&max](auto v) { return v/max; }); // Copy normalised curve to the curve vector curve.swap(normalisedCurve); return; } }; } #endif /* LIBEXADRUMS_SOURCE_DRUMKIT_CURVES_CURVES_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/TriggerFactory.h000066400000000000000000000016641377264061000222220ustar00rootroot00000000000000/* * TriggerFactory.h * * Created on: 18 Jun 2017 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERFACTORY_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERFACTORY_H_ #include "../../Util/ErrorHandling.h" #include "Triggers/DiscreteTrigger.h" #include "Triggers/ContinuousTrigger.h" namespace DrumKit { class TriggerFactory { public: static TriggerPtr CreateTrigger(const TriggerParameters& triggerParameters) { switch (triggerParameters.type) { case TriggerType::Discrete: return std::make_shared(triggerParameters); case TriggerType::Continuous: return std::make_shared(triggerParameters); default: Util::Exception("Unknown trigger type.", Util::error_type_error); return TriggerPtr(nullptr); } } private: TriggerFactory() = delete; ~TriggerFactory() = delete; }; } // namespace DrumKit #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERFACTORY_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/TriggerLocation.h000066400000000000000000000023041377264061000223530ustar00rootroot00000000000000/* * TriggerLocation.h * * Created on: 8 Mar 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERLOCATION_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERLOCATION_H_ #include #include #include namespace DrumKit { enum class TriggerLocation { DrumHead, Rim, First = DrumHead, Last = Rim }; inline std::ostream& operator<<(std::ostream& o, const TriggerLocation& x) { std::string os; switch (x) { case TriggerLocation::DrumHead: os = "DrumHead"; break; case TriggerLocation::Rim: os = "Rim"; break; default: break; } return o << os; } inline TriggerLocation operator++(TriggerLocation& x) { return x = static_cast(std::underlying_type_t(x) + 1); }; inline TriggerLocation operator*(TriggerLocation i) { return i; }; inline TriggerLocation begin(TriggerLocation x) { return TriggerLocation::First; }; inline TriggerLocation end(TriggerLocation x) { TriggerLocation l = TriggerLocation::Last; return ++l; }; inline std::istream& operator>>(std::istream& is, TriggerLocation& x) { return Util::StreamToEnum(is, x); } } #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERLOCATION_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/TriggerManager.cpp000066400000000000000000000132501377264061000225120ustar00rootroot00000000000000/* * TriggerManager.cpp * * Created on: 3 Sep 2016 * Author: jeremy */ #include "TriggerManager.h" #include "../../Util/Enums.h" #include "../../Util/ErrorHandling.h" #include "../../Util/Xml.h" #include "Curves/CurveType.h" #include "TriggerLocation.h" #include #include #include using namespace tinyxml2; using namespace Util; namespace DrumKit { std::vector TriggerManager::LoadTriggersIds(const std::string& moduleDir) { // Set default sensors parameters IO::SensorsConfig sensorsConfig; sensorsConfig.resolution = 12; sensorsConfig.samplingRate = 1000000; sensorsConfig.sensorType = IO::SensorType::Hdd; sensorsConfig.hddDataFolder = ""; // Load triggers config with default sensor parameters std::vector trigsParams; LoadTriggersConfig(moduleDir, sensorsConfig, trigsParams); std::vector triggersIds(trigsParams.size()); std::transform(trigsParams.cbegin(), trigsParams.cend(), triggersIds.begin(), [&](const TriggerParameters& p) { return p.sensorId; }); return triggersIds; } void TriggerManager::LoadTriggersConfig(const std::string& moduleDir, const IO::SensorsConfig& sensorsConfig, std::vector& trigsParams) { trigsParams.clear(); const std::string file(moduleDir + "triggersConfig.xml"); XMLDocument doc; if(doc.LoadFile(file.data()) != XML_SUCCESS) { throw Exception("Could not load triggers configuration.", error_type_error); } XMLElement* root = doc.RootElement(); // Read all triggers for(const auto& trigger : XmlElement{root, "Trigger"}) { TriggerParameters trigParams; // Get trigger id trigger.Attribute("sensorId", trigParams.sensorId); // Get trigger type trigParams.type = trigger.Attribute("sensorType"); // Read xml elements auto gainDefined = trigger.FirstChildElement("Gain").GetValue(trigParams.gain); if(!gainDefined) { trigParams.gain = 1.; } trigger.FirstChildElement("Threshold").GetValue(trigParams.threshold); trigger.FirstChildElement("ScanTime").GetValue(trigParams.scanTime); trigger.FirstChildElement("MaskTime").GetValue(trigParams.maskTime); auto response = trigger.FirstChildElement("Response"); trigParams.response = response.GetValue(); // Sensors configuation trigParams.sensorConfig = sensorsConfig; trigsParams.push_back(trigParams); } return; } void TriggerManager::SaveTriggersConfig(const std::string& moduleDir, const std::vector& triggerParameters) { std::vector trigsParams = triggerParameters; std::sort(trigsParams.begin(), trigsParams.end(), [](const auto& t1, const auto& t2) { return t1.sensorId < t2.sensorId; }); std::string file(moduleDir + "triggersConfig.xml"); // Create document XMLDocument doc; // Add root element auto root = doc.NewElement("Triggers"); doc.InsertFirstChild(root); // Add triggers configurations for(const auto& trigger : trigsParams) { std::string sensorTypeStr = Enums::ToString(trigger.type); auto trig = CreateXmlElement(doc, "Trigger", "", {{"sensorType", sensorTypeStr}, {"sensorId", trigger.sensorId}}); trig->InsertEndChild(CreateXmlElement(doc, "Threshold", trigger.threshold)); trig->InsertEndChild(CreateXmlElement(doc, "Gain", trigger.gain)); trig->InsertEndChild(CreateXmlElement(doc, "ScanTime", trigger.scanTime)); trig->InsertEndChild(CreateXmlElement(doc, "MaskTime", trigger.maskTime)); trig->InsertEndChild(CreateXmlElement(doc, "Response", Enums::ToString(trigger.response))); root->InsertEndChild(trig); } // Save file auto result = doc.SaveFile(file.c_str()); if(XML_SUCCESS != result) { throw Exception("Could not save triggers configuration.", error_type_error); } return; } void TriggerManager::LoadSensorsConfig(const std::string& moduleDir, IO::SensorsConfig& sensorConfig) { std::string file(moduleDir + "sensorsConfig.xml"); XMLDocument doc; auto ret = doc.LoadFile(file.c_str()); if(ret != XML_SUCCESS) { throw Exception("Could not load sensors configuration.", error_type_error); } auto root = XmlElement{doc.RootElement()}; root.FirstChildElement("SamplingRate").GetValue(sensorConfig.samplingRate); root.FirstChildElement("Resolution").GetValue(sensorConfig.resolution); root.FirstChildElement("HddDataFolder").GetValue(sensorConfig.hddDataFolder); auto type = root.FirstChildElement("Type").GetValue(); sensorConfig.sensorType = Enums::ToElement(type); return; } void TriggerManager::SaveSensorsConfig(const std::string& moduleDir, IO::SensorsConfig& sensorConfig) { std::string file(moduleDir + "sensorsConfig.xml"); XMLDocument doc; // Add root element XMLElement* root = doc.NewElement("SensorsConfig"); doc.InsertFirstChild(root); // Create Elements XMLElement* samplingRate = doc.NewElement("SamplingRate"); XMLElement* resolution = doc.NewElement("Resolution"); XMLElement* type = doc.NewElement("Type"); XMLElement* dataFolder = doc.NewElement("HddDataFolder"); samplingRate->SetText(sensorConfig.samplingRate); resolution->SetText(sensorConfig.resolution); std::string typeStr = Enums::ToString(sensorConfig.sensorType); type->SetText(typeStr.c_str()); dataFolder->SetText(sensorConfig.hddDataFolder.c_str()); // Add elements to document root->InsertEndChild(samplingRate); root->InsertEndChild(resolution); root->InsertEndChild(type); root->InsertEndChild(dataFolder); auto result = doc.SaveFile(file.c_str()); if(XML_SUCCESS != result) { throw Exception("Could not save sensors configuration.", error_type_error); } return; } } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/Triggers/TriggerManager.h000066400000000000000000000020651377264061000221610ustar00rootroot00000000000000/* * TriggerManager.h * * Created on: 3 Sep 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERMANAGER_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERMANAGER_H_ #include "../../IO/SensorType.h" #include "../../IO/SensorsConfig.h" #include "TriggerParameters.h" #include #include namespace DrumKit { class TriggerManager { public: static void LoadTriggersConfig(const std::string& moduleDir, const IO::SensorsConfig& sensorsConfig, std::vector& trigsParams); static void SaveTriggersConfig(const std::string& moduleDir, const std::vector& trigsParams); static std::vector LoadTriggersIds(const std::string& moduleDir); static void LoadSensorsConfig(const std::string& moduleDir, IO::SensorsConfig& sensorConfig); static void SaveSensorsConfig(const std::string& moduleDir, IO::SensorsConfig& sensorConfig); private: TriggerManager() = delete; virtual ~TriggerManager() = delete; }; } /* namespace DrumKit */ #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERMANAGER_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/TriggerParameters.h000066400000000000000000000010711377264061000227060ustar00rootroot00000000000000/* * TriggerParameters.h * * Created on: 8 Nov 2015 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERPARAMETERS_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERPARAMETERS_H_ #include "../../IO/SensorsConfig.h" #include "Curves/CurveType.h" #include "TriggerType.h" namespace DrumKit { struct TriggerParameters { int sensorId; int scanTime; double threshold; int maskTime; double gain = 1.; TriggerType type; CurveType response; IO::SensorsConfig sensorConfig; }; } #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERPARAMETERS_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/TriggerState.h000066400000000000000000000005421377264061000216650ustar00rootroot00000000000000/* * TriggerState.h * * Created on: 6 Mar 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERSTATE_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERSTATE_H_ namespace DrumKit { struct TriggerState { int sensorId; float value; bool isTrig; int64_t trigTime; }; } #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERSTATE_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/TriggerType.h000066400000000000000000000022051377264061000215240ustar00rootroot00000000000000/* * TriggerType.h * * Created on: 6 Mar 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERTYPE_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERTYPE_H_ #include #include #include namespace DrumKit { enum class TriggerType { Discrete, Continuous, First = Discrete, Last = Continuous }; inline std::ostream& operator<<(std::ostream& o, const TriggerType& x) { std::string os; switch (x) { case TriggerType::Discrete: os = "Discrete"; break; case TriggerType::Continuous: os = "Continuous"; break; default: break; } return o << os; } inline TriggerType operator++(TriggerType& x) { return x = static_cast(std::underlying_type_t(x) + 1); }; inline TriggerType operator*(TriggerType x) { return x; }; inline TriggerType begin(TriggerType x) { return TriggerType::First; }; inline TriggerType end(TriggerType x) { TriggerType l = TriggerType::Last; return ++l; }; inline std::istream& operator>>(std::istream& is, TriggerType& x) { return Util::StreamToEnum(is, x); } } #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERTYPE_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/Triggers/000077500000000000000000000000001377264061000206755ustar00rootroot00000000000000libeXaDrums-0.6.0/DrumKit/Triggers/Triggers/ContinuousTrigger.cpp000066400000000000000000000011461377264061000250750ustar00rootroot00000000000000/* * ContinuousTrigger.cpp * * Created on: 28 Oct 2016 * Author: jeremy */ #include "ContinuousTrigger.h" namespace DrumKit { ContinuousTrigger::ContinuousTrigger(const TriggerParameters& triggerParams) : Trigger(triggerParams) { return; } void ContinuousTrigger::Refresh() { // Save previous value //float previousValue = state.value; // Reset state value state.value = 0.0f; state.isTrig = false; // Read sensor date short value = this->GetSensorData(); state.value = value / numSamples / 2.0f; lastTrigValue = state.value; return; } } /* namespace DrumKit */ libeXaDrums-0.6.0/DrumKit/Triggers/Triggers/ContinuousTrigger.h000066400000000000000000000011201377264061000245320ustar00rootroot00000000000000/* * ContinuousTrigger.h * * Created on: 28 Oct 2016 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERS_CONTINUOUSTRIGGER_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERS_CONTINUOUSTRIGGER_H_ #include "Trigger.h" #include "../TriggerParameters.h" namespace DrumKit { class ContinuousTrigger : public Trigger { public: explicit ContinuousTrigger(const TriggerParameters& triggerParams); ~ContinuousTrigger() = default; virtual void Refresh() final; private: }; } /* namespace DrumKit */ #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERS_CONTINUOUSTRIGGER_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/Triggers/DiscreteTrigger.cpp000066400000000000000000000033061377264061000244710ustar00rootroot00000000000000/* * Conditioner.cpp * * Created on: 4 May 2015 * Author: jeremy */ #include "DiscreteTrigger.h" #include #include using namespace std::chrono; namespace DrumKit { DiscreteTrigger::DiscreteTrigger(const TriggerParameters& triggerParams) : Trigger(triggerParams) { return; } void DiscreteTrigger::Refresh() { // Reset state value state.value = 0.0f; state.isTrig = false; const auto parameters = this->GetParameters(); // Read sensor date double value = parameters.gain * this->GetSensorData(); // Remove DC offset (high pass filter: y[n] = x[n] - x[n-1] + R * y[n-1]) filteredValue = value - prevValue + 0.99 * prevFilteredValue; // Update values prevValue = value; prevFilteredValue = filteredValue; velocity = std::abs(filteredValue); // Get current time high_resolution_clock::time_point t = high_resolution_clock::now(); int64_t dt = static_cast(duration(t - t0).count()); if(velocity > parameters.threshold) { if(!trig) { trigTime = dt; trig = true; } if(maxVelocity < velocity && dt < trigTime + parameters.scanTime) { maxVelocity = velocity; } if(sensor->IsDigital() || (dt > trigTime + parameters.scanTime && !out)) { out = true; // Update trigger state state.value = Curves::Apply(this->curves[static_cast(parameters.response)], maxVelocity); state.trigTime = static_cast(time_point_cast(t).time_since_epoch().count()); state.isTrig = true; lastTrigValue = state.value; } } if(trig && dt > trigTime + parameters.maskTime) { trig = false; maxVelocity = 0; out = false; } return; } } libeXaDrums-0.6.0/DrumKit/Triggers/Triggers/DiscreteTrigger.h000066400000000000000000000012011377264061000241260ustar00rootroot00000000000000/* * Conditioner.h * * Created on: 4 May 2015 * Author: jeremy */ #ifndef RASPIDRUMS_SOURCE_DRUMKIT_TRIGGER_H_ #define RASPIDRUMS_SOURCE_DRUMKIT_TRIGGER_H_ #include "Trigger.h" #include "../TriggerParameters.h" namespace DrumKit { class DiscreteTrigger : public Trigger { public: explicit DiscreteTrigger(const TriggerParameters& triggerParams); ~DiscreteTrigger() = default; virtual void Refresh() final; //virtual bool Trig(short value, float& strength); //bool GetState() const { return trig; } private: //mutable std::mutex triggerMutex; }; } #endif /* RASPIDRUMS_SOURCE_DRUMKIT_TRIGGER_H_ */ libeXaDrums-0.6.0/DrumKit/Triggers/Triggers/Trigger.cpp000066400000000000000000000040501377264061000230030ustar00rootroot00000000000000/* * Trigger.cpp * * Created on: 13 Dec 2015 * Author: jeremy */ #include "Trigger.h" #include "../../../IO/HddSensor.h" #include "../../../IO/SpiSensor.h" #include "../../../IO/VirtualSensor.h" #include #include #include #include using namespace std::chrono; using namespace Util; namespace DrumKit { Trigger::Trigger(TriggerParameters triggerParams) : trig(false), out(false), trigTime(0), velocity(0), maxVelocity(0), state(), triggerParameters(triggerParams) { t0 = high_resolution_clock::now(); numSamples = static_cast(std::pow(2.0f, triggerParams.sensorConfig.resolution) / 2.0f); //mean = numSamples; prevValue = 0; filteredValue = 0; prevFilteredValue = 0; // Default state values state.isTrig = false; state.sensorId = triggerParams.sensorId; state.value = 0.0f; const std::string dataFolder = triggerParams.sensorConfig.hddDataFolder; switch(triggerParams.sensorConfig.sensorType) { case IO::SensorType::Hdd: this->sensor = std::make_unique(dataFolder); break; case IO::SensorType::Spi: this->sensor = std::make_unique(); break; case IO::SensorType::Virtual: this->sensor = std::make_unique(); break; default: throw -1; break; } // Generate curves const auto curves_types = Enums::GetEnumVector(); curves.resize(curves_types.size()); std::transform(curves_types.cbegin(), curves_types.cend(), curves.begin(), [&](const auto& t) { return Curves::MakeCurve(t, numSamples);}); return; } short Trigger::GetSensorData() const { return sensor->GetData(triggerParameters.sensorId); } void Trigger::SetParameters(const TriggerParameters& params) { std::lock_guard lock(spin); const auto sensorConfig = triggerParameters.sensorConfig; triggerParameters = params; triggerParameters.sensorConfig = sensorConfig; } TriggerParameters Trigger::GetParameters() const { std::lock_guard lock(spin); return triggerParameters; } } libeXaDrums-0.6.0/DrumKit/Triggers/Triggers/Trigger.h000066400000000000000000000033321377264061000224520ustar00rootroot00000000000000/* * ITrigger.h * * Created on: 9 Dec 2015 * Author: jeremy */ #ifndef SOURCE_DRUMKIT_TRIGGERS_TRIGGERS_TRIGGER_H_ #define SOURCE_DRUMKIT_TRIGGERS_TRIGGERS_TRIGGER_H_ #include "../Curves/Curves.h" #include "../../../IO/ISensor.h" #include "../../../Util/Threading.h" #include "../TriggerParameters.h" #include "../TriggerState.h" #include #include namespace DrumKit { class Trigger { public: explicit Trigger(TriggerParameters trigParams); //virtual bool Trig(short value, float& strength) = 0; virtual void Refresh() = 0; virtual void SetParameters(const TriggerParameters& params); virtual void SetSensorData(char channel, short data) { this->sensor->SetData(channel, data); } virtual int GetId() const { return this->triggerParameters.sensorId; }; virtual TriggerType GetType() const { return this->triggerParameters.type; } virtual TriggerState const& GetTriggerState() const { return state; } virtual float GetLastTrigValue() const { return lastTrigValue; } virtual TriggerParameters GetParameters() const; protected: virtual ~Trigger() = default; virtual short GetSensorData() const; std::chrono::high_resolution_clock::time_point t0; size_t numSamples; float lastTrigValue = 0.f; double prevValue; double filteredValue{}; double prevFilteredValue; bool trig; bool out; bool reset = false; int64_t trigTime; double velocity; double maxVelocity; TriggerState state; std::vector> curves; std::unique_ptr sensor; private: mutable Util::SpinLock spin; TriggerParameters triggerParameters; }; typedef std::shared_ptr TriggerPtr; } #endif /* SOURCE_DRUMKIT_TRIGGERS_TRIGGERS_TRIGGER_H_ */ libeXaDrums-0.6.0/IO/000077500000000000000000000000001377264061000142515ustar00rootroot00000000000000libeXaDrums-0.6.0/IO/HddSensor.cpp000066400000000000000000000021761377264061000166540ustar00rootroot00000000000000/* * Sensor.cpp * * Created on: 7 May 2015 * Author: jeremy */ #include "HddSensor.h" #include #include #include namespace IO { const std::vector HddSensor::dataFiles({"out.raw", "out.raw", "out.raw"}); HddSensor::HddSensor(const std::string& dataFolder) : path(dataFolder), index(0) { return; } HddSensor::~HddSensor() { return; } short HddSensor::GetData(char channel) { if(data.empty()) { ReadData(channel); } // Wait for a few microseconds { clock_t endwait = clock() + (5.0f * (double)CLOCKS_PER_SEC) / 1000000.0f ; while (clock() < endwait); } short val = data[++index]; if(index == data.size()) { index = 0; } return val; } // Private Methods void HddSensor::ReadData(char channel) { std::string fileName = dataFiles[channel % dataFiles.size()]; std::string fileLoc(path + fileName); std::ifstream file(fileLoc, std::ifstream::binary); if(!file.good()) { throw - 1; } short val; while(file.read((char*)&val, sizeof(short))) { data.push_back(val); } file.close(); return; } } libeXaDrums-0.6.0/IO/HddSensor.h000066400000000000000000000012541377264061000163150ustar00rootroot00000000000000/* * Sensor.h * * Created on: 7 May 2015 * Author: jeremy */ #ifndef RASPIDRUMS_SOURCE_IO_HDDSENSOR_H_ #define RASPIDRUMS_SOURCE_IO_HDDSENSOR_H_ #include "ISensor.h" #include #include namespace IO { class HddSensor : public ISensor { public: explicit HddSensor(const std::string& filePath); virtual ~HddSensor(); virtual short GetData(char channel) final; virtual void SetData(char channel, short value) final {} private: static const std::vector dataFiles; void ReadData(char channel); std::string path; std::vector data; unsigned int index; }; } #endif /* RASPIDRUMS_SOURCE_IO_HDDSENSOR_H_ */ libeXaDrums-0.6.0/IO/ISensor.h000066400000000000000000000007601377264061000160070ustar00rootroot00000000000000/* * Sensor.h * * Created on: 25 Oct 2015 * Author: jeremy */ #ifndef LIBEXADRUMS_SOURCE_IO_ISENSOR_H_ #define LIBEXADRUMS_SOURCE_IO_ISENSOR_H_ namespace IO { class ISensor { public: virtual short GetData(char channel) = 0; virtual void SetData(char channel, short value) = 0; virtual bool IsDigital() const { return is_digital; } virtual ~ISensor() {}; protected: bool is_digital{false}; }; } /* namespace IO */ #endif /* LIBEXADRUMS_SOURCE_IO_ISENSOR_H_ */ libeXaDrums-0.6.0/IO/SensorType.h000066400000000000000000000021231377264061000165330ustar00rootroot00000000000000/* * SensorType.h * * Created on: 28 Oct 2015 * Author: jeremy */ #ifndef LIBEXADRUMS_SOURCE_IO_SENSORTYPE_H_ #define LIBEXADRUMS_SOURCE_IO_SENSORTYPE_H_ #include #include #include namespace IO { enum class SensorType { Virtual, Spi, UsbSerial, Hdd, First = Virtual, Last = Hdd }; inline std::ostream& operator<<(std::ostream& o, const SensorType& x) { std::string os; switch (x) { case SensorType::Virtual: os = "Virtual"; break; case SensorType::Spi: os = "Spi"; break; case SensorType::UsbSerial: os = "UsbSerial"; break; case SensorType::Hdd: os = "Hdd"; break; default: break; } return o << os; } inline SensorType operator++(SensorType& x) { return x = static_cast(std::underlying_type_t(x) + 1); }; inline SensorType operator*(SensorType x) { return x; }; inline SensorType begin(SensorType x) { return SensorType::First; }; inline SensorType end(SensorType x) { SensorType l = SensorType::Last; return ++l; }; } #endif /* LIBEXADRUMS_SOURCE_IO_SENSORTYPE_H_ */ libeXaDrums-0.6.0/IO/SensorsConfig.h000066400000000000000000000006001377264061000172000ustar00rootroot00000000000000/* * SensorsConfig.h * * Created on: 21 Feb 2017 * Author: jeremy */ #ifndef SOURCE_IO_SENSORSCONFIG_H_ #define SOURCE_IO_SENSORSCONFIG_H_ #include "SensorType.h" #include namespace IO { struct SensorsConfig { int samplingRate; int resolution; SensorType sensorType; std::string hddDataFolder; }; } #endif /* SOURCE_IO_SENSORSCONFIG_H_ */ libeXaDrums-0.6.0/IO/Serial.cpp000066400000000000000000000046421377264061000162020ustar00rootroot00000000000000/* * Serial.cpp * * Created on: 13 Sep 2015 * Author: jeremy */ #include "Serial.h" #include // Standard input / output functions #include #include // String function definitions #include // UNIX standard function definitions #include // File control definitions #include // Error number definitions #include // POSIX terminal control definitions namespace IO { Serial::Serial() : isOpen(false), baudRate(230400), port("/dev/ttyUSB0"), handle(), buf() { this->Open(); return; } Serial::~Serial() { if(isOpen) this->Close(); return; } short Serial::GetData(char port) { if(::write(this->handle, &port, 1) == 1) { auto res = ::read(this->handle, &this->buf, 1); if(res != -1) { if(this->buf == 's') { auto resValue = ::read(this->handle, &this->buf, 1); if(resValue < 0) { return 0; } } } } else return 0; return short(this->buf); } // PRIVATE void Serial::Open() { this->handle = ::open(this->port.c_str(), O_RDWR| O_NOCTTY); this->isOpen = true; this->Configure(); return; } void Serial::Close() { if(this->handle) ::close(this->handle); this->isOpen = false; return; } void Serial::Configure() { if(!isOpen) return; struct termios tty; struct termios tty_old; memset(&tty, 0, sizeof tty); /* Error Handling */ if(tcgetattr(this->handle, &tty) != 0 ) { //std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl; } /* Save old tty parameters */ tty_old = tty; /* Set Baud Rate */ cfsetospeed (&tty, (speed_t)B230400); cfsetispeed (&tty, (speed_t)B230400); /* Setting other Port Stuff */ tty.c_cflag &= ~PARENB; // Make 8n1 tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~CRTSCTS; // no flow control tty.c_cc[VMIN] = 1; // read doesn't block tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines /* Make raw */ cfmakeraw(&tty); /* Flush Port, then applies attributes */ tcflush(this->handle, TCIFLUSH); if(tcsetattr (this->handle, TCSANOW, &tty) != 0) { //std::cout << "Error " << errno << " from tcsetattr" << std::endl; } return; } } libeXaDrums-0.6.0/IO/Serial.h000066400000000000000000000012611377264061000156410ustar00rootroot00000000000000/* * Serial.h * * Created on: 13 Sep 2015 * Author: jeremy */ #ifndef LIBEXADRUMS_SOURCE_IO_SERIAL_H_ #define LIBEXADRUMS_SOURCE_IO_SERIAL_H_ #include "ISensor.h" #include namespace IO { class Serial : public ISensor { public: Serial(); virtual ~Serial(); short GetData(char port); virtual void SetData(char channel, short value) final {} private: void Configure(); void Open(); void Close(); bool isOpen; //const unsigned int defaultBaudRate = 230400; //const std::string defaultPort = "/dev/ttyUSB0"; unsigned int baudRate; std::string port; int handle; char buf; }; } #endif /* LIBEXADRUMS_SOURCE_IO_SERIAL_H_ */ libeXaDrums-0.6.0/IO/Spi.cpp000066400000000000000000000022071377264061000155110ustar00rootroot00000000000000/* * Spi.cpp * * Created on: 2 May 2017 * Author: jeremy */ #include "Spi.h" #include #include #include namespace IO { const std::string Spi::spiDev0 = "/dev/spidev0.0"; const uint8_t Spi::bitsPerWord = 8; const uint16_t Spi::delay = 0; void Spi::Open(int freq, int mode) noexcept { this->clkFreq = freq; this->fd = open(Spi::spiDev0.c_str(), O_RDWR); mode &= 3; ioctl(this->fd, SPI_IOC_WR_MODE, &mode); ioctl(this->fd, SPI_IOC_WR_BITS_PER_WORD, &Spi::bitsPerWord); ioctl(this->fd, SPI_IOC_WR_MAX_SPEED_HZ, &this->clkFreq); return; } void Spi::Close() noexcept { if(fd != -1) { close(this->fd); fd = -1; } return; } int Spi::dataRW(unsigned char* data, int len) { struct spi_ioc_transfer spiData{}; spiData.tx_buf = (unsigned long)data; spiData.rx_buf = (unsigned long)data; spiData.len = len ; spiData.speed_hz = this->clkFreq; spiData.delay_usecs = 0; spiData.bits_per_word = Spi::bitsPerWord; spiData.cs_change = 0; spiData.pad = 0; return ioctl(this->fd, SPI_IOC_MESSAGE(1), &spiData); } } /* namespace IO */ libeXaDrums-0.6.0/IO/Spi.h000066400000000000000000000013421377264061000151550ustar00rootroot00000000000000/* * Spi.h * * Created on: 2 May 2017 * Author: jeremy */ #ifndef SOURCE_IO_SPI_H_ #define SOURCE_IO_SPI_H_ #include #include namespace IO { class Spi { public: static Spi& get() { static Spi instance; return instance; } void Open(int speed, int mode) noexcept; void Close() noexcept; int dataRW(unsigned char* data, int len); private: Spi(Spi const&) = delete; void operator=(Spi const&) = delete; Spi() : clkFreq(1000000), fd(-1) {} static const std::string spiDev0; static const uint8_t bitsPerWord; static const uint16_t delay; unsigned int clkFreq; int fd; }; } /* namespace IO */ #endif /* SOURCE_IO_SPI_H_ */ libeXaDrums-0.6.0/IO/SpiSensor.cpp000066400000000000000000000030561377264061000167060ustar00rootroot00000000000000/* * Accelerometer.cpp * * Created on: 25 May 2015 * Author: jeremy */ #include "SpiSensor.h" #include "Spi.h" //#include namespace IO { // Initilize number of instances //std::size_t SpiSensor::numInstances = 0; SpiSensor::SpiSensor() { /*if(numInstances == 0) { if (!bcm2835_init()) { //std::cout << "Could not initialise bcm2835" << std::endl; return ; } // Bcm2835 configuration { bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // The default bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_128); // bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // The default bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default } } numInstances++; */ return; } SpiSensor::~SpiSensor() { /*numInstances--; if(numInstances == 0) { // End Spi transfer bcm2835_spi_end(); // Close bcm2835 library bcm2835_close(); }*/ return; } short SpiSensor::GetData(char channel) { // Select SPI channel unsigned char data = 0b11000000 | (channel << 3); unsigned char mosi[3] = {data}; //char miso[3] = {0}; // Receive data //bcm2835_spi_transfernb(mosi, miso, 3); Spi::get().dataRW(mosi, 3); // Calculate value from received bits //short value = ((miso[0] & 0x01) << 11) | (miso[1] << 3) | ((miso[2] >> 5) & 0x07); short value = ((mosi[0] & 0x01) << 11) | (mosi[1] << 3) | ((mosi[2] >> 5) & 0x07); return value; } } libeXaDrums-0.6.0/IO/SpiSensor.h000066400000000000000000000007661377264061000163600ustar00rootroot00000000000000/* * Accelerometer.h * * Created on: 25 May 2015 * Author: jeremy */ #ifndef RASPIDRUMS_SOURCE_IO_SENSOR_H_ #define RASPIDRUMS_SOURCE_IO_SENSOR_H_ #include "ISensor.h" #include namespace IO { class SpiSensor : public ISensor { public: SpiSensor(); virtual ~SpiSensor(); short GetData(char channel); virtual void SetData(char channel, short value) final {} private: //static std::size_t numInstances; }; } #endif /* RASPIDRUMS_SOURCE_IO_SENSOR_H_ */ libeXaDrums-0.6.0/IO/VirtualSensor.h000066400000000000000000000011531377264061000172420ustar00rootroot00000000000000/* * ExternalSensor.h * * Created on: 6 Jan 2019 * Author: jeremy */ #ifndef IO_VIRTUALSENSOR_H_ #define IO_VIRTUALSENSOR_H_ #include "ISensor.h" #include namespace IO { class VirtualSensor : public ISensor { public: VirtualSensor() { is_digital = true; } virtual ~VirtualSensor() {} virtual short GetData(char channel) final { return data.exchange(0, std::memory_order_acquire); } virtual void SetData(char channel, short value) final { data.store(value, std::memory_order_release); } private: std::atomic data{}; }; } #endif /* IO_VIRTUALSENSOR_H_ */ libeXaDrums-0.6.0/LICENSE000066400000000000000000000027541377264061000147570ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2015-2020, Jeremy Oden All rights reserved. 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 notice, this list of conditions and the following disclaimer. * 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. * Neither the name of the copyright holder 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 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 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, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libeXaDrums-0.6.0/Makefile.am000066400000000000000000000144741377264061000160100ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in # Copyright (C) 2018-2021 Jérémy Oden # Copyright (C) 2018-2021 Nicolas Boulenguez # Unless otherwise specified, the numbers refer to # https://www.gnu.org/software/automake/manual/automake.html ###################################################################### # https://www.gnu.org/software/gettext/manual/gettext.html # 13.4.8 aclocal.m4 at top level # Cache shared with autoconf. ACLOCAL_AMFLAGS = -I m4 ###################################################################### # 8 building programs and libraries # Delegate library handling to libtool. lib_LTLIBRARIES = libexadrums.la # libtool 7.3 updating library version information # Downstream packagers may have to override the SO version. exadrums_libtool_version_info = 1:0:0 libexadrums_la_CXXFLAGS = \ -Wall \ -pthread \ -std=c++17 $(alsa_CFLAGS) $(tinyxml2_CFLAGS) $(minizip_CFLAGS) libexadrums_la_LDFLAGS = \ -Wl,--as-needed \ -Wl,--no-allow-shlib-undefined \ -Wl,--no-copy-dt-needed-entries \ -Wl,--no-undefined \ -pthread \ -version-info $(exadrums_libtool_version_info) # The -pthread compiler and linker option links with libpthread, but # also selects various architecture-dependent settings. libexadrums_la_LIBADD = $(alsa_LIBS) $(tinyxml2_LIBS) $(minizip_LIBS) -lpthread -lstdc++fs -latomic # The standard C++ library does not include libatomic on some # architectures (armel mips mipsel). It does not hurt to always list # it here, it will only be effective when used thanks to --as-needed. # Distribute and embed into the library. libexadrums_la_SOURCES = \ Api/Config/Config_api.cpp \ Api/Config/TriggerParameters_api.cpp \ Api/KitCreator/KitCreator_api.cpp \ Api/eXaDrums.cpp \ DrumKit/DrumModule/Module.cpp \ DrumKit/DrumModule/Module.h \ DrumKit/DrumModule/Recorder.cpp \ DrumKit/DrumModule/Recorder.h \ DrumKit/DrumModule/TrigSound.h \ DrumKit/Instruments/Cymbals/TestHiHat.cpp \ DrumKit/Instruments/Cymbals/TestHiHat.h \ DrumKit/Instruments/Drums/TestDrum.cpp \ DrumKit/Instruments/Drums/TestDrum.h \ DrumKit/Instruments/Instrument.cpp \ DrumKit/Instruments/Instrument.h \ DrumKit/Instruments/InstrumentFactory.h \ DrumKit/Instruments/InstrumentParameters.h \ DrumKit/Instruments/InstrumentSoundInfo.h \ DrumKit/Instruments/InstrumentType.h \ DrumKit/Instruments/Pads/Pad.cpp \ DrumKit/Instruments/Pads/Pad.h \ DrumKit/Kits/Kit.cpp \ DrumKit/Kits/Kit.h \ DrumKit/Kits/KitCreator.cpp \ DrumKit/Kits/KitManager.cpp \ DrumKit/Triggers/Curves/Curves.h \ DrumKit/Triggers/TriggerFactory.h \ DrumKit/Triggers/TriggerManager.cpp \ DrumKit/Triggers/TriggerState.h \ DrumKit/Triggers/Triggers/ContinuousTrigger.cpp \ DrumKit/Triggers/Triggers/ContinuousTrigger.h \ DrumKit/Triggers/Triggers/DiscreteTrigger.cpp \ DrumKit/Triggers/Triggers/DiscreteTrigger.h \ DrumKit/Triggers/Triggers/Trigger.cpp \ DrumKit/Triggers/Triggers/Trigger.h \ IO/HddSensor.cpp \ IO/HddSensor.h \ IO/ISensor.h \ IO/Serial.cpp \ IO/Serial.h \ IO/Spi.cpp \ IO/Spi.h \ IO/SpiSensor.cpp \ IO/SpiSensor.h \ IO/VirtualSensor.h \ Metronome/ClickTypes.h \ Metronome/Metronome.cpp \ Metronome/Metronome.h \ Metronome/MetronomeParameters.h \ Sound/Alsa/Alsa.cpp \ Sound/Alsa/Alsa.h \ Sound/Alsa/AlsaParameters.cpp \ Sound/Alsa/AlsaParameters.h \ Sound/Mixer/Mixer.cpp \ Sound/Mixer/Mixer.h \ Sound/Sound.cpp \ Sound/Sound.h \ Sound/SoundBank/SoundBank.cpp \ Sound/SoundBank/SoundBank.h \ Sound/SoundProcessor/SoundProcessor.cpp \ Sound/SoundProcessor/SoundProcessor.h \ Sound/SoundState.h \ Sound/Util/WavUtil.h \ Util/Crypt.h \ Util/ErrorHandling.h \ Util/Misc.h \ Util/Parsing.h \ Util/SimpleSafeQueue.h \ Util/Threading.h \ Util/Time.h \ Util/Zip.h # Install into pkgincludedir, but do not distribute (it is generated # by configure.ac). nobase_nodist_pkginclude_HEADERS = \ Api/Version.h # Distribute and install into pkgincludedir. nobase_pkginclude_HEADERS = \ Api/Config/AlsaParams_api.h \ Api/Config/Config_api.h \ Api/Config/Config_api.hpp \ Api/Config/TriggerParameters_api.h \ Api/KitCreator/KitCreator_api.h \ Api/KitCreator/KitCreator_api.hpp \ Api/eXaDrums.h \ Api/eXaDrums.hpp \ DrumKit/Instruments/InstrumentParameters.h \ DrumKit/Instruments/InstrumentSoundInfo.h \ DrumKit/Instruments/InstrumentType.h \ DrumKit/Kits/KitCreator.h \ DrumKit/Kits/KitManager.h \ DrumKit/Kits/KitParameters.h \ DrumKit/Triggers/Curves/CurveType.h \ DrumKit/Triggers/TriggerLocation.h \ DrumKit/Triggers/TriggerManager.h \ DrumKit/Triggers/TriggerParameters.h \ DrumKit/Triggers/TriggerType.h \ IO/SensorType.h \ IO/SensorsConfig.h \ Sound/Alsa/AlsaParams.h \ Sound/InstrumentSoundType.h \ Util/Enums.h \ Util/ErrorHandling.h \ Util/Xml.h \ Util/Zip.h ###################################################################### # 12 what gets installed # Distribute and install into docdir. dist_doc_DATA = README.md # Install in an architecture-dependent directory, do not distribute. pkgexecconfigdir = $(libdir)/pkgconfig pkgexecconfig_DATA = exadrums.pc # autoconf 4.8.2 Installation Directory Variables # Substituting from here instead of from configure.ac ensures that the # user can override prefix on the Make command line. edit = sed \ -e 's|@PACKAGE_NAME[@]|$(PACKAGE_NAME)|g' \ -e 's|@PACKAGE_URL[@]|$(PACKAGE_URL)|g' \ -e 's|@PACKAGE_VERSION[@]|$(PACKAGE_VERSION)|g' \ -e 's|@includedir[@]|$(includedir)|g' \ -e 's|@libdir[@]|$(libdir)|g' exadrums.pc: Makefile rm -f $@ $@.tmp srcdir=''; \ test -f ./$@.in || srcdir=$(srcdir)/; \ $(edit) $${srcdir}$@.in >$@.tmp mv $@.tmp $@ exadrums.pc: $(srcdir)/exadrums.pc.in ###################################################################### # 13 what gets cleaned # Remove files created by Makefile. CLEANFILES = \ exadrums.pc # Should silent `rm .gitignore && git status`. MAINTAINERCLEANFILES = \ Makefile.in \ aclocal.m4 \ ar-lib \ compile \ config.guess \ config.h.in \ config.sub \ configure \ depcomp \ install-sh \ ltmain.sh \ m4/* \ missing ###################################################################### # 14 what goes in a distribution # Distribute (in the tarball created by 'make dist-gzip'). EXTRA_DIST = \ LICENSE \ Roadmap.md \ bootstrap \ exadrums.pc.in libeXaDrums-0.6.0/Metronome/000077500000000000000000000000001377264061000157075ustar00rootroot00000000000000libeXaDrums-0.6.0/Metronome/ClickTypes.h000066400000000000000000000016751377264061000201430ustar00rootroot00000000000000/* * clickTypes.h * * Created on: 28 Sep 2016 * Author: jeremy */ #ifndef SOURCE_METRONOME_CLICKTYPES_H_ #define SOURCE_METRONOME_CLICKTYPES_H_ #include #include #include namespace DrumKit { enum class ClickType { Sine, Square, First = Sine, Last = Square }; inline std::ostream& operator<<(std::ostream& o, const ClickType& x) { std::string os; switch (x) { case ClickType::Sine: os = "Sine"; break; case ClickType::Square: os = "Square"; break; default: break; } return o << os; } inline ClickType operator++(ClickType& x) { return x = static_cast(std::underlying_type_t(x) + 1); }; inline ClickType operator*(ClickType c) { return c; }; inline ClickType begin(ClickType x) { return ClickType::First; }; inline ClickType end(ClickType x) { ClickType l = ClickType::Last; return ++l; }; } #endif /* SOURCE_METRONOME_CLICKTYPES_H_ */ libeXaDrums-0.6.0/Metronome/Metronome.cpp000066400000000000000000000142351377264061000203650ustar00rootroot00000000000000/* * Metronome.cpp * * Created on: 28 Aug 2016 * Author: jeremy */ #include "Metronome.h" #include "../Util/Enums.h" #include "../Util/ErrorHandling.h" #include #include #include #define _USE_MATH_DEFINES using namespace Sound; using namespace tinyxml2; using namespace Util; namespace DrumKit { Metronome::Metronome(AlsaParams alsaParams) noexcept : Metronome(alsaParams, MetronomeParameters()) { return; } Metronome::Metronome(AlsaParams alsaParams, MetronomeParameters params) noexcept : alsaParameters(alsaParams), parameters(params) { bpmeasList = std::vector{1, 2, 3, 4, 5, 6, 7, 8}; rhythmList = std::vector{1, 2, 4}; return; } Metronome::~Metronome() { return; } void Metronome::GenerateClick() noexcept { switch (this->parameters.clickType) { case ClickType::Sine: GenerateSine(); break; case ClickType::Square: GenerateSquare(); break; default: GenerateSine(); break; } return; } void Metronome::SetTempo(int t) { parameters.tempo = std::min(std::max(t, 40), 250); return; } void Metronome::LoadConfig(const std::string& filePath, MetronomeParameters& params) { XMLDocument doc; if(doc.LoadFile(filePath.c_str()) != XML_SUCCESS) { throw Exception("Could not load metronome configuration.", error_type_error); } // Get elements XMLElement* root = doc.RootElement(); XMLElement* tempo = root->FirstChildElement("Tempo"); XMLElement* rhythm = root->FirstChildElement("Rhythm"); XMLElement* beatsPerMeasure = root->FirstChildElement("BeatsPerMeasure"); XMLElement* clickType = root->FirstChildElement("ClickType"); // Get values params.tempo = std::atoi(tempo->GetText()); params.rhythm = std::atoi(rhythm->GetText()); params.beatsPerMeasure = std::atoi(beatsPerMeasure->GetText()); params.clickType = Enums::ToElement(clickType->GetText()); return; } void Metronome::SaveConfig(const std::string& filePath, const MetronomeParameters& params) { // Create document XMLDocument doc; // Add root element XMLElement* root = doc.NewElement("Metronome"); doc.InsertFirstChild(root); // Create elements XMLElement* tempo = doc.NewElement("Tempo"); XMLElement* rhythm = doc.NewElement("Rhythm"); XMLElement* beatsPerMeasure = doc.NewElement("BeatsPerMeasure"); XMLElement* clickType = doc.NewElement("ClickType"); // Add values and elements to document tempo->SetText(params.tempo); rhythm->SetText(params.rhythm); beatsPerMeasure->SetText(params.beatsPerMeasure); clickType->SetText(Enums::ToString(params.clickType).c_str()); // Add elements to document root->InsertEndChild(tempo); root->InsertEndChild(rhythm); root->InsertEndChild(beatsPerMeasure); root->InsertEndChild(clickType); // Save file auto err = doc.SaveFile(filePath.c_str()); if(err != XML_SUCCESS) { throw Exception("Could not save metronome configuration.", error_type_error); } return; } // Private Methods int Metronome::GetNumSamples() const { // Calculate number of samples to generate the measure float beatsPerSecond = parameters.rhythm * float(parameters.tempo) / 60.0f; float measureTime = parameters.beatsPerMeasure / beatsPerSecond; int numSamples = alsaParameters.nChannels * alsaParameters.sampleRate * measureTime; return numSamples; } int Metronome::GetBeatsRate() const { // Get number of samples between each click float beatsPerSecond = parameters.rhythm * float(parameters.tempo) / 60.0f; float beatsFreq = alsaParameters.nChannels/beatsPerSecond; int beatsRate = std::floor(alsaParameters.sampleRate*beatsFreq); return beatsRate; } void Metronome::GenerateSquare() { const int numSamples = GetNumSamples(); data.clear(); data.resize(numSamples); // Get number of samples between each click const int beatsRate = GetBeatsRate(); // Sinusoid parameters float fSineHz = 880.0f; float radiansPerSample = fSineHz * 2*M_PI / alsaParameters.sampleRate / alsaParameters.nChannels; float clickDuration = alsaParameters.nChannels * 10.0f/1000.0f; std::size_t clickSamples = std::floor(alsaParameters.sampleRate*clickDuration); int n = 0; int mul = 1; double phase = 0; short amplitude = std::numeric_limits::max(); for(std::size_t i = 0; i < data.size(); i++) { std::size_t click = n * beatsRate;// + correction; if(i > click && i < click + clickSamples) { if(n % parameters.beatsPerMeasure == 0) { mul = 2; } else { mul = 1; } phase += mul*radiansPerSample; data[i] = (short)(float(amplitude) * (std::sin(phase) > 0)); } else // No click, so signal is zero { data[i] = 0; phase = 0; if(i == click + clickSamples) { n++; } } } return; } void Metronome::GenerateSine() { const int numSamples = GetNumSamples(); data.clear(); data.resize(numSamples); // Get number of samples between each click const int beatsRate = GetBeatsRate(); // Sinusoid parameters float fSineHz = 880.0f; float radiansPerSample = fSineHz * 2*M_PI / alsaParameters.sampleRate / alsaParameters.nChannels; float clickDuration = alsaParameters.nChannels * 10.0f/1000.0f; std::size_t clickSamples = std::floor(alsaParameters.sampleRate*clickDuration); int n = 0; int mul = 1; double phase = 0; short amplitude = std::numeric_limits::max(); for(std::size_t i = 0; i < data.size(); i++) { std::size_t click = n * beatsRate;// + correction; if(i > click && i < click + clickSamples) { if(n % parameters.beatsPerMeasure == 0) { mul = 2; } else { mul = 1; } phase += mul*radiansPerSample; data[i] = (short)(float(amplitude) * std::sin(phase)); } else // No click, so signal is zero { data[i] = 0; phase = 0; if(i == click + clickSamples) { n++; } } } return; } } /* namespace DrumKit */ libeXaDrums-0.6.0/Metronome/Metronome.h000066400000000000000000000036611377264061000200330ustar00rootroot00000000000000/* * Metronome.h * * Created on: 28 Aug 2016 * Author: jeremy */ #ifndef SOURCE_METRONOME_METRONOME_H_ #define SOURCE_METRONOME_METRONOME_H_ #include "../Sound/Alsa/AlsaParams.h" #include "ClickTypes.h" #include "MetronomeParameters.h" #include #include namespace DrumKit { class Metronome { public: explicit Metronome(Sound::AlsaParams alsaParams) noexcept; Metronome(Sound::AlsaParams alsaParams, MetronomeParameters params) noexcept; virtual ~Metronome(); void GenerateClick() noexcept; void SetParameters(const MetronomeParameters& params) { parameters = params; } void SetClickType(const ClickType& type) { parameters.clickType = type; } void SetRhythm(int rhythm) noexcept { parameters.rhythm = rhythm; } void SetBpmeas(int bpmeas) noexcept { parameters.beatsPerMeasure = bpmeas; } void SetTempo(int tempo); MetronomeParameters GetParameters() const { return parameters; } ClickType GetClickType() const noexcept { return parameters.clickType; } int GetTempo() const noexcept { return parameters.tempo; } int GetRhythm() const noexcept { return parameters.rhythm; } int GetBpmeas() const noexcept { return parameters.beatsPerMeasure; } std::vector GetRhythmList() const { return rhythmList; } std::vector GetBpmeasList() const { return bpmeasList; } std::vector GetData() const noexcept { return data; } static void LoadConfig(const std::string& filePath, MetronomeParameters& params); static void SaveConfig(const std::string& filePath, const MetronomeParameters& params); private: void GenerateSine(); void GenerateSquare(); int GetNumSamples() const; int GetBeatsRate() const; Sound::AlsaParams alsaParameters; // Metronome parameters MetronomeParameters parameters; std::vector bpmeasList; std::vector rhythmList; std::vector data; }; } /* namespace DrumKit */ #endif /* SOURCE_METRONOME_METRONOME_H_ */ libeXaDrums-0.6.0/Metronome/MetronomeParameters.h000066400000000000000000000007651377264061000220610ustar00rootroot00000000000000/* * MetronomeParameters.h * * Created on: 5 Oct 2016 * Author: jeremy */ #ifndef SOURCE_METRONOME_METRONOMEPARAMETERS_H_ #define SOURCE_METRONOME_METRONOMEPARAMETERS_H_ #include "ClickTypes.h" namespace DrumKit { struct MetronomeParameters { MetronomeParameters() : tempo(120), rhythm(2), beatsPerMeasure(4), clickType(ClickType::First) {}; int tempo; int rhythm; int beatsPerMeasure; ClickType clickType; }; } #endif /* SOURCE_METRONOME_METRONOMEPARAMETERS_H_ */ libeXaDrums-0.6.0/README.md000066400000000000000000000062421377264061000152250ustar00rootroot00000000000000# libeXaDrums [![Codacy Badge](https://api.codacy.com/project/badge/Grade/6fd320220fc24258a77b70ac716e4ee1)](https://app.codacy.com/app/SpintroniK/libeXaDrums?utm_source=github.com&utm_medium=referral&utm_content=SpintroniK/libeXaDrums&utm_campaign=Badge_Grade_Dashboard) ExaDrums is a software drum module that allows drummers play with custom-made drum kits. This C++ library provides the most common features of a drum module. Its associated graphical user interface can be found in a separate repository: Each drum kit is made of instruments that can be individually controlled. A built-in metronome can be combined with a rhythm coach to make practice sessions easier and efficient. The drum triggers can be adjusted so that their response feels as natural as possible, and different sensor interfaces include external sensors. Although eXaDrums is usable as a drum module, it is still a young project. As such, some features are not yet implemented or are still experimental. ## Table of content - [Installation and configuration](#installation-and-configuration) - [Dependencies](#dependencies) - [Building libeXaDrums](#building-libexadrums) - [Installation](#installation) - [Configuration](#configuration) - [Usage](#usage) ## Installation and configuration Libexadrums is available from the Debian unstable and testing distributions, as well as Ubuntu >=19.10. If you wish to install the latest version of the library, you can build the packages from the Github repositorie. ### Dependencies libeXaDrums depends on Alsa, TinyXml2 and minizip. You can install those two libraries using this command line: ```shell sudo apt install libasound2-dev libtinyxml2-dev libminizip-dev ``` LibeXaDrums also depends on build-essential, autotools and pkg-config in order to build and install the binaries: ```shell sudo apt install autoconf automake libtool build-essential pkg-config ``` If you wish to build the Debian packages (which is the recommended way to install the library), you will need to install debhelper: ```shell sudo apt install debhelper ``` You will also need to install git, so that you can clone this repository: ```shell sudo apt install git git clone https://github.com/SpintroniK/libeXaDrums.git ``` ### Building libeXaDrums Now that you have all dependencies, you can build the Debian packages. Make sure you have dehelper installed, and then checkout the debian branch from your cloned repository: ```shell cd libeXaDrums git checkout debian ``` Then you can build the package: ```shell dpkg-buildpackage -b -uc -us ``` If you have multiple cores/threads, you can speed up the build process by appending the option -jn to dpkg-buildpackage, where n is the number of threads that you want to use for the compilation. Example, for four threads type: `dpkg-buildpackage -b -uc -us -j4`. ### Installation The Debian packages are built in the parent directory, so you should be able to install them by using dpkg (don't forget to install them as root): ```shell cd .. sudo apt install ./libexadrums0_[...].deb sudo apt install ./libexadrums-dev[...].deb ``` ### Configuration ## Usage The library is self documented. libeXaDrums-0.6.0/Roadmap.md000066400000000000000000000024071377264061000156520ustar00rootroot00000000000000# LibeXaDrums roadmap This document outlines the development plan from a high level and will be updated as progress is made. ## Legend of annotations | Mark | Description | | ---------- | ------------------------------- | | open box | work not started - scheduled | | check mark | work completed | | ✍ | on-going work | ## Current version ### 0.6.0 - [X] Recorder export to PCM (wav). - [X] Simple calibration stuff. ## Future versions ### 0.7.0 ### 0.8.0 ### 0.9.0 - [ ] Instrument builder. ### 1.0.0 ## Documentation - [ ] Doxygen: document code. - [ ] API documentation. ## Past versions ### 0.5.0 - [x] Add export configuration feature. - [x] Add import configuration feature. - [x] Add GetVersion() to API. - [x] Document API. - [x] Bug fix: check if instrument's triggers exist. - [x] Add trigger sensitivity (gain). - [x] Fix trig on start bug (to be improved). - [x] Add rhythm coach score (done in eXaDrums directly). - [x] Create instrument only if enough triggers are available. - [x] Fix metronome volume bug. ### 0.4.2 - [x] Build successfully with clang++-7. - [x] Version.h.in instead of eXaDrums.h.in. - [x] Fix Readme (installation procedure -- checkout debian branch first). libeXaDrums-0.6.0/Sound/000077500000000000000000000000001377264061000150325ustar00rootroot00000000000000libeXaDrums-0.6.0/Sound/Alsa/000077500000000000000000000000001377264061000157125ustar00rootroot00000000000000libeXaDrums-0.6.0/Sound/Alsa/Alsa.cpp000066400000000000000000000154421377264061000173040ustar00rootroot00000000000000/* * Alsa.cpp * * Created on: 11 Apr 2015 * Author: jeremy */ #include "Alsa.h" #include "../../Util/Threading.h" #include "../../Util/ErrorHandling.h" #include #include #include #include #include #include #include using namespace Util; namespace Sound { Alsa::Alsa(const AlsaParams& parameters, std::shared_ptr const& mix) : params(parameters), mixer(mix), play(false), rec(false) { _snd_pcm_stream type = (params.capture)? SND_PCM_STREAM_CAPTURE:SND_PCM_STREAM_PLAYBACK; int err = snd_pcm_open(¶ms.handle, params.device.c_str(), type, 0); if(err >= 0) { snd_pcm_hw_params_alloca(¶ms.hwParams); SetHwParams(); snd_pcm_sw_params_alloca(¶ms.swParams); SetSwParams(); } else { throw Exception(snd_strerror(err), error_type_error); } auto devices = GetDevices(); auto itDev = std::find_if(devices.begin(), devices.end(), [&](const auto& d) { return d.second == params.device; }); if(itDev == devices.end()) { if(params.device == "default") { this->deviceName = devices.front().first; } else { throw Exception("Audio device not found.", error_type_error); // Audio device not found } } else { this->deviceName = itDev->first; } return; } Alsa::~Alsa() { params.buffer.clear(); // Close pcm handle //snd_pcm_drop(params.handle); snd_pcm_drain(params.handle); snd_pcm_close(params.handle); return; } std::vector> Alsa::GetDevices(const snd_pcm_stream_t type) { std::vector> devices; for(int card = 0; card >= 0; snd_card_next(&card)) { snd_ctl_t* handle; const std::string name("hw:" + std::to_string(card)); if(snd_ctl_open(&handle, name.data(), 0) < 0) { continue; } snd_ctl_card_info_t* cardInfo; snd_ctl_card_info_alloca(&cardInfo); if(snd_ctl_card_info(handle, cardInfo) < 0) { snd_ctl_close(handle); continue; } for(int dev = 0; dev >= 0; snd_ctl_pcm_next_device(handle, &dev)) { snd_pcm_info_t* pcmInfo; snd_pcm_info_alloca(&pcmInfo); snd_pcm_info_set_device(pcmInfo, dev); snd_pcm_info_set_subdevice(pcmInfo, 0); snd_pcm_info_set_stream(pcmInfo, type); if(snd_ctl_pcm_info(handle, pcmInfo) < 0) { continue; } if(dev == 0) { const std::string deviceId("plughw:" + std::to_string(card) + "," + std::to_string(dev)); const std::string deviceName(std::string(snd_ctl_card_info_get_name(cardInfo)) + " [hw:" + std::to_string(card) + "," + std::to_string(dev) + "]"); devices.push_back(std::make_pair(deviceName, deviceId)); } } snd_ctl_close(handle); } return devices; } void Alsa::Start() { auto sndErrorToException = [](auto code) { if(code != 0) { throw Exception(snd_strerror(code), error_type_error); } }; sndErrorToException(snd_pcm_drop(params.handle)); sndErrorToException(snd_pcm_prepare(params.handle)); //sndErrorToException(snd_pcm_start(params.handle)); if(params.capture) { rec = true; StartRecord(); } else { play = true; StartPlayback(); } return; } void Alsa::Stop() { if(params.capture) { rec = false; StopRecord(); } else { play = false; StopPlayback(); } return; } /// PRIVATE int Alsa::SetHwParams() { unsigned int realRate; snd_pcm_uframes_t size; int dir; snd_pcm_hw_params_any(params.handle, params.hwParams); snd_pcm_hw_params_set_rate_resample(params.handle, params.hwParams, 1); snd_pcm_hw_params_set_access(params.handle, params.hwParams, params.access); snd_pcm_hw_params_set_format(params.handle, params.hwParams, params.format); snd_pcm_hw_params_set_channels(params.handle, params.hwParams, params.nChannels); realRate = params.sampleRate; snd_pcm_hw_params_set_rate_near(params.handle, params.hwParams, &realRate, 0); //std::cout << "Real rate: " << realRate; snd_pcm_hw_params_set_buffer_time_near(params.handle, params.hwParams, ¶ms.bufferTime, &dir); snd_pcm_hw_params_get_buffer_size(params.hwParams, &size); params.bufferSize = size; snd_pcm_hw_params_set_period_time_near(params.handle, params.hwParams, ¶ms.periodTime, &dir); snd_pcm_hw_params_get_period_size(params.hwParams, &size, &dir); params.periodSize = size; params.buffer.resize(size); snd_pcm_hw_params(params.handle, params.hwParams); return 0; } int Alsa::SetSwParams() { snd_pcm_sw_params_current(params.handle, params.swParams); snd_pcm_sw_params_set_start_threshold(params.handle, params.swParams, (params.bufferSize / params.periodSize) * params.periodSize); snd_pcm_sw_params_set_avail_min(params.handle,params.swParams, params.periodSize); snd_pcm_sw_params(params.handle, params.swParams); return 0; } void Alsa::StartRecord() { recordThread = std::thread(&Alsa::Record, this); return; } void Alsa::StopRecord() { rec.store(false); recordThread.join(); return; } void Alsa::Record() { return; } void Alsa::StartPlayback() { playThread = std::thread(&Alsa::Playback, this); // // Set maximum priority to the thread // sched_param sch_params; // sch_params.sched_priority = sched_get_priority_max(SCHED_FIFO); // // pthread_setschedparam(playThread.native_handle(), SCHED_FIFO, &sch_params); Util::SetThreadPriority(playThread.native_handle(), 100); // Uncomment if debugging under Linux // pthread_setname_np(playThread.native_handle(), "Audio Thread\0"); return; } void Alsa::StopPlayback() { play.store(false); playThread.join(); return; } void Alsa::Playback() { int err = 0; while(play.load()) { int frames = params.periodSize / params.nChannels; while(frames > 0) { //time_point t_start = high_resolution_clock::now(); mixer->Mix(params.buffer); /*time_point t_end = high_resolution_clock::now(); auto d = duration(t_end-t_start).count(); if(d > 20) { std::cout << std::fixed << std::setprecision(2) << "Wall clock time passed: " << d << " us" << std::endl; } */ err = snd_pcm_writei(params.handle, params.buffer.data(), frames); if (err == -EAGAIN) { continue; } if (err < 0) { XrunRecovery(err); } frames -= err; } } return; } void Alsa::XrunRecovery(int& err) { using namespace std::literals::chrono_literals; if (err == -EPIPE) { err = snd_pcm_prepare(params.handle); } else if (err == -ESTRPIPE) { while ((err = snd_pcm_resume(params.handle)) == -EAGAIN) { std::this_thread::sleep_for(1ms); } if (err < 0) { err = snd_pcm_prepare(params.handle); } } return; } } libeXaDrums-0.6.0/Sound/Alsa/Alsa.h000066400000000000000000000021211377264061000167370ustar00rootroot00000000000000/* * Alsa.h * * Created on: 11 Apr 2015 * Author: jeremy */ #ifndef ALSA_H_ #define ALSA_H_ #define ALSA_PCM_NEW_HW_PARAMS_API #include "../Mixer/Mixer.h" #include "AlsaParams.h" #include #include #include namespace Sound { class Alsa { public: Alsa(const AlsaParams& parameters, std::shared_ptr const& mixer); virtual ~Alsa(); static std::vector> GetDevices(const snd_pcm_stream_t type = SND_PCM_STREAM_PLAYBACK); void Start(); void Stop(); std::string GetDeviceName() const noexcept { return deviceName; } AlsaParams GetParameters() const { return params; } private: int SetHwParams(); int SetSwParams(); void StartPlayback(); void StopPlayback(); void Playback(); void StartRecord(); void StopRecord(); void Record(); void XrunRecovery(int& err); std::thread playThread; std::thread recordThread; AlsaParams params; std::string deviceName; std::shared_ptr mixer; std::atomic play; std::atomic rec; }; } #endif /* ALSA_H_ */ libeXaDrums-0.6.0/Sound/Alsa/AlsaParameters.cpp000066400000000000000000000101701377264061000213210ustar00rootroot00000000000000/* * AlsaParameters.cpp * * Created on: 7 Feb 2016 * Author: jeremy */ #include "AlsaParameters.h" #include "../../Util/ErrorHandling.h" #include #include using namespace tinyxml2; using namespace Util; namespace Sound { void AlsaParameters::LoadAlsaParameters(const std::string& filePath, AlsaParams& parameters) { XMLDocument doc; if(doc.LoadFile(filePath.c_str()) != XML_SUCCESS) { throw Exception("Could not load sound card parameters.", error_type_error); } XMLElement* root = doc.RootElement(); XMLElement* device = root->FirstChildElement("device"); XMLElement* capture = root->FirstChildElement("capture"); XMLElement* format = root->FirstChildElement("format"); XMLElement* sampleRate = root->FirstChildElement("sampleRate"); XMLElement* nChannels = root->FirstChildElement("nChannels"); XMLElement* bufferTime = root->FirstChildElement("bufferTime"); XMLElement* periodTime = root->FirstChildElement("periodTime"); XMLElement* access = root->FirstChildElement("access"); parameters.device = device->GetText(); parameters.capture = (bool) std::stoi(capture->GetText()); parameters.format = GetSndFormat(format->GetText()); parameters.sampleRate = (unsigned int) std::stoi(sampleRate->GetText()); parameters.nChannels = (unsigned int) std::stoi(nChannels->GetText()); parameters.bufferTime = (unsigned int) std::stoi(bufferTime->GetText()); parameters.periodTime = (unsigned int) std::stoi(periodTime->GetText()); parameters.access = GetAccessType(access->GetText()); return; } void AlsaParameters::SaveAlsaParameters(const std::string& filePath, const AlsaParams& parameters) { // Create document XMLDocument doc; // Add root element XMLElement* root = doc.NewElement("root"); doc.InsertFirstChild(root); // Create Elements XMLElement* device = doc.NewElement("device"); XMLElement* capture = doc.NewElement("capture"); XMLElement* format = doc.NewElement("format"); XMLElement* sampleRate = doc.NewElement("sampleRate"); XMLElement* nChannels = doc.NewElement("nChannels"); XMLElement* bufferTime = doc.NewElement("bufferTime"); XMLElement* periodTime = doc.NewElement("periodTime"); XMLElement* access = doc.NewElement("access"); // Set values device->SetText(parameters.device.data()); capture->SetText(int(parameters.capture)); format->SetText("SND_PCM_FORMAT_S16_LE"); // XXX: temporary sampleRate->SetText(parameters.sampleRate); nChannels->SetText(parameters.nChannels); bufferTime->SetText(parameters.bufferTime); periodTime->SetText(parameters.periodTime); access->SetText("SND_PCM_ACCESS_RW_INTERLEAVED"); // XXX: temporary // Insert all elements root->InsertEndChild(device); root->InsertEndChild(capture); root->InsertEndChild(format); root->InsertEndChild(sampleRate); root->InsertEndChild(nChannels); root->InsertEndChild(bufferTime); root->InsertEndChild(periodTime); root->InsertEndChild(access); // Save modified file auto result = doc.SaveFile(filePath.data()); if(XML_SUCCESS != result) { throw Exception("Could not save triggers configuration.", error_type_error); } return; } // PRIVATE snd_pcm_format_t AlsaParameters::GetSndFormat(std::string formatName) { snd_pcm_format_t format; std::map dic; // Add definitions to dic dic["SND_PCM_FORMAT_S16_LE"] = SND_PCM_FORMAT_S16_LE; std::map< std::string, snd_pcm_format_t>::iterator i = dic.find(formatName); if(i != dic.end()) format = i->second; else format = SND_PCM_FORMAT_S8; // Default value return format; } snd_pcm_access_t AlsaParameters::GetAccessType(std::string accessName) { snd_pcm_access_t access; std::map dic; // Add definitions to dic dic["SND_PCM_ACCESS_RW_INTERLEAVED"] = SND_PCM_ACCESS_RW_INTERLEAVED; dic["SND_PCM_ACCESS_MMAP_INTERLEAVED"] = SND_PCM_ACCESS_MMAP_INTERLEAVED; std::map< std::string, snd_pcm_access_t>::iterator i = dic.find(accessName); if(i != dic.end()) access = i->second; else access = SND_PCM_ACCESS_RW_INTERLEAVED; // Default value return access; } } /* namespace Sound */ libeXaDrums-0.6.0/Sound/Alsa/AlsaParameters.h000066400000000000000000000013471377264061000207740ustar00rootroot00000000000000/* * AlsaParameters.h * * Created on: 7 Feb 2016 * Author: jeremy */ #ifndef SOURCE_SOUND_ALSA_ALSAPARAMETERS_H_ #define SOURCE_SOUND_ALSA_ALSAPARAMETERS_H_ #include "AlsaParams.h" #include #include namespace Sound { class AlsaParameters { public: static void LoadAlsaParameters(const std::string& filePath, AlsaParams& parameters); static void SaveAlsaParameters(const std::string& filePath, const AlsaParams& parameters); private: AlsaParameters() {}; virtual ~AlsaParameters() {}; static snd_pcm_format_t GetSndFormat(std::string formatName); static snd_pcm_access_t GetAccessType(std::string accessName); }; } /* namespace Sound */ #endif /* SOURCE_SOUND_ALSA_ALSAPARAMETERS_H_ */ libeXaDrums-0.6.0/Sound/Alsa/AlsaParams.h000066400000000000000000000013611377264061000201100ustar00rootroot00000000000000/* * AlsaParams.h * * Created on: 2 May 2015 * Author: jeremy */ #ifndef RASPIDRUMS_SOURCE_SOUND_ALSAPARAMS_H_ #define RASPIDRUMS_SOURCE_SOUND_ALSAPARAMS_H_ #include "alsa/asoundlib.h" #include #include namespace Sound { struct AlsaParams { std::string device; snd_pcm_t* handle; snd_pcm_hw_params_t* hwParams; snd_pcm_sw_params_t* swParams; bool capture; snd_pcm_format_t format; unsigned int sampleRate; unsigned int nChannels; unsigned int bufferTime; unsigned int periodTime; snd_pcm_sframes_t bufferSize; snd_pcm_sframes_t periodSize; snd_pcm_access_t access; std::vector buffer; }; } #endif /* RASPIDRUMS_SOURCE_SOUND_ALSAPARAMS_H_ */ libeXaDrums-0.6.0/Sound/InstrumentSoundType.h000066400000000000000000000025721377264061000212340ustar00rootroot00000000000000/* * InstrumentSoundType.h * * Created on: 15 Feb 2016 * Author: jeremy */ #ifndef SOURCE_SOUND_INSTRUMENTSOUNDTYPE_H_ #define SOURCE_SOUND_INSTRUMENTSOUNDTYPE_H_ #include "../Util/Enums.h" #include #include #include namespace Sound { enum class InstrumentSoundType { Default, RimShot, ClosingHiHat, First = Default, Last = ClosingHiHat }; inline std::ostream& operator<<(std::ostream& o, const InstrumentSoundType& x) { std::string os; switch (x) { case InstrumentSoundType::Default: os = "DrumHead"; break; case InstrumentSoundType::RimShot: os = "RimShot"; break; case InstrumentSoundType::ClosingHiHat: os = "ClosingHiHat";break; default: break; } return o << os; } inline InstrumentSoundType operator++(InstrumentSoundType& x) { return x = static_cast(std::underlying_type_t(x) + 1); }; inline InstrumentSoundType operator*(InstrumentSoundType x) { return x; }; inline InstrumentSoundType begin(InstrumentSoundType x) { return InstrumentSoundType::First; }; inline InstrumentSoundType end(InstrumentSoundType x) { InstrumentSoundType l = InstrumentSoundType::Last; return ++l; }; inline std::istream& operator>>(std::istream& is, InstrumentSoundType& x) { return Util::StreamToEnum(is, x); } } #endif /* SOURCE_SOUND_INSTRUMENTSOUNDTYPE_H_ */ libeXaDrums-0.6.0/Sound/Mixer/000077500000000000000000000000001377264061000161165ustar00rootroot00000000000000libeXaDrums-0.6.0/Sound/Mixer/Mixer.cpp000066400000000000000000000055671377264061000177230ustar00rootroot00000000000000/* * Mixer.cpp * * Created on: 11 Apr 2015 * Author: jeremy */ #include "Mixer.h" #include namespace Sound { Mixer::Mixer() noexcept { return; } Mixer::~Mixer() { return; } void Mixer::PlaySound(int id, float volume) { auto s = std::find_if(playList.begin(), playList.end(), [&id](SoundState& s) { return id == s.id && !s.isPlaying.load(std::memory_order_relaxed); }); if(s != playList.end()) { // The sound is already in playList //soundBank->sounds[s->id].Seek(0); s->volume = volume; s->index = 0; s->isPlaying.store(true, std::memory_order_release); } else { // Add sound to playList //playList.emplace_back(id, volume, true); const auto index = playListIndex.fetch_add(1, std::memory_order_relaxed); // Protect from overflow (ignore new sound...) if(index >= playList.size()) { return; } playList[index].id = id; playList[index].volume = volume; playList[index].isPlaying.store(true, std::memory_order_release); } return; } void Mixer::StopSound(int id) { // Stop sound in the play list for(auto& sound : playList) { if(sound.id == id) { sound.isPlaying.store(false, std::memory_order_relaxed); } } return; } void Mixer::Mix(std::vector& buffer) noexcept { // Fill buffer with zeros std::fill(buffer.begin(), buffer.end(), 0); std::size_t periodSize = buffer.size(); // Mix sounds for(std::size_t si = 0; si < playList.size(); si++) { if(playList[si].isPlaying.load(std::memory_order_acquire)) { SoundState& soundState = playList[si]; Sound& sound = soundBank->sounds[soundState.id]; if(sound.HasMoreData(soundState.index, periodSize)) { const float volume = sound.GetVolume(); const float mix_volume = soundState.volume; if(sound.IsLoop()) { std::size_t prevIdx = sound.LoadIndex(); if(prevIdx % sound.GetLength() < periodSize) { sound.SetStartTime(); } for(std::size_t i = 0; i < periodSize; i++) { buffer[i] += volume * mix_volume * sound.GetValue(i + soundState.index); } sound.StoreIndex(soundState.index); } else { const short* data = sound.GetData(); const std::size_t idx = soundState.index; for(std::size_t i = 0; i < periodSize; i++) { buffer[i] += volume * mix_volume * data[idx + i]; } } soundState.index += periodSize; //sound.AddToIndex(periodSize); } else { soundState.isPlaying.store(false, std::memory_order_relaxed); } } } return; } /** * @brief Stops all sounds (must be done after the mixer has been stopped). * */ void Mixer::Clear() noexcept { playListIndex.store(0, std::memory_order_relaxed); for(auto& s : playList) { s.isPlaying.store(false, std::memory_order_relaxed); } } } libeXaDrums-0.6.0/Sound/Mixer/Mixer.h000066400000000000000000000014231377264061000173530ustar00rootroot00000000000000/* * Mixer.h * * Created on: 11 Apr 2015 * Author: jeremy */ #ifndef MIXER_H_ #define MIXER_H_ #include "../SoundBank/SoundBank.h" #include "../SoundState.h" #include "../Sound.h" #include #include #include #include namespace Sound { class Mixer { public: Mixer() noexcept; virtual ~Mixer(); void PlaySound(int id, float volume); void LoopSound(int id, float volume); void StopSound(int id); void Mix(std::vector& buffer) noexcept; void Clear() noexcept; void SetSoundBank(std::shared_ptr& sb) noexcept { this->soundBank = sb; } private: std::shared_ptr soundBank; std::array playList; std::atomic playListIndex{0}; }; } #endif /* MIXER_H_ */ libeXaDrums-0.6.0/Sound/Sound.cpp000066400000000000000000000036701377264061000166340ustar00rootroot00000000000000/* * SoundSample.cpp * * Created on: 27 Feb 2016 * Author: jeremy */ #include "Sound.h" #include using namespace std::chrono; namespace Sound { Sound::Sound() : Sound(std::vector{}) { return; } Sound::Sound(Sound& s) : Sound() { swap(*this, s); return; } Sound::Sound(Sound&& s) : Sound() { swap(*this, s); return; } Sound::Sound(int id, const std::vector& soundData, float vol) : id(id), loop(false), data(soundData), length(data.size()) { volume.store(vol); idx.store(0); lastStartTime.store(time_point_cast(high_resolution_clock::now()).time_since_epoch().count(), std::memory_order_relaxed); return; } Sound::Sound(int id, const std::vector& soundData) : Sound(id, soundData, 1.0f) { return; } Sound::Sound(const std::vector& soundData, float volume) : Sound(-1, soundData, volume) { return; } Sound::Sound(const std::vector& soundData) : Sound(-1, soundData, 1.0f) { return; } Sound::~Sound() { return; } bool Sound::HasMoreData(std::size_t index, std::size_t length) const { if(loop) return true; if(index + length <= data.size()) { return true; } else { return false; } } void Sound::SetVolume(float volume) { float newVolume = std::min(std::max(0., volume), 1.); this->volume.store(newVolume); return; } void Sound::SetStartTime() { this->lastStartTime.store(time_point_cast(high_resolution_clock::now()).time_since_epoch().count(), std::memory_order_relaxed); return; } /*void Sound::AddToIndex(int offset) { if(!HasMoreData(offset)) { idx.store(0); } else { long prevIdx = idx.fetch_add(offset); if(prevIdx % length < offset) { this->lastStartTime.store(time_point_cast(high_resolution_clock::now()).time_since_epoch().count()); } } return; }*/ // PRIVATE } /* namespace Sound */ libeXaDrums-0.6.0/Sound/Sound.h000066400000000000000000000045431377264061000163010ustar00rootroot00000000000000/* * SoundSample.h * * Created on: 27 Feb 2016 * Author: jeremy */ #ifndef SOURCE_SOUND_SOUND_H_ #define SOURCE_SOUND_SOUND_H_ #include #include #include namespace Sound { class Sound { friend class SoundBank; friend class SoundProcessor; public: Sound(); Sound(Sound& s); Sound(Sound&& s); explicit Sound(const std::vector& soundData); Sound(const std::vector& soundData, float volume); Sound(int id, const std::vector& soundData); Sound(int id, const std::vector& soundData, float vol); virtual ~Sound(); friend void swap(Sound& first, Sound& second) noexcept { using std::swap; swap(first.id, second.id); swap(first.loop, second.loop); swap(first.data, second.data); swap(first.length, second.length); // "swap" atomics second.volume.store(first.volume.exchange(second.volume.load())); second.idx.store(first.idx.exchange(second.idx.load())); second.lastStartTime.store(first.lastStartTime.exchange(second.lastStartTime.load())); return; } Sound& operator=(Sound s) noexcept { swap(*this, s); return *this; } void SetVolume(float volume); //void AddToIndex(int offset); bool HasMoreData(std::size_t index, std::size_t length) const; //bool IsFinished() const { return idx.load() >= (int)data.size(); } inline void StoreIndex(long i) { idx.store(i); } void SetStartTime(); inline void SetLoop(bool s) noexcept { loop = s; } inline int GetId() const { return this->id; } inline float GetVolume() const noexcept { return volume.load(); } inline unsigned long LoadIndex() const noexcept { return idx.load(); } inline unsigned long long GetLastStartTime() const noexcept { return lastStartTime.load(std::memory_order_relaxed); } inline int GetLength() const noexcept { return length; } inline const short* GetData() const { return data.data(); } const std::vector& GetInternalData() const { return data; } inline const short GetValue(int i) const noexcept { return data[i % length]; } inline bool IsLoop() const { return loop; } private: int id; bool loop; std::atomic idx; std::atomic lastStartTime; std::vector data; int length; std::atomic volume; }; typedef std::shared_ptr SoundPtr; } /* namespace Sound */ #endif /* SOURCE_SOUND_SOUND_H_ */ libeXaDrums-0.6.0/Sound/SoundBank/000077500000000000000000000000001377264061000167165ustar00rootroot00000000000000libeXaDrums-0.6.0/Sound/SoundBank/SoundBank.cpp000066400000000000000000000070411377264061000213100ustar00rootroot00000000000000/* * SoundBank.cpp * * Created on: 14 Feb 2016 * Author: jeremy */ #include "SoundBank.h" #include "../Util/WavUtil.h" #include #include #include #include namespace Sound { SoundBank::SoundBank(const std::string& dataFolder) noexcept : soundBankFolder(dataFolder + "SoundBank/") { return; } SoundBank::~SoundBank() { return; } int SoundBank::AddSound(const std::vector& data) { return AddSound(data, 1.0f); } int SoundBank::AddSound(const std::vector& data, float volume) { // Add sound to collection std::size_t sId = sounds.size(); sounds.push_back(Sound(sId, data, volume)); return sounds.back().GetId(); } int SoundBank::AddSound(Sound&& sound, float volume) { // Add sound to collection sound.id = sounds.size(); sound.volume.store(volume); sounds.push_back(std::move(sound)); return sounds.back().GetId(); } void SoundBank::DeleteSound(int id) { auto it = std::remove_if(sounds.begin(), sounds.end(), [&id](Sound& sound) { return id == sound.GetId(); }); if(it != end(sounds)) { sounds.erase(it); } return; } void SoundBank::LoopSound(int id, bool s) { if(static_cast(id) < sounds.size()) { sounds[id].SetLoop(s); } } int SoundBank::LoadSound(const std::string& filename) { return LoadSound(filename, 1.0f); } int SoundBank::LoadSound(const std::string& filename, float volume) { std::string fileLocation = this->soundBankFolder + filename; // Open file std::ifstream soundFile(fileLocation); // Check file validity if(!soundFile.good()) { throw - 1; } soundFile.seekg(0, std::ios::end); // Get file size in bytes size_t fileSize = soundFile.tellg(); soundFile.seekg(0, std::ios::beg); // HEADER std::vector header_data(44); soundFile.read((char*)header_data.data(), header_data.size()); WavHeader header(header_data); size_t chunkLength = header.get_subchunk2_size(); size_t dataLength = 0; if(chunkLength + header_data.size() == fileSize) { dataLength = chunkLength; } else { std::string error{"Couldn't read sound file: " + fileLocation}; throw std::runtime_error(error); } uint32_t data_size_short = dataLength / sizeof(short); std::vector data(data_size_short); soundFile.read((char*)data.data(), dataLength); // Close file soundFile.close(); // Add sound to collection std::size_t sId = sounds.size(); sounds.push_back(Sound(sId, data, volume)); return sounds.back().GetId(); } std::vector SoundBank::GetSoundFiles(const std::string& dataFolder) { std::vector paths; std::string location = dataFolder + "SoundBank/"; struct dirent* ent; DIR* directory = opendir(location.c_str()); // Scan directory while((ent = readdir(directory)) != NULL) { // Check if entry is a directory if(ent->d_type == DT_DIR) { // Get directory path std::string soundsDirPath = location + std::string(ent->d_name) + "/"; std::string dirName = std::string(ent->d_name) + "/"; struct dirent* dir; DIR* soundsDir = opendir(soundsDirPath.c_str()); while((dir = readdir(soundsDir)) != NULL) { // Get file name and extension std::string fileName(dir->d_name); std::string fileExtension = fileName.substr(fileName.find_last_of(".") + 1); if(fileExtension == "raw") { paths.push_back(dirName + fileName); } } closedir(soundsDir); } } closedir(directory); return paths; } } /* namespace Sound */ libeXaDrums-0.6.0/Sound/SoundBank/SoundBank.h000066400000000000000000000022251377264061000207540ustar00rootroot00000000000000/* * SoundBank.h * * Created on: 14 Feb 2016 * Author: jeremy */ #ifndef SOURCE_SOUND_SOUNDBANK_SOUNDBANK_H_ #define SOURCE_SOUND_SOUNDBANK_SOUNDBANK_H_ #include "../Sound.h" #include #include namespace Sound { class Mixer; class SoundBank { friend class Mixer; public: explicit SoundBank(const std::string& dataFolder) noexcept; virtual ~SoundBank(); static std::vector GetSoundFiles(const std::string& dataFolder); int LoadSound(const std::string& filename); int LoadSound(const std::string& filename, float volume); int AddSound(const std::vector& soundData); int AddSound(const std::vector& soundData, float volume); int AddSound(Sound&& sound, float volume); void DeleteSound(int id); void LoopSound(int id, bool s); void Clear() noexcept { std::vector().swap(sounds); } void SetSoundVolume(int id, float volume) { sounds[id].SetVolume(volume); } const Sound& GetSound(int id) const { return sounds[id]; } private: std::string soundBankFolder; std::vector sounds; }; } /* namespace Sound */ #endif /* SOURCE_SOUND_SOUNDBANK_SOUNDBANK_H_ */ libeXaDrums-0.6.0/Sound/SoundProcessor/000077500000000000000000000000001377264061000200225ustar00rootroot00000000000000libeXaDrums-0.6.0/Sound/SoundProcessor/SoundProcessor.cpp000066400000000000000000000011731377264061000235200ustar00rootroot00000000000000/* * SoundProcessor.cpp * * Created on: 14 Nov 2015 * Author: jeremy */ #include "SoundProcessor.h" #include #include #include namespace Sound { Sound SoundProcessor::Muffle(const Sound& sound, float m) { //XXX Need to check m! const std::vector& soundData = sound.GetInternalData(); std::vector newSoundData(soundData.size()); const float gamma = -3.0f / (m * soundData.size()); for(std::size_t i = 0; i < newSoundData.size(); ++i) { newSoundData[i] = soundData[i] * std::exp(i * gamma); } return Sound(newSoundData); } } /* namespace Sound */ libeXaDrums-0.6.0/Sound/SoundProcessor/SoundProcessor.h000066400000000000000000000007421377264061000231660ustar00rootroot00000000000000/* * SoundProcessor.h * * Created on: 14 Nov 2015 * Author: jeremy */ #ifndef SOURCE_SOUND_SOUNDPROCESSOR_SOUNDPROCESSOR_H_ #define SOURCE_SOUND_SOUNDPROCESSOR_SOUNDPROCESSOR_H_ #include "../Sound.h" namespace Sound { class SoundProcessor { public: static Sound Muffle(const Sound& sound, float m); private: SoundProcessor() {}; virtual ~SoundProcessor() {}; }; } /* namespace Sound */ #endif /* SOURCE_SOUND_SOUNDPROCESSOR_SOUNDPROCESSOR_H_ */ libeXaDrums-0.6.0/Sound/SoundState.h000066400000000000000000000017121377264061000172750ustar00rootroot00000000000000/* * SoundState.h * * Created on: 30 Jul 2017 * Author: jeremy */ #ifndef SOURCE_SOUND_SOUNDSTATE_H_ #define SOURCE_SOUND_SOUNDSTATE_H_ #include namespace Sound { struct SoundState { SoundState() : id(0), volume(0.), index(0) { isPlaying.store(false); } SoundState(int i, float v, bool ip) : id(i), volume(v), index(0) { isPlaying.store(ip, std::memory_order_release); } SoundState(const SoundState& s) : id(s.id), volume(s.volume), index(s.index) { isPlaying.store(s.isPlaying.load(std::memory_order_acquire), std::memory_order_release); } SoundState& operator=(const SoundState& s) { this->id = s.id; this->volume = s.volume; this->index = s.index; this->isPlaying.store(s.isPlaying.load(std::memory_order_acquire), std::memory_order_release); return *this; } int id; float volume; std::size_t index; std::atomic isPlaying; }; } #endif /* SOURCE_SOUND_SOUNDSTATE_H_ */ libeXaDrums-0.6.0/Sound/Util/000077500000000000000000000000001377264061000157475ustar00rootroot00000000000000libeXaDrums-0.6.0/Sound/Util/WavUtil.h000066400000000000000000000114501377264061000175140ustar00rootroot00000000000000/* * WavUtil.h * * Created on: 12 Nov 2017 * Author: jeremy */ #ifndef SOURCE_SOUND_UTIL_WAVUTIL_H_ #define SOURCE_SOUND_UTIL_WAVUTIL_H_ #include #include #include #include #include #include #include namespace Sound { template inline T BytesToWord(const std::vector& bytes) { return std::accumulate(bytes.rbegin(), bytes.rend(), T{0}, [](T a, T b) { return a << 8 | b; }); } template inline std::vector SubVector(const std::vector& vec, size_t i, size_t j) { return std::vector(vec.begin() + i, vec.begin() + j); } inline std::string BytesToString(const std::vector& bytes, size_t start = 0, size_t stop = 0) { if(stop == 0) { stop = bytes.size(); } return std::string(bytes.begin() + start, bytes.begin() + stop); } template inline std::enable_if_t::value, void> CopyBytesToVector(const T& bytes, std::vector& vec, size_t offset) { std::copy(std::begin(bytes), std::end(bytes), vec.begin() + offset); } template inline std::enable_if_t::value, void> CopyBytesToVector(const T& bytes, std::vector& vec, size_t offset) { auto ptr = reinterpret_cast(&bytes); CopyBytesToVector(std::vector(ptr, ptr + sizeof(T)), vec, offset); } class WavHeader { public: explicit WavHeader(const std::vector& header_data) : chunk_id(BytesToString(header_data, 0, 4)), format(BytesToString(header_data, 8, 12)), subchunk1_id(BytesToString(header_data, 12, 16)), subchunk2_id(BytesToString(header_data, 36, 40)) { chunk_size = BytesToWord(SubVector(header_data, 4, 8)); subchunk1_size = BytesToWord(SubVector(header_data, 16, 20)); audio_format = BytesToWord(SubVector(header_data, 20, 22)); num_channels = BytesToWord(SubVector(header_data, 22, 24)); sample_rate = BytesToWord(SubVector(header_data, 24, 28)); byte_rate = BytesToWord(SubVector(header_data, 28, 32)); block_align = BytesToWord(SubVector(header_data, 32, 34)); bits_per_sample = BytesToWord(SubVector(header_data, 34, 36)); subchunk2_size = BytesToWord(SubVector(header_data, 40, 44)); } WavHeader() : chunk_id("RIFF"), chunk_size(0), format("WAVE"), subchunk1_id("fmt "), subchunk1_size(16), audio_format(1), num_channels(2), sample_rate(48000), byte_rate(192000), block_align(4), bits_per_sample(16), subchunk2_id("data"), subchunk2_size(0) {} void SetDataLength(size_t length) noexcept { subchunk2_size = length; chunk_size = subchunk2_size + 36; } void SetSampleRate(uint32_t sampleRate) { sample_rate = sampleRate; byte_rate = num_channels * sample_rate * (bits_per_sample / 8); } std::vector ToBytes() const { std::vector headerData(44); CopyBytesToVector(chunk_id, headerData, 0); CopyBytesToVector(chunk_size, headerData, 4); CopyBytesToVector(format, headerData, 8); CopyBytesToVector(subchunk1_id, headerData, 12); CopyBytesToVector(subchunk1_size, headerData, 16); CopyBytesToVector(audio_format, headerData, 20); CopyBytesToVector(num_channels, headerData, 22); CopyBytesToVector(sample_rate, headerData, 24); CopyBytesToVector(byte_rate, headerData, 28); CopyBytesToVector(block_align, headerData, 32); CopyBytesToVector(bits_per_sample, headerData, 34); CopyBytesToVector(subchunk2_id, headerData, 36); CopyBytesToVector(subchunk2_size, headerData, 40); return headerData; } inline uint32_t get_subchunk2_size() const noexcept { return subchunk2_size; } ~WavHeader() = default; private: std::string chunk_id; uint32_t chunk_size; std::string format; std::string subchunk1_id; uint32_t subchunk1_size; uint16_t audio_format; uint16_t num_channels; uint32_t sample_rate; uint32_t byte_rate; uint16_t block_align; uint16_t bits_per_sample; std::string subchunk2_id; uint32_t subchunk2_size; }; // static std::vector load_wav_from_disk(const std::string& file_location) // { // // // Open file // std::ifstream file(file_location); // // // Check file validity // if(!file.good()) // { // throw std::runtime_error("Couldn't load file."); // } // // // HEADER // std::vector header_data(44); // file.read((char*)header_data.data(), header_data.size()); // wav_header header(header_data); // // // DATA // auto data_size{header.get_subchunk2_size()}; // // uint32_t data_size_short = data_size / sizeof(short); // std::vector data(data_size_short); // // file.read((char*)data.data(), data_size); // // return data; // } } #endif /* SOURCE_SOUND_UTIL_WAVUTIL_H_ */ libeXaDrums-0.6.0/Util/000077500000000000000000000000001377264061000146575ustar00rootroot00000000000000libeXaDrums-0.6.0/Util/Crypt.h000066400000000000000000000056001377264061000161320ustar00rootroot00000000000000/* * Crypt.h * * Created on: 26 Feb 2018 * Author: jeremy */ #ifndef SOURCE_UTIL_CRYPT_H_ #define SOURCE_UTIL_CRYPT_H_ #include #include #include #include namespace Util { static constexpr char Base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static constexpr std::array Base64Indices = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }}; static std::string Base64Encode(const std::vector& str) { const auto len = str.size(); size_t d = len % 3; std::string str64(4 * (int(d > 0) + len / 3), '='); for(size_t i = 0, j = 0; i < len - d; i += 3) { int32_t n = int32_t(str[i]) << 16 | int32_t(str[i + 1]) << 8 | str[i + 2]; str64[j++] = Base64Chars[n >> 18]; str64[j++] = Base64Chars[n >> 12 & 0x3F]; str64[j++] = Base64Chars[n >> 6 & 0x3F]; str64[j++] = Base64Chars[n & 0x3F]; } if(d-- > 0) // Padding { int32_t n = d ? int32_t(str[len - 2]) << 8 | str[len - 1] : str[len - 1]; str64[str64.size() - 2] = d ? Base64Chars[(n & 0xF) << 2] : '='; str64[str64.size() - 3] = d ? Base64Chars[n >> 4 & 0x03F] : Base64Chars[(n & 3) << 4]; str64[str64.size() - 4] = d ? Base64Chars[n >> 10] : Base64Chars[n >> 2]; } return str64; } template static std::string Base64Encode(const std::vector& input) { std::vector str; str.reserve(sizeof(T) * input.size()); std::copy_n(reinterpret_cast(input.data()), sizeof(T) * input.size(), std::back_inserter(str)); return Base64Encode(str); } /*static std::string Base64Decode(const std::string& str64) { const auto len = str64.size(); const auto pad = len > 0 && (len % 4 || str64[len - 1] == '='); const size_t length = ((len + 3) / 4 - pad) * 4; std::string str(length / 4 * 3 + pad, '\0'); for(size_t i = 0, j = 0; i < length; i += 4) { int32_t n = Base64Indices[str64[i]] << 18 | Base64Indices[str64[i + 1]] << 12 | Base64Indices[str64[i + 2]] << 6 | Base64Indices[str64[i + 3]]; str[j++] = n >> 16; str[j++] = n >> 8 & 0xFF; str[j++] = n & 0xFF; } if(pad != 0) { int32_t n = Base64Indices[str64[length]] << 18 | Base64Indices[str64[length + 1]] << 12; str.back() = n >> 16; if (len > length + 2 && str64[length + 2] != '=') { n |= Base64Indices[str64[length + 2]] << 6; str.push_back(n >> 8 & 0xFF); } } return str; } */ } #endif /* SOURCE_UTIL_CRYPT_H_ */ libeXaDrums-0.6.0/Util/Enums.h000066400000000000000000000030321377264061000161150ustar00rootroot00000000000000/* * Enums.h * * Created on: 29 Sep 2016 * Author: jeremy */ #ifndef SOURCE_UTIL_ENUMS_H_ #define SOURCE_UTIL_ENUMS_H_ #include "ErrorHandling.h" #include #include #include #include namespace Util { /** * Helper function to make conversion to enums easier. * @param is Input stream. * @param x Enum type to convert the input stream to. * @return */ template inline std::istream& StreamToEnum(std::istream& is, T& x) { std::string s; is >> s; for(const auto& l : T()) { std::ostringstream os; os << l; if(os.str() == s) { x = l; } } return is; } class Enums { public: template static std::string ToString(const T& e) { for (const auto& c : T{}) { if(c == e) { std::stringstream ss; ss << e; return ss.str(); } } throw Exception("Could not convert enum to string.", error_type_error); return ""; } template static T ToElement(const std::string& s) { for (const auto& c : T{}) { std::stringstream ss; ss << c; if(ss.str() == s) { return c; } } throw Exception("Could not convert string " + s + " to enum.", error_type_error); return T::First; } template static std::vector GetEnumVector() { std::vector v; for (const auto& c : T{}) { v.push_back(c); } return v; } private: Enums() = delete; ~Enums() = delete; }; } #endif /* SOURCE_UTIL_ENUMS_H_ */ libeXaDrums-0.6.0/Util/ErrorHandling.h000066400000000000000000000076751377264061000176050ustar00rootroot00000000000000#ifndef LIBEXADRUMS_SOURCE_UTIL_ERROR_HANDLING_H_ #define LIBEXADRUMS_SOURCE_UTIL_ERROR_HANDLING_H_ #include #include namespace Util { #if __cplusplus__ extern "C" { #endif enum errorType : int32_t { error_type_success = 0, error_type_warning = 1, error_type_question = 2, error_type_error = 3, error_type_other = 4 }; typedef struct _error { char message[255]; int32_t type; } error; inline error make_error(const char* message, errorType error_type) { error e{"", error_type}; std::snprintf(e.message, sizeof e.message, "%s", message); // prevents overflow return e; } /** * @brief Merge two errors together * * @param e1 first erorr * @param e2 second error * @return error merged error */ inline error merge_errors(const error& e1, const error& e2) { error merged_error; if(e1.type >= e2.type) { std::snprintf(merged_error.message, sizeof merged_error.message, "%s", e1.message); merged_error.type = e1.type; } else { std::snprintf(merged_error.message, sizeof merged_error.message, "%s", e2.message); merged_error.type = e2.type; } return merged_error; } /** * @brief Update an error using another, newer, error. * * @param e error to be updated * @param new_error another error that alters the state of e * @return errorType error type */ inline errorType update_error(error& e, const error& new_error) { e = merge_errors(e, new_error); return errorType{static_cast(e.type)}; } #if __cplusplus__ } #endif class Exception : public std::exception { public: /** * @brief Construct a new Exception object from an error type. * * @param err */ explicit Exception(const error& err) noexcept : message{err.message}, error_type{static_cast(err.type)} {} Exception(const char* what_arg, errorType err_type) noexcept : message{what_arg}, error_type{err_type} {} Exception(std::string&& what_arg, errorType err_type) noexcept : message{std::move(what_arg)}, error_type{err_type} {} virtual const char* what() const noexcept final { return message.data(); } errorType type() const noexcept { return error_type; } private: std::string message; errorType error_type; }; /** * @brief Convert an error to an Exception. Throws if the error type is not error_type_success. * * @tparam F A callable type. * @param f A callable that returns an error. */ template void ErrorToException(F&& f) { error err = f(); if(err.type != error_type_success) { throw Exception(err); } } /** * @brief Convert an Exception to an error. * * @tparam F A callable type. * @param f A callable that throws the Exception that is to be converted. * @return error The error that corresponds to the Exception that has been thrown from the callable, or success if no exception was thrown. */ template error ExceptionToError(F&& f) noexcept { try { f(); } catch(const Exception& except) { return make_error(except.what(), except.type()); } catch(...) { return make_error("Unknown error.", error_type_error); } return error{"", error_type_success}; } } #endiflibeXaDrums-0.6.0/Util/Misc.h000066400000000000000000000047211377264061000157270ustar00rootroot00000000000000/* * Misc.h * * Created on: 11 Feb 2018 * Author: jeremy */ #ifndef SOURCE_UTIL_MISC_H_ #define SOURCE_UTIL_MISC_H_ #include #include #include #include #include namespace Util { template inline typename std::enable_if_t for_each_tuple(std::tuple&, F) { return; } template inline typename std::enable_if_t<(I < sizeof...(T))> for_each_tuple(std::tuple& t, F f) { f(std::get(t)); for_each_tuple(t, f); } template inline void StrToValue(const std::string& is, T& val) { std::stringstream s(is); s >> val; } template inline void VectorOfStrToTuple(const std::vector& vec, std::tuple& tuple) { size_t i = 0; for_each_tuple(tuple, [&](auto& t){ StrToValue(vec[i++], t); }); } /** * Converts every element of input and concatenates the results to a string, where elements are separated by separator. * The last separator is removed. * @param input * @param separator * @return */ template static std::string JoinToStr(const T& input, std::string separator = ",") { std::string str = std::accumulate(std::begin(input), std::end(input), std::string{}, [&](const auto& a, const auto& b) { return a + std::to_string(b) + separator; }); return str.substr(0, str.size() - separator.size()); } /** * * @brief If v compares less than lo, returns lo; otherwise if hi compares less than v, returns hi; otherwise returns v. Uses comp to compare the values. * @param v the value to clamp. * @param lo the low boundary to clamp v to. * @param hi the high boundary to clamp v to. * @param comp Comparison function object. * @return Clamped value. */ template inline constexpr const T& clamp( const T& v, const T& lo, const T& hi, Compare comp ) { return comp(v, lo) ? lo : comp(hi, v) ? hi : v; } /** * @brief If v compares less than lo, returns lo; otherwise if hi compares less than v, returns hi; otherwise returns v. * @param v the value to clamp. * @param lo the low boundary to clamp v to. * @param hi the high boundary to clamp v to. * @return Clamped value. */ template inline constexpr const T& clamp( const T& v, const T& lo, const T& hi ) { return Util::clamp( v, lo, hi, std::less<>() ); } } #endif /* SOURCE_UTIL_MISC_H_ */ libeXaDrums-0.6.0/Util/Parsing.h000066400000000000000000000010401377264061000164260ustar00rootroot00000000000000/* * Parsing.h * * Created on: 25 Feb 2018 * Author: jeremy */ #ifndef SOURCE_UTIL_PARSING_H_ #define SOURCE_UTIL_PARSING_H_ #include namespace Util { template class Token { public: friend std::istream& operator>>(std::istream& is, Token& t) { std::getline(is, t.data, delim); return is; } operator std::string() const { return data; } static constexpr char delimiter = delim; private: std::string data; }; using Line = Token<'\n'>; } #endif /* SOURCE_UTIL_PARSING_H_ */ libeXaDrums-0.6.0/Util/SimpleSafeQueue.h000066400000000000000000000040771377264061000200750ustar00rootroot00000000000000/* * SimpleSafeQueue.h * * Created on: 20 Feb 2018 * Author: jeremy */ #ifndef SOURCE_UTIL_SIMPLESAFEQUEUE_H_ #define SOURCE_UTIL_SIMPLESAFEQUEUE_H_ #include #include #include namespace Util { /** * A thread-safe Single Producer Single Consumer queue. * Its size is fixed, it's a circular buffer. * Default size = 32. */ template class SimpleSafeQueue { public: /** * @brief Pushes new value to the queue, and returns true if successful. * @param value * @return true if the queue isn't full, false otherwise. */ bool Push(const T& value) { const auto oldWriterIndex = writerIndex.load(std::memory_order_relaxed); const auto newWriterIndex = GetNextIndex(oldWriterIndex); // Is the queue full? if(newWriterIndex == readerIndex.load(std::memory_order_acquire)) { return false; } buffer[oldWriterIndex] = value; writerIndex.store(newWriterIndex, std::memory_order_release); return true; } /** * @brief Pops an element from the queue, and returns true if successful. * @param value * @return true if the queue isn't empty, false otherwise. */ bool Pop(T& value) { const auto oldWriterIndex = writerIndex.load(std::memory_order_acquire); const auto oldReaderIndex = readerIndex.load(std::memory_order_relaxed); // Is the queue empty? if(oldWriterIndex == oldReaderIndex) { return false; } value = std::move(buffer[oldReaderIndex]); readerIndex.store(GetNextIndex(oldReaderIndex), std::memory_order_release); return true; } /** * @brief Returns the number of elements that the queue can hold. * @return */ constexpr size_t Capacity() const { return Length; } private: static constexpr size_t GetNextIndex(size_t pos) noexcept { return (++pos == bufferSize)? 0 : pos; } static constexpr size_t bufferSize = Length + 1; std::array buffer; std::atomic writerIndex{0}; std::atomic readerIndex{0}; }; } #endif /* SOURCE_UTIL_SIMPLESAFEQUEUE_H_ */ libeXaDrums-0.6.0/Util/Threading.h000066400000000000000000000026321377264061000167400ustar00rootroot00000000000000/* * Threading.h * * Created on: 11 Feb 2018 * Author: jeremy */ #ifndef SOURCE_UTIL_THREADING_H_ #define SOURCE_UTIL_THREADING_H_ #include "Misc.h" #include #include #include #include namespace Util { static constexpr size_t minNbThreads = 3; // Minimum number of threads available to enable threads priority. /** * @brief Gives a relative priority to a thread, only if more than minNbThreads threads are available. * @param threadHandle Native handle of the thread object. * @param p Priority in percent: 0 = min, 100 = max. * @param schedType Type of scheduler. */ template static void SetThreadPriority(const T& threadHandle, int p, int schedType = SCHED_FIFO) { if(std::thread::hardware_concurrency() >= minNbThreads) { p = Util::clamp(p, 0, 100); auto maxPriority{sched_get_priority_max(SCHED_FIFO)}; size_t priority = static_cast((p * maxPriority) / 100); sched_param sch_params; sch_params.sched_priority = priority; pthread_setschedparam(threadHandle, schedType, &sch_params); } } class SpinLock { public: void lock() { while(locked.test_and_set(std::memory_order_acquire)) { ; } } void unlock() { locked.clear(std::memory_order_release); } private: std::atomic_flag locked = ATOMIC_FLAG_INIT; }; } #endif /* SOURCE_UTIL_THREADING_H_ */ libeXaDrums-0.6.0/Util/Time.h000066400000000000000000000023321377264061000157260ustar00rootroot00000000000000/* * Timing.h * * Created on: 15 Sep 2017 * Author: jeremy */ #ifndef SOURCE_UTIL_TIME_H_ #define SOURCE_UTIL_TIME_H_ #include #include #include namespace Util { /** * Converts a timestamp (int64_t for instance) in a string that contains the corresponding date. * @param timestamp * @return */ template static std::string TimeStampToStr(T timestamp) { using namespace std::chrono; auto tp = time_point{U{timestamp}}; auto time_t = system_clock::to_time_t(tp); auto result = std::string{std::ctime(&time_t)}; result.pop_back(); return result; } /** * Very useful to easily benchmark things using lambda functions as parameter. * @param f Callable to be executed. * @return Execution time in units of T. */ template static double GetTime(const Func& f) { using namespace std::chrono; auto t_start = high_resolution_clock::now(); f(); auto t_end = high_resolution_clock::now(); auto d = duration>(t_end - t_start); return d.count(); } } #endif /* SOURCE_UTIL_TIME_H_ */ libeXaDrums-0.6.0/Util/Xml.h000066400000000000000000000066251377264061000156010ustar00rootroot00000000000000/* * Xml.h * * Created on: 11 Feb 2018 * Author: jeremy */ #ifndef SOURCE_UTIL_XML_H_ #define SOURCE_UTIL_XML_H_ #include #include #include #include namespace Util { /** * Xml Element wrapper, very useful for range-based for loops. */ class XmlElement { public: XmlElement(tinyxml2::XMLElement* element_, const std::string& name_) : element{element_}, name{name_} {} XmlElement(tinyxml2::XMLElement* element_) : element{element_}, name{} {} inline XmlElement begin() const { if(name.empty()) { return XmlElement{element->FirstChildElement()}; } return XmlElement{element->FirstChildElement(name.data())}; } inline XmlElement end() const { return XmlElement{nullptr}; } inline XmlElement operator++() { if(name.empty()) { element = element->NextSiblingElement(); } else { element = element->NextSiblingElement(name.data()); } return *this; } inline XmlElement operator*() const { return *this; } inline bool operator!=(const XmlElement& xe) const { return element != xe.element; } inline const char* GetText() const { return element->GetText(); } template inline bool GetValue(T& value) const { if(!element) { value = T{}; return false; } auto text = element->GetText(); if(text == nullptr) { value = T{}; return false; } std::istringstream iss{text}; iss >> value; return true; } template inline T GetValue() const { T value{}; GetValue(value); return value; } template inline void Attribute(const std::string& name, T& value) const { std::istringstream iss{element->Attribute(name.data())}; iss >> value; } template inline T Attribute(const std::string& name) const { T value{}; Attribute(name, value); return value; } inline XmlElement FirstChildElement(const std::string& childName) const { return XmlElement{element->FirstChildElement(childName.data())}; } private: tinyxml2::XMLElement* element; std::string name; }; /** * Structure that holds an xml attribute: a name and a value. */ struct XmlAttr { template XmlAttr(std::string name_, const T& val) : name(std::move(name_)) { std::ostringstream oss; oss << val; value = oss.str(); } std::string name; std::string value; }; /** * Creates a new xml element. * @param doc Xml document in which the xml element is to be inserted. * @param name Tag name of the xml element. * @param text Contents. * @param attributes Xml attributes. * @return */ template tinyxml2::XMLElement* CreateXmlElement(tinyxml2::XMLDocument& doc, const std::string name, const T& text = "", const std::vector& attributes = {}) { namespace xml = tinyxml2; xml::XMLElement* element = doc.NewElement(name.data()); std::ostringstream oss; oss << text; if(!oss.str().empty()) { element->SetText(oss.str().data()); } for(const auto& itAttr : attributes) { element->SetAttribute(itAttr.name.data(), itAttr.value.data()); } return element; } } #endif /* SOURCE_UTIL_XML_H_ */ libeXaDrums-0.6.0/Util/Zip.h000066400000000000000000000122251377264061000155740ustar00rootroot00000000000000#ifndef SOURCE_UTIL_ZIP_H_ #define SOURCE_UTIL_ZIP_H_ #include #include #include #include #include #include #include #include #if __has_include() #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif namespace Util { /** * @brief Create a zip file of a directory * * @param dir Directory to zip. * @param fileName Destination file: if the file's directory doesn't exist nothing happens. * * @return true if the zip file has been created successfully * @return false if the zip file's folder doesn't exist (not file created) */ inline bool ZipDir(const std::string& dir, const std::string& fileName) { const auto filePath = fs::path{fileName}; const auto absFilePath = fs::absolute(filePath).parent_path(); if(!fs::exists(absFilePath)) { return false; } auto zf = zipOpen(fileName.data(), APPEND_STATUS_CREATE); for(const fs::path& path : fs::recursive_directory_iterator(dir)) { if(fs::is_regular_file(path)) { std::fstream file(path.c_str(), std::ios::binary | std::ios::in); // Check file validity if(!file.good()) { continue; } std::vector buffer(fs::file_size(path)); // Read file contents file.read(buffer.data(), buffer.size()); // Get filename and directory structure const auto dirName = fs::path{dir}.filename(); const auto dirIt = std::find_if(path.begin(), path.end(), [&](const auto& d) { return d.c_str() == dirName; }); const auto zipFilePath = std::accumulate(std::next(dirIt), path.end(), fs::path{}, std::divides()); // Zip file zip_fileinfo zfi{}; if(Z_OK == zipOpenNewFileInZip(zf, zipFilePath.c_str(), &zfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION)) { zipWriteInFileInZip(zf, buffer.data(), buffer.size()); zipCloseFileInZip(zf); } } } zipClose(zf, NULL); return true; } /** * @brief Unzip a compressed directory into a given folder. * * @param zipFile Zip file location * @param outputDir Output directory (where the file is extracted). * @param replace Whether to replace existing files or not. * @return true File unzipped successfully. * @return false Could not unzip file (invalid file, or the file doesn't exist). */ inline bool UnzipDir(const std::string& zipFile, const std::string& outputDir, bool replace) { // Open the zip file unzFile zipfile = unzOpen(zipFile.data()); if(zipfile == NULL) { return false; } // Get info about the zip file unz_global_info global_info; unzGetGlobalInfo(zipfile, &global_info); // Loop to extract all files for(size_t i = 0; i < global_info.number_entry; ++i) { // Get info about current file. unz_file_info file_info; std::string filename(256, '\0'); unzGetCurrentFileInfo(zipfile, &file_info, (char*)filename.data(), filename.size(), NULL, 0, NULL, 0); // Update string's size to match its length filename = filename.data(); //std::cout << "File name = " << filename << std::endl; //std::cout << "Size uncompressed = " << file_info.uncompressed_size << std::endl; // Create parent directories of the file fs::path filePath{filename}; fs::create_directories(fs::path{outputDir} / filePath.parent_path()); const auto outputPath = fs::path{outputDir} / fs::path{filename}; try { const auto outFileExists = fs::exists(outputPath); if(outFileExists && !replace) { // Go the the next entry listed in the zip file. unzGoToNextFile(zipfile); continue; } } catch(const fs::filesystem_error& e) { std::cerr << e.what() << '\n'; return false; } // Select file unzOpenCurrentFile(zipfile); // Create buffer to hold data std::vector buffer(file_info.uncompressed_size); unzReadCurrentFile(zipfile, buffer.data(), buffer.size()); // Write to file std::ofstream file(outputPath.string(), std::ios::out | std::ios::binary); file.write(buffer.data(), buffer.size()); unzCloseCurrentFile(zipfile); // Go the the next entry listed in the zip file. unzGoToNextFile(zipfile); } unzClose(zipfile); return true; } } #endif libeXaDrums-0.6.0/bootstrap000077500000000000000000000000571377264061000157070ustar00rootroot00000000000000#!/bin/sh set -e autoreconf --force --install libeXaDrums-0.6.0/configure.ac000066400000000000000000000015661377264061000162400ustar00rootroot00000000000000# Process this file with autoconf to produce configure. # Copyright (C) 2018-2021 Jérémy Oden # Copyright (C) 2018-2021 Nicolas Boulenguez # https://www.gnu.org/software/autoconf/manual/autoconf.html AC_INIT([libeXaDrums], [0.6.0], [Jérémy Oden ], [], [https://exadrums.com]) AC_CONFIG_SRCDIR([Api/eXaDrums.cpp]) AC_PROG_CXX AC_CONFIG_MACRO_DIRS([m4]) # https://www.gnu.org/software/automake/manual/automake.html AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AM_PROG_AR # https://www.gnu.org/software/libtool/manual/libtool.html LT_INIT # https://autotools.io/pkgconfig/pkg_check_modules.html PKG_CHECK_MODULES([alsa], [alsa]) PKG_CHECK_MODULES([tinyxml2], [tinyxml2]) PKG_CHECK_MODULES([minizip], [minizip]) AC_CONFIG_FILES([ Makefile Api/Version.h ]) AC_OUTPUT libeXaDrums-0.6.0/exadrums.pc.in000066400000000000000000000010071377264061000165210ustar00rootroot00000000000000# https://people.freedesktop.org/~dbn/pkg-config-guide.html Name: @PACKAGE_NAME@ Description: The eXaDrums software drum module library URL: @PACKAGE_URL@ Version: @PACKAGE_VERSION@ # When a compilation needs the library headers: Cflags: -I@includedir@ # When linking against the shared library: Libs: -L@libdir@ -lexadrums # When linking against the static archive: Libs.private: # Let pkg-config extend Cflags and Libs. Requires: # Let pkg-config extend Cflags and Libs.private. Requires.private: alsa tinyxml2