Merge branch 'master' into develop

This commit is contained in:
paulfd 2019-09-28 14:06:34 +02:00
commit b946170b7b
9 changed files with 133 additions and 25 deletions

2
.gitignore vendored
View file

@ -16,5 +16,7 @@ compile_commands.json
*.a
*.txt.user
.DS_Store
clients/sfizz_jack
clients/sfzprint

View file

@ -14,8 +14,8 @@ matrix:
env:
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
- os: osx
osx_image: xcode10.1
# - os: osx
# osx_image: xcode10.1
before_install:
- sh ${TRAVIS_BUILD_DIR}/.travis/before_install.sh

View file

@ -7,10 +7,10 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
gcc -v && g++ -v && cmake --version && /usr/local/bin/cmake --version && $SHELL --version
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)
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
mkdir build && cd build
cmake -D CMAKE_BUILD_TYPE=Release -D SFIZZ_CLIENTS=ON -G Xcode ..
xcodebuild -project sfizz.xcodeproj -alltargets -configuration Release build
cmake -D SFIZZ_CLIENTS=OFF -G Xcode .. # FIXME: client build
xcodebuild -project sfizz.xcodeproj -alltargets -configuration Debug build
fi

View file

@ -1,7 +1,10 @@
cmake_minimum_required(VERSION 3.13)
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
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)
file(COPY "${WIN_SNDFILE_PATH}/bin/libsndfile-1.dll" DESTINATION ${CMAKE_BINARY_DIR})
target_include_directories(sndfile INTERFACE "${WIN_SNDFILE_PATH}/include")
elseif (APPLE)
add_library(sndfile STATIC IMPORTED)
set_target_properties(sndfile PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(sndfile INTERFACE "usr/local/include")
elseif(APPLE)
find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.hh PATHS "/usr/local/include")
find_library(SNDFILE_LIBRARY NAMES sndfile PATHS "/usr/local/lib")
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()
add_subdirectory(sfizz)

View file

@ -6,5 +6,14 @@ target_link_libraries(sfzprint sfizz::parser absl::flags_parse)
###############################
# 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)
target_link_libraries(sfizz_jack sfizz::sfizz jack absl::flags_parse)

60
sfizz/AtomicGuard.h Normal file
View 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;
};
}

View file

@ -160,4 +160,5 @@ private:
pointer _alignedEnd { nullptr };
LEAK_DETECTOR(Buffer);
};
}

View file

@ -14,7 +14,9 @@ set(SFIZZ_SOURCES
include(CheckIncludeFiles)
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_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
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)
add_library(sfizz::parser ALIAS sfizz_parser)
add_library(sfizz::sfizz ALIAS sfizz)
add_library(sfizz::sfizz ALIAS sfizz)

View file

@ -22,9 +22,10 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Synth.h"
#include "MidiState.h"
#include "AtomicGuard.h"
#include "Config.h"
#include "Debug.h"
#include "MidiState.h"
#include "ScopedFTZ.h"
#include "StringViewHelpers.h"
#include "absl/algorithm/container.h"
@ -139,17 +140,20 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
{
for (auto& member : members) {
switch (hash(member.opcode)) {
case hash("Set_cc"): [[fallthrough]];
case hash("Set_cc"):
[[fallthrough]];
case hash("set_cc"):
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
setValueFromOpcode(member, midiState.cc[*member.parameter], Default::ccRange);
break;
case hash("Label_cc"): [[fallthrough]];
case hash("Label_cc"):
[[fallthrough]];
case hash("label_cc"):
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
ccNames.emplace_back(*member.parameter, member.value);
break;
case hash("Default_path"): [[fallthrough]];
case hash("Default_path"):
[[fallthrough]];
case hash("default_path"): {
auto stringPath = std::string(member.value.begin(), member.value.end());
auto newPath = fs::path(stringPath);
@ -184,7 +188,7 @@ void addEndpointsToVelocityCurve(sfz::Region& region)
bool sfz::Synth::loadSfzFile(const fs::path& filename)
{
canEnterCallback = false;
AtomicDisabler callbackDisabler { canEnterCallback };
while (inCallback) {
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.");
regions.resize(std::distance(regions.begin(), lastRegion) + 1);
canEnterCallback = true;
return parserReturned;
}
@ -261,12 +264,12 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept
// Find voices that can be stolen
DBG("No free voice, trying to steal");
voiceViewArray.clear();
for (auto& voice: voices)
for (auto& voice : voices)
if (voice->canBeStolen())
voiceViewArray.push_back(voice.get());
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());
if (voice->getMeanSquaredAverage() < config::voiceStealingThreshold) {
DBG("Stealing voice...");
@ -297,6 +300,11 @@ void sfz::Synth::garbageCollect() noexcept
void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
{
AtomicDisabler callbackDisabler { canEnterCallback };
while (inCallback) {
std::this_thread::sleep_for(1ms);
}
this->samplesPerBlock = samplesPerBlock;
this->tempBuffer.resize(samplesPerBlock);
for (auto& voice : voices)
@ -305,6 +313,11 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
void sfz::Synth::setSampleRate(float sampleRate) noexcept
{
AtomicDisabler callbackDisabler { canEnterCallback };
while (inCallback) {
std::this_thread::sleep_for(1ms);
}
this->sampleRate = sampleRate;
for (auto& voice : voices)
voice->setSampleRate(sampleRate);
@ -314,18 +327,17 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
{
ScopedFTZ ftz;
buffer.fill(0.0f);
if (!canEnterCallback)
return;
inCallback = true;
AtomicGuard callbackGuard { inCallback };
auto tempSpan = AudioSpan<float>(tempBuffer).first(buffer.getNumFrames());
for (auto& voice : voices) {
voice->renderBlock(tempSpan);
buffer.add(tempSpan);
}
inCallback = false;
}
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);
midiState.noteOn(noteNumber, velocity);
if (!canEnterCallback)
return;
AtomicGuard callbackGuard { inCallback };
auto randValue = randNoteDistribution(Random::randomGenerator);
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 >= 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?
// auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity);
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 >= 0);
if (!canEnterCallback)
return;
AtomicGuard callbackGuard { inCallback };
for (auto& voice : voices)
voice->registerCC(delay, channel, ccNumber, ccValue);