Merge branch 'master' into develop
This commit is contained in:
commit
b946170b7b
9 changed files with 133 additions and 25 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -16,5 +16,7 @@ compile_commands.json
|
||||||
*.a
|
*.a
|
||||||
*.txt.user
|
*.txt.user
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
clients/sfizz_jack
|
clients/sfizz_jack
|
||||||
clients/sfzprint
|
clients/sfzprint
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ matrix:
|
||||||
env:
|
env:
|
||||||
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
|
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
|
||||||
|
|
||||||
- os: osx
|
# - os: osx
|
||||||
osx_image: xcode10.1
|
# osx_image: xcode10.1
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- sh ${TRAVIS_BUILD_DIR}/.travis/before_install.sh
|
- sh ${TRAVIS_BUILD_DIR}/.travis/before_install.sh
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||||
gcc -v && g++ -v && cmake --version && /usr/local/bin/cmake --version && $SHELL --version
|
gcc -v && g++ -v && cmake --version && /usr/local/bin/cmake --version && $SHELL --version
|
||||||
|
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
/usr/local/bin/cmake -D CMAKE_BUILD_TYPE=Release -D SFIZZ_CLIENTS=ON ..
|
/usr/local/bin/cmake -D CMAKE_BUILD_TYPE=Debug -D SFIZZ_CLIENTS=ON ..
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake -D CMAKE_BUILD_TYPE=Release -D SFIZZ_CLIENTS=ON -G Xcode ..
|
cmake -D SFIZZ_CLIENTS=OFF -G Xcode .. # FIXME: client build
|
||||||
xcodebuild -project sfizz.xcodeproj -alltargets -configuration Release build
|
xcodebuild -project sfizz.xcodeproj -alltargets -configuration Debug build
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(sfizz VERSION 1.0.0 LANGUAGES CXX)
|
project(sfizz VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
# Do not override if set to more than 14
|
||||||
|
if (NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 14)
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Enable LTO
|
# Enable LTO
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # To override the policy in abseil and benchmark
|
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # To override the policy in abseil and benchmark
|
||||||
|
|
@ -67,10 +70,13 @@ if (WIN32)
|
||||||
set_target_properties(sndfile PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(sndfile PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
file(COPY "${WIN_SNDFILE_PATH}/bin/libsndfile-1.dll" DESTINATION ${CMAKE_BINARY_DIR})
|
file(COPY "${WIN_SNDFILE_PATH}/bin/libsndfile-1.dll" DESTINATION ${CMAKE_BINARY_DIR})
|
||||||
target_include_directories(sndfile INTERFACE "${WIN_SNDFILE_PATH}/include")
|
target_include_directories(sndfile INTERFACE "${WIN_SNDFILE_PATH}/include")
|
||||||
elseif (APPLE)
|
elseif(APPLE)
|
||||||
add_library(sndfile STATIC IMPORTED)
|
find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.hh PATHS "/usr/local/include")
|
||||||
set_target_properties(sndfile PROPERTIES LINKER_LANGUAGE CXX)
|
find_library(SNDFILE_LIBRARY NAMES sndfile PATHS "/usr/local/lib")
|
||||||
target_include_directories(sndfile INTERFACE "usr/local/include")
|
message(STATUS "include dir: " ${SNDFILE_INCLUDE_DIR} " lib: " ${SNDFILE_LIBRARY})
|
||||||
|
add_library(sndfile SHARED IMPORTED GLOBAL)
|
||||||
|
target_include_directories(sndfile INTERFACE ${SNDFILE_INCLUDE_DIR})
|
||||||
|
target_link_libraries(sndfile INTERFACE ${SNDFILE_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(sfizz)
|
add_subdirectory(sfizz)
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,14 @@ target_link_libraries(sfzprint sfizz::parser absl::flags_parse)
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Basic command line program
|
# Basic command line program
|
||||||
|
if(APPLE)
|
||||||
|
find_path(JACK_INCLUDE_DIR NAMES jack.h PATHS "/usr/local/include/jack")
|
||||||
|
find_library(JACK_LIBRARY NAMES jack PATHS "/usr/local/lib")
|
||||||
|
message(STATUS "include dir: " ${JACK_INCLUDE_DIR} " lib: " ${JACK_LIBRARY})
|
||||||
|
add_library(jack SHARED IMPORTED GLOBAL)
|
||||||
|
target_include_directories(jack INTERFACE ${JACK_INCLUDE_DIR})
|
||||||
|
target_link_libraries(jack INTERFACE ${JACK_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(sfizz_jack jack_client.cpp)
|
add_executable(sfizz_jack jack_client.cpp)
|
||||||
target_link_libraries(sfizz_jack sfizz::sfizz jack absl::flags_parse)
|
target_link_libraries(sfizz_jack sfizz::sfizz jack absl::flags_parse)
|
||||||
|
|
|
||||||
60
sfizz/AtomicGuard.h
Normal file
60
sfizz/AtomicGuard.h
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) 2019, Paul Ferrand
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
// list of conditions and the following disclaimer.
|
||||||
|
// 2. 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.
|
||||||
|
|
||||||
|
// 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 OWNER 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.
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace sfz
|
||||||
|
{
|
||||||
|
class AtomicGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AtomicGuard() = delete;
|
||||||
|
AtomicGuard(std::atomic<bool>& guard)
|
||||||
|
: guard(guard)
|
||||||
|
{
|
||||||
|
guard = true;
|
||||||
|
}
|
||||||
|
~AtomicGuard()
|
||||||
|
{
|
||||||
|
guard = false;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::atomic<bool>& guard;
|
||||||
|
};
|
||||||
|
class AtomicDisabler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AtomicDisabler() = delete;
|
||||||
|
AtomicDisabler(std::atomic<bool>& allowed)
|
||||||
|
: allowed(allowed)
|
||||||
|
{
|
||||||
|
allowed = false;
|
||||||
|
}
|
||||||
|
~AtomicDisabler()
|
||||||
|
{
|
||||||
|
allowed = true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::atomic<bool>& allowed;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -160,4 +160,5 @@ private:
|
||||||
pointer _alignedEnd { nullptr };
|
pointer _alignedEnd { nullptr };
|
||||||
LEAK_DETECTOR(Buffer);
|
LEAK_DETECTOR(Buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,9 @@ set(SFIZZ_SOURCES
|
||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
|
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
|
||||||
CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H)
|
CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H)
|
||||||
CHECK_INCLUDE_FILES(arm_neon.h HAVE_ARM_NEON_H)
|
if (!APPLE)
|
||||||
|
CHECK_INCLUDE_FILES(arm_neon.h HAVE_ARM_NEON_H)
|
||||||
|
endif()
|
||||||
|
|
||||||
# SIMD checks
|
# SIMD checks
|
||||||
if (HAVE_X86INTRIN_H AND UNIX)
|
if (HAVE_X86INTRIN_H AND UNIX)
|
||||||
|
|
@ -71,4 +73,4 @@ target_link_libraries(sfizz PUBLIC absl::strings)
|
||||||
target_link_libraries(sfizz PRIVATE sndfile absl::flat_hash_map)
|
target_link_libraries(sfizz PRIVATE sndfile absl::flat_hash_map)
|
||||||
|
|
||||||
add_library(sfizz::parser ALIAS sfizz_parser)
|
add_library(sfizz::parser ALIAS sfizz_parser)
|
||||||
add_library(sfizz::sfizz ALIAS sfizz)
|
add_library(sfizz::sfizz ALIAS sfizz)
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,10 @@
|
||||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "Synth.h"
|
#include "Synth.h"
|
||||||
#include "MidiState.h"
|
#include "AtomicGuard.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
|
#include "MidiState.h"
|
||||||
#include "ScopedFTZ.h"
|
#include "ScopedFTZ.h"
|
||||||
#include "StringViewHelpers.h"
|
#include "StringViewHelpers.h"
|
||||||
#include "absl/algorithm/container.h"
|
#include "absl/algorithm/container.h"
|
||||||
|
|
@ -139,17 +140,20 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
||||||
{
|
{
|
||||||
for (auto& member : members) {
|
for (auto& member : members) {
|
||||||
switch (hash(member.opcode)) {
|
switch (hash(member.opcode)) {
|
||||||
case hash("Set_cc"): [[fallthrough]];
|
case hash("Set_cc"):
|
||||||
|
[[fallthrough]];
|
||||||
case hash("set_cc"):
|
case hash("set_cc"):
|
||||||
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
|
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
|
||||||
setValueFromOpcode(member, midiState.cc[*member.parameter], Default::ccRange);
|
setValueFromOpcode(member, midiState.cc[*member.parameter], Default::ccRange);
|
||||||
break;
|
break;
|
||||||
case hash("Label_cc"): [[fallthrough]];
|
case hash("Label_cc"):
|
||||||
|
[[fallthrough]];
|
||||||
case hash("label_cc"):
|
case hash("label_cc"):
|
||||||
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
|
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
|
||||||
ccNames.emplace_back(*member.parameter, member.value);
|
ccNames.emplace_back(*member.parameter, member.value);
|
||||||
break;
|
break;
|
||||||
case hash("Default_path"): [[fallthrough]];
|
case hash("Default_path"):
|
||||||
|
[[fallthrough]];
|
||||||
case hash("default_path"): {
|
case hash("default_path"): {
|
||||||
auto stringPath = std::string(member.value.begin(), member.value.end());
|
auto stringPath = std::string(member.value.begin(), member.value.end());
|
||||||
auto newPath = fs::path(stringPath);
|
auto newPath = fs::path(stringPath);
|
||||||
|
|
@ -184,7 +188,7 @@ void addEndpointsToVelocityCurve(sfz::Region& region)
|
||||||
|
|
||||||
bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
||||||
{
|
{
|
||||||
canEnterCallback = false;
|
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||||
while (inCallback) {
|
while (inCallback) {
|
||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
}
|
}
|
||||||
|
|
@ -247,8 +251,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
||||||
|
|
||||||
DBG("Removed " << regions.size() - std::distance(regions.begin(), lastRegion) - 1 << " out of " << regions.size() << " regions.");
|
DBG("Removed " << regions.size() - std::distance(regions.begin(), lastRegion) - 1 << " out of " << regions.size() << " regions.");
|
||||||
regions.resize(std::distance(regions.begin(), lastRegion) + 1);
|
regions.resize(std::distance(regions.begin(), lastRegion) + 1);
|
||||||
|
|
||||||
canEnterCallback = true;
|
|
||||||
return parserReturned;
|
return parserReturned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,12 +264,12 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept
|
||||||
// Find voices that can be stolen
|
// Find voices that can be stolen
|
||||||
DBG("No free voice, trying to steal");
|
DBG("No free voice, trying to steal");
|
||||||
voiceViewArray.clear();
|
voiceViewArray.clear();
|
||||||
for (auto& voice: voices)
|
for (auto& voice : voices)
|
||||||
if (voice->canBeStolen())
|
if (voice->canBeStolen())
|
||||||
voiceViewArray.push_back(voice.get());
|
voiceViewArray.push_back(voice.get());
|
||||||
absl::c_sort(voices, [](const auto& lhs, const auto& rhs) { return lhs->getSourcePosition() > rhs->getSourcePosition(); });
|
absl::c_sort(voices, [](const auto& lhs, const auto& rhs) { return lhs->getSourcePosition() > rhs->getSourcePosition(); });
|
||||||
|
|
||||||
for (auto* voice: voiceViewArray) {
|
for (auto* voice : voiceViewArray) {
|
||||||
DBG("Average voice power: " << voice->getMeanSquaredAverage());
|
DBG("Average voice power: " << voice->getMeanSquaredAverage());
|
||||||
if (voice->getMeanSquaredAverage() < config::voiceStealingThreshold) {
|
if (voice->getMeanSquaredAverage() < config::voiceStealingThreshold) {
|
||||||
DBG("Stealing voice...");
|
DBG("Stealing voice...");
|
||||||
|
|
@ -297,6 +300,11 @@ void sfz::Synth::garbageCollect() noexcept
|
||||||
|
|
||||||
void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||||
{
|
{
|
||||||
|
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||||
|
while (inCallback) {
|
||||||
|
std::this_thread::sleep_for(1ms);
|
||||||
|
}
|
||||||
|
|
||||||
this->samplesPerBlock = samplesPerBlock;
|
this->samplesPerBlock = samplesPerBlock;
|
||||||
this->tempBuffer.resize(samplesPerBlock);
|
this->tempBuffer.resize(samplesPerBlock);
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
|
|
@ -305,6 +313,11 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||||
|
|
||||||
void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
||||||
{
|
{
|
||||||
|
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||||
|
while (inCallback) {
|
||||||
|
std::this_thread::sleep_for(1ms);
|
||||||
|
}
|
||||||
|
|
||||||
this->sampleRate = sampleRate;
|
this->sampleRate = sampleRate;
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->setSampleRate(sampleRate);
|
voice->setSampleRate(sampleRate);
|
||||||
|
|
@ -314,18 +327,17 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
{
|
{
|
||||||
ScopedFTZ ftz;
|
ScopedFTZ ftz;
|
||||||
buffer.fill(0.0f);
|
buffer.fill(0.0f);
|
||||||
|
|
||||||
if (!canEnterCallback)
|
if (!canEnterCallback)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
inCallback = true;
|
AtomicGuard callbackGuard { inCallback };
|
||||||
|
|
||||||
auto tempSpan = AudioSpan<float>(tempBuffer).first(buffer.getNumFrames());
|
auto tempSpan = AudioSpan<float>(tempBuffer).first(buffer.getNumFrames());
|
||||||
for (auto& voice : voices) {
|
for (auto& voice : voices) {
|
||||||
voice->renderBlock(tempSpan);
|
voice->renderBlock(tempSpan);
|
||||||
buffer.add(tempSpan);
|
buffer.add(tempSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
inCallback = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept
|
void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept
|
||||||
|
|
@ -334,6 +346,12 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
|
||||||
ASSERT(noteNumber >= 0);
|
ASSERT(noteNumber >= 0);
|
||||||
|
|
||||||
midiState.noteOn(noteNumber, velocity);
|
midiState.noteOn(noteNumber, velocity);
|
||||||
|
|
||||||
|
if (!canEnterCallback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AtomicGuard callbackGuard { inCallback };
|
||||||
|
|
||||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||||
|
|
||||||
for (auto& region : noteActivationLists[noteNumber]) {
|
for (auto& region : noteActivationLists[noteNumber]) {
|
||||||
|
|
@ -361,7 +379,12 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
|
||||||
ASSERT(noteNumber < 128);
|
ASSERT(noteNumber < 128);
|
||||||
ASSERT(noteNumber >= 0);
|
ASSERT(noteNumber >= 0);
|
||||||
|
|
||||||
// FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a
|
if (!canEnterCallback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AtomicGuard callbackGuard { inCallback };
|
||||||
|
|
||||||
|
// FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a
|
||||||
// way in sfz to specify that a release trigger should NOT use the note-on velocity?
|
// way in sfz to specify that a release trigger should NOT use the note-on velocity?
|
||||||
// auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity);
|
// auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity);
|
||||||
auto replacedVelocity = midiState.getNoteVelocity(noteNumber);
|
auto replacedVelocity = midiState.getNoteVelocity(noteNumber);
|
||||||
|
|
@ -389,6 +412,11 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc
|
||||||
ASSERT(ccNumber < 128);
|
ASSERT(ccNumber < 128);
|
||||||
ASSERT(ccNumber >= 0);
|
ASSERT(ccNumber >= 0);
|
||||||
|
|
||||||
|
if (!canEnterCallback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AtomicGuard callbackGuard { inCallback };
|
||||||
|
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->registerCC(delay, channel, ccNumber, ccValue);
|
voice->registerCC(delay, channel, ccNumber, ccValue);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue