Added a C++ opaque API for the shared lib

This commit is contained in:
Paul Ferrand 2020-01-23 22:17:06 +01:00
parent 6bf3d78778
commit aef8352cfc
8 changed files with 437 additions and 12 deletions

View file

@ -21,7 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "sfizz.hpp"
#include "sfizz/Synth.h"
#include <absl/flags/parse.h>
#include <absl/flags/flag.h>
#include <absl/types/span.h>

View file

@ -37,12 +37,12 @@ add_library (sfizz::sfizz ALIAS sfizz_static)
# Shared library and installation target
if (SFIZZ_SHARED)
add_library (sfizz_shared SHARED)
target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp)
target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
target_include_directories (sfizz_shared PRIVATE .)
target_include_directories (sfizz_static PRIVATE external)
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads)
target_compile_definitions(sfizz_shared PRIVATE SFIZZ_EXPORT_SYMBOLS)
set_target_properties (sfizz_shared PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER sfizz.h)
set_target_properties (sfizz_shared PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp")
sfizz_link_libsndfile(sfizz_shared)
sfizz_enable_lto_if_needed(sfizz_shared)
configure_file (${PROJECT_SOURCE_DIR}/scripts/sfizz.pc.in sfizz.pc @ONLY)

View file

@ -1,2 +1,249 @@
#include "sfizz/AudioSpan.h"
#include "sfizz/Synth.h"
#include <string>
#include <vector>
#include <memory>
#if defined SFIZZ_EXPORT_SYMBOLS
#if defined _WIN32
#define SFIZZ_EXPORTED_API __declspec(dllexport)
#else
#define SFIZZ_EXPORTED_API __attribute__ ((visibility ("default")))
#endif
#else
#define SFIZZ_EXPORTED_API
#endif
namespace sfz
{
class Synth;
class SFIZZ_EXPORTED_API Sfizz
{
public:
Sfizz();
~Sfizz();
/**
* @brief Empties the current regions and load a new SFZ file into the synth.
*
* This function will disable all callbacks so it is safe to call from a
* UI thread for example, although it may generate a click. However it is
* not reentrant, so you should not call it from concurrent threads.
*
* @param file
* @return true
* @return false if the file was not found or no regions were loaded.
*/
bool loadSfzFile(const std::string& path);
/**
* @brief Get the current number of regions loaded
*
* @return int
*/
int getNumRegions() const noexcept;
/**
* @brief Get the current number of groups loaded
*
* @return int
*/
int getNumGroups() const noexcept;
/**
* @brief Get the current number of masters loaded
*
* @return int
*/
int getNumMasters() const noexcept;
/**
* @brief Get the current number of curves loaded
*
* @return int
*/
int getNumCurves() const noexcept;
const std::vector<std::string>& getUnknownOpcodes() const noexcept;
/**
* @brief Get the number of preloaded samples in the synth
*
* @return size_t
*/
size_t getNumPreloadedSamples() const noexcept;
/**
* @brief Set the maximum size of the blocks for the callback. The actual
* size can be lower in each callback but should not be larger
* than this value.
*
* @param samplesPerBlock
*/
void setSamplesPerBlock(int samplesPerBlock) noexcept;
/**
* @brief Set the sample rate. If you do not call it it is initialized
* to sfz::config::defaultSampleRate.
*
* @param sampleRate
*/
void setSampleRate(float sampleRate) noexcept;
/**
* @brief Get the current value for the volume, in dB.
*
* @return float
*/
float getVolume() const noexcept;
/**
* @brief Set the value for the volume. This value will be
* clamped within sfz::default::volumeRange.
*
* @param volume
*/
void setVolume(float volume) noexcept;
/**
* @brief Send a note on event to the synth
*
* @param delay the delay at which the event occurs; this should be lower
* than the size of the block in the next call to renderBlock().
* @param noteNumber the midi note number
* @param velocity the midi note velocity
*/
void noteOn(int delay, int noteNumber, uint8_t velocity) noexcept;
/**
* @brief Send a note off event to the synth
*
* @param delay the delay at which the event occurs; this should be lower
* than the size of the block in the next call to renderBlock().
* @param noteNumber the midi note number
* @param velocity the midi note velocity
*/
void noteOff(int delay, int noteNumber, uint8_t velocity) noexcept;
/**
* @brief Send a CC event to the synth
*
* @param delay the delay at which the event occurs; this should be lower than the size of
* the block in the next call to renderBlock().
* @param ccNumber the cc number
* @param ccValue the cc value
*/
void cc(int delay, int ccNumber, uint8_t ccValue) noexcept;
/**
* @brief Send a pitch bend event to the synth
*
* @param delay the delay at which the event occurs; this should be lower
* than the size of the block in the next call to
* renderBlock().
* @param pitch the pitch value centered between -8192 and 8192
*/
void pitchWheel(int delay, int pitch) noexcept;
/**
* @brief Send a aftertouch event to the synth
*
* @param delay the delay at which the event occurs; this should be lower than the size of
* the block in the next call to renderBlock().
* @param aftertouch the aftertouch value
*/
void aftertouch(int delay, uint8_t aftertouch) noexcept;
/**
* @brief Send a tempo event to the synth
*
* @param delay the delay at which the event occurs; this should be lower than the size of
* the block in the next call to renderBlock().
* @param secondsPerQuarter the new period of the quarter note
*/
void tempo(int delay, float secondsPerQuarter) noexcept;
/**
* @brief Render an block of audio data in the buffer. This call will reset
* the synth in its waiting state for the next batch of events. The size of
* the block is integrated in the AudioSpan object. You can build an
* AudioSpan implicitely from a large number of source objects; check the
* AudioSpan reference for more precision.
*
* @param buffer the buffer to write the next block into
* @param numSamples the number of samples
*/
void renderBlock(float** buffers, size_t numSamples, int numOutputs) noexcept;
/**
* @brief Get the number of active voices
*
* @return int
*/
int getNumActiveVoices() const noexcept;
/**
* @brief Get the total number of voices in the synth (the polyphony)
*
* @return int
*/
int getNumVoices() const noexcept;
/**
* @brief Change the number of voices (the polyphony)
*
* @param numVoices
*/
void setNumVoices(int numVoices) noexcept;
/**
* @brief Set the oversampling factor to a new value. This will disable all callbacks
* kill all the voices, and trigger a reloading of every file in the FilePool under
* the new oversampling.
*
* @param factor
* @return true if the factor did indeed change
*/
bool setOversamplingFactor(int factor) noexcept;
/**
* @brief get the current oversampling factor
*
* @return Oversampling
*/
int getOversamplingFactor() const noexcept;
/**
* @brief Set the preloaded file size. This will disable the callback.
*
* @param factor
*/
void setPreloadSize(uint32_t preloadSize) noexcept;
/**
* @brief get the current preloaded file size
*
* @return Oversampling
*/
uint32_t getPreloadSize() const noexcept;
/**
* @brief Gets the number of allocated buffers.
*
* @return The allocated buffers.
*/
int getAllocatedBuffers() const noexcept;
/**
* @brief Gets the number of bytes allocated through the buffers
*
* @return The allocated bytes.
*/
int getAllocatedBytes() const noexcept;
/**
* @brief Enable freewheeling on the synth. This will wait for background
* loaded files to finish loading before each render callback to ensure that
* there will be no dropouts.
*
*/
void enableFreeWheeling() noexcept;
/**
* @brief Disable freewheeling on the synth. You should disable freewheeling
* before live use of the plugin otherwise the audio thread will lock.
*
*/
void disableFreeWheeling() noexcept;
/**
* @brief Check if the SFZ should be reloaded.
*
* Depending on the platform this can create file descriptors.
*
* @return true if any included files (including the root file) have
* been modified since the sfz file was loaded.
* @return false
*/
bool shouldReloadFile();
private:
std::unique_ptr<sfz::Synth> synth;
};
}

View file

@ -105,7 +105,7 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
}
if (!lastRegion->parseOpcode(opcode))
unknownOpcodes.insert(opcode.opcode);
unknownOpcodes.emplace_back(opcode.opcode);
}
};
@ -556,7 +556,7 @@ const sfz::Voice* sfz::Synth::getVoiceView(int idx) const noexcept
return (size_t)idx < voices.size() ? voices[idx].get() : nullptr;
}
std::set<absl::string_view> sfz::Synth::getUnknownOpcodes() const noexcept
const std::vector<std::string>& sfz::Synth::getUnknownOpcodes() const noexcept
{
return unknownOpcodes;
}

View file

@ -154,7 +154,7 @@ public:
*
* @return std::set<absl::string_view>
*/
std::set<absl::string_view> getUnknownOpcodes() const noexcept;
const std::vector<std::string>& getUnknownOpcodes() const noexcept;
/**
* @brief Get the number of preloaded samples in the synth
*
@ -432,7 +432,7 @@ private:
std::vector<CCNamePair> ccNames;
// Default active switch if multiple keyswitchable regions are present
absl::optional<uint8_t> defaultSwitch;
std::set<absl::string_view> unknownOpcodes;
std::vector<std::string> unknownOpcodes;
using RegionPtrVector = std::vector<Region*>;
using VoicePtrVector = std::vector<Voice*>;
std::vector<std::unique_ptr<Region>> regions;

180
src/sfizz/sfizz.cpp Normal file
View file

@ -0,0 +1,180 @@
#include "Synth.h"
#include "sfizz.hpp"
sfz::Sfizz::Sfizz()
{
synth = std::make_unique<sfz::Synth>();
}
sfz::Sfizz::~Sfizz()
{
}
bool sfz::Sfizz::loadSfzFile(const std::string& path)
{
return synth->loadSfzFile(path);
}
int sfz::Sfizz::getNumRegions() const noexcept
{
return synth->getNumRegions();
}
int sfz::Sfizz::getNumGroups() const noexcept
{
return synth->getNumGroups();
}
int sfz::Sfizz::getNumMasters() const noexcept
{
return synth->getNumMasters();
}
int sfz::Sfizz::getNumCurves() const noexcept
{
return synth->getNumCurves();
}
const std::vector<std::string>& sfz::Sfizz::getUnknownOpcodes() const noexcept
{
return synth->getUnknownOpcodes();
}
size_t sfz::Sfizz::getNumPreloadedSamples() const noexcept
{
return synth->getNumPreloadedSamples();
}
void sfz::Sfizz::setSamplesPerBlock(int samplesPerBlock) noexcept
{
synth->setSamplesPerBlock(samplesPerBlock);
}
void sfz::Sfizz::setSampleRate(float sampleRate) noexcept
{
synth->setSampleRate(sampleRate);
}
float sfz::Sfizz::getVolume() const noexcept
{
return synth->getVolume();
}
void sfz::Sfizz::setVolume(float volume) noexcept
{
synth->setVolume(volume);
}
void sfz::Sfizz::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
{
synth->noteOn(delay, noteNumber, velocity);
}
void sfz::Sfizz::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
{
synth->noteOff(delay, noteNumber, velocity);
}
void sfz::Sfizz::cc(int delay, int ccNumber, uint8_t ccValue) noexcept
{
synth->cc(delay, ccNumber, ccValue);
}
void sfz::Sfizz::pitchWheel(int delay, int pitch) noexcept
{
synth->pitchWheel(delay, pitch);
}
void sfz::Sfizz::aftertouch(int delay, uint8_t aftertouch) noexcept
{
synth->aftertouch(delay, aftertouch);
}
void sfz::Sfizz::tempo(int delay, float secondsPerQuarter) noexcept
{
synth->tempo(delay, secondsPerQuarter);
}
void sfz::Sfizz::renderBlock(float** buffers, size_t numSamples, int /*numOutputs*/) noexcept
{
synth->renderBlock({{buffers[0], buffers[1]}, numSamples});
}
int sfz::Sfizz::getNumActiveVoices() const noexcept
{
return synth->getNumActiveVoices();
}
int sfz::Sfizz::getNumVoices() const noexcept
{
return synth->getNumVoices();
}
void sfz::Sfizz::setNumVoices(int numVoices) noexcept
{
synth->setNumVoices(numVoices);
}
bool sfz::Sfizz::setOversamplingFactor(int factor) noexcept
{
using sfz::Oversampling;
switch(factor)
{
case 1:
synth->setOversamplingFactor(sfz::Oversampling::x1);
return true;
case 2:
synth->setOversamplingFactor(sfz::Oversampling::x2);
return true;
case 4:
synth->setOversamplingFactor(sfz::Oversampling::x4);
return true;
case 8:
synth->setOversamplingFactor(sfz::Oversampling::x8);
return true;
default:
return false;
}
}
int sfz::Sfizz::getOversamplingFactor() const noexcept
{
return static_cast<int>(synth->getOversamplingFactor());
}
void sfz::Sfizz::setPreloadSize(uint32_t preloadSize) noexcept
{
synth->setPreloadSize(preloadSize);
}
uint32_t sfz::Sfizz::getPreloadSize() const noexcept
{
return synth->getPreloadSize();
}
int sfz::Sfizz::getAllocatedBuffers() const noexcept
{
return synth->getAllocatedBuffers();
}
int sfz::Sfizz::getAllocatedBytes() const noexcept
{
return synth->getAllocatedBytes();
}
void sfz::Sfizz::enableFreeWheeling() noexcept
{
synth->enableFreeWheeling();
}
void sfz::Sfizz::disableFreeWheeling() noexcept
{
synth->disableFreeWheeling();
}
bool sfz::Sfizz::shouldReloadFile()
{
return synth->shouldReloadFile();
}

View file

@ -1,2 +0,0 @@
#include "sfizz/Parser.h"
#include "sfizz/Opcode.h"

View file

@ -21,7 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "sfizz.hpp"
#include "sfizz/Synth.h"
#include "catch2/catch.hpp"
using namespace Catch::literals;