Jack is working now :) Almost there...
This commit is contained in:
parent
215a5fdb57
commit
63beae0b2a
7 changed files with 386 additions and 31 deletions
|
|
@ -133,7 +133,7 @@ if(UNIX)
|
||||||
target_compile_options(sfizz PRIVATE -fno-rtti -fno-exceptions)
|
target_compile_options(sfizz PRIVATE -fno-rtti -fno-exceptions)
|
||||||
target_link_libraries(sfizz stdc++fs)
|
target_link_libraries(sfizz stdc++fs)
|
||||||
endif(UNIX)
|
endif(UNIX)
|
||||||
target_link_libraries(sfizz absl::strings sndfile absl::flat_hash_map readerwriterqueue absl::flags_parse)
|
target_link_libraries(sfizz jack absl::strings sndfile absl::flat_hash_map readerwriterqueue absl::flags_parse)
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Test application
|
# Test application
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace config
|
||||||
{
|
{
|
||||||
constexpr double defaultSampleRate { 48000 };
|
constexpr double defaultSampleRate { 48000 };
|
||||||
constexpr int defaultSamplesPerBlock { 1024 };
|
constexpr int defaultSamplesPerBlock { 1024 };
|
||||||
constexpr int preloadSize { 32768 };
|
constexpr int preloadSize { 8192 };
|
||||||
constexpr int numChannels { 2 };
|
constexpr int numChannels { 2 };
|
||||||
constexpr int numVoices { 64 };
|
constexpr int numVoices { 64 };
|
||||||
constexpr int numLoadingThreads { 4 };
|
constexpr int numLoadingThreads { 4 };
|
||||||
|
|
|
||||||
207
sources/Main.cpp
207
sources/Main.cpp
|
|
@ -1,15 +1,146 @@
|
||||||
|
#include "StereoSpan.h"
|
||||||
#include "Synth.h"
|
#include "Synth.h"
|
||||||
#include <iostream>
|
|
||||||
#include <absl/flags/parse.h>
|
#include <absl/flags/parse.h>
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <bits/stdint-uintn.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <ios>
|
||||||
|
#include <iostream>
|
||||||
|
#include <jack/jack.h>
|
||||||
|
#include <jack/midiport.h>
|
||||||
|
#include <jack/types.h>
|
||||||
|
#include <ostream>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
static jack_port_t* midiInputPort;
|
||||||
|
static jack_port_t* outputPort1;
|
||||||
|
static jack_port_t* outputPort2;
|
||||||
|
static jack_client_t* client;
|
||||||
|
|
||||||
|
namespace midi {
|
||||||
|
constexpr uint8_t statusMask { 0b11110000 };
|
||||||
|
constexpr uint8_t channelMask { 0b00001111 };
|
||||||
|
constexpr uint8_t noteOff { 0x80 };
|
||||||
|
constexpr uint8_t noteOn { 0x90 };
|
||||||
|
constexpr uint8_t polyphonicPressure { 0xA0 };
|
||||||
|
constexpr uint8_t controlChange { 0xB0 };
|
||||||
|
constexpr uint8_t programChange { 0xC0 };
|
||||||
|
constexpr uint8_t channelPressure { 0xD0 };
|
||||||
|
constexpr uint8_t pitchBend { 0xE0 };
|
||||||
|
constexpr uint8_t systemMessage { 0xF0 };
|
||||||
|
|
||||||
|
constexpr uint8_t status(uint8_t midiStatusByte)
|
||||||
|
{
|
||||||
|
return midiStatusByte & statusMask;
|
||||||
|
}
|
||||||
|
constexpr uint8_t channel(uint8_t midiStatusByte)
|
||||||
|
{
|
||||||
|
return midiStatusByte & channelMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::atomic<bool> keepRunning [[maybe_unused]] { true };
|
||||||
|
|
||||||
|
int process(jack_nframes_t numFrames, void* arg [[maybe_unused]])
|
||||||
|
{
|
||||||
|
auto synth = reinterpret_cast<sfz::Synth*>(arg);
|
||||||
|
|
||||||
|
auto buffer = jack_port_get_buffer(midiInputPort, numFrames);
|
||||||
|
assert(buffer);
|
||||||
|
|
||||||
|
auto numMidiEvents = jack_midi_get_event_count(buffer);
|
||||||
|
jack_midi_event_t event;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numMidiEvents; ++i) {
|
||||||
|
if (jack_midi_event_get(&event, buffer, i) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (event.size == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (midi::status(event.buffer[0])) {
|
||||||
|
case midi::noteOff:
|
||||||
|
DBG("[MIDI] Note " << event.buffer[1] << " OFF at time " << event.time);
|
||||||
|
synth->noteOff(event.time, midi::channel(event.buffer[0]), event.buffer[1], event.buffer[2]);
|
||||||
|
break;
|
||||||
|
case midi::noteOn:
|
||||||
|
DBG("[MIDI] Note " << event.buffer[1] << " ON at time " << event.time);
|
||||||
|
synth->noteOn(event.time, midi::channel(event.buffer[0]), event.buffer[1], event.buffer[2]);
|
||||||
|
break;
|
||||||
|
case midi::polyphonicPressure:
|
||||||
|
DBG("[MIDI] Polyphonic pressure on at time " << event.time);
|
||||||
|
break;
|
||||||
|
case midi::controlChange:
|
||||||
|
DBG("[MIDI] CC " << event.buffer[1] << " at time " << event.time);
|
||||||
|
synth->cc(event.time, midi::channel(event.buffer[0]), event.buffer[1], event.buffer[2]);
|
||||||
|
break;
|
||||||
|
case midi::programChange:
|
||||||
|
DBG("[MIDI] Program change at time " << event.time);
|
||||||
|
break;
|
||||||
|
case midi::channelPressure:
|
||||||
|
DBG("[MIDI] Channel pressure at time " << event.time);
|
||||||
|
break;
|
||||||
|
case midi::pitchBend:
|
||||||
|
DBG("[MIDI] Pitch bend at time " << event.time);
|
||||||
|
break;
|
||||||
|
case midi::systemMessage:
|
||||||
|
DBG("[MIDI] System message at time " << event.time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan<float> output {
|
||||||
|
jack_port_get_buffer(outputPort1, numFrames),
|
||||||
|
jack_port_get_buffer(outputPort2, numFrames),
|
||||||
|
numFrames
|
||||||
|
};
|
||||||
|
|
||||||
|
synth->renderBlock(output);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sampleBlockChanged(jack_nframes_t nframes, void* arg [[maybe_unused]])
|
||||||
|
{
|
||||||
|
if (arg == nullptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto synth = reinterpret_cast<sfz::Synth*>(arg);
|
||||||
|
DBG("Sample per block changed to " << nframes);
|
||||||
|
synth->setSamplesPerBlock(nframes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sampleRateChanged(jack_nframes_t nframes, void* arg [[maybe_unused]])
|
||||||
|
{
|
||||||
|
if (arg == nullptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto synth = reinterpret_cast<sfz::Synth*>(arg);
|
||||||
|
DBG("Sample rate changed to " << nframes);
|
||||||
|
synth->setSampleRate(nframes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void done(int sig [[maybe_unused]])
|
||||||
|
{
|
||||||
|
std::cout << "Closing..." << '\n';
|
||||||
|
if (client != nullptr)
|
||||||
|
jack_client_close(client);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
std::ios::sync_with_stdio(false);
|
// std::ios::sync_with_stdio(false);
|
||||||
auto arguments = absl::ParseCommandLine(argc, argv);
|
auto arguments = absl::ParseCommandLine(argc, argv);
|
||||||
auto filesToParse = absl::MakeConstSpan(arguments).subspan(1);
|
auto filesToParse = absl::MakeConstSpan(arguments).subspan(1);
|
||||||
std::cout << "Positional arguments:";
|
std::cout << "Positional arguments:";
|
||||||
for (auto& file: filesToParse)
|
for (auto& file : filesToParse)
|
||||||
std::cout << " " << file << ',';
|
std::cout << " " << file << ',';
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
|
|
||||||
|
|
@ -25,16 +156,80 @@ int main(int argc, char** argv)
|
||||||
std::cout << "\tPreloadedSamples: " << synth.getNumPreloadedSamples() << '\n';
|
std::cout << "\tPreloadedSamples: " << synth.getNumPreloadedSamples() << '\n';
|
||||||
std::cout << "==========" << '\n';
|
std::cout << "==========" << '\n';
|
||||||
std::cout << "Included files:" << '\n';
|
std::cout << "Included files:" << '\n';
|
||||||
for (auto& file: synth.getIncludedFiles())
|
for (auto& file : synth.getIncludedFiles())
|
||||||
std::cout << '\t' << file.string() << '\n';
|
std::cout << '\t' << file.string() << '\n';
|
||||||
std::cout << "==========" << '\n';
|
std::cout << "==========" << '\n';
|
||||||
std::cout << "Defines:" << '\n';
|
std::cout << "Defines:" << '\n';
|
||||||
for (auto& define: synth.getDefines())
|
for (auto& define : synth.getDefines())
|
||||||
std::cout << '\t' << define.first << '=' << define.second << '\n';
|
std::cout << '\t' << define.first << '=' << define.second << '\n';
|
||||||
std::cout << "==========" << '\n';
|
std::cout << "==========" << '\n';
|
||||||
std::cout << "Unknown opcodes:";
|
std::cout << "Unknown opcodes:";
|
||||||
for (auto& opcode: synth.getUnknownOpcodes())
|
for (auto& opcode : synth.getUnknownOpcodes())
|
||||||
std::cout << opcode << ',';
|
std::cout << opcode << ',';
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
|
// std::cout << std::flush;
|
||||||
|
|
||||||
|
auto defaultName = "sfizz";
|
||||||
|
jack_status_t status;
|
||||||
|
client = jack_client_open(defaultName, JackNullOption, &status);
|
||||||
|
if (client == nullptr) {
|
||||||
|
std::cerr << "Could not open JACK client" << '\n';
|
||||||
|
// if (status & JackFailure)
|
||||||
|
// std::cerr << "JackFailure: Overall operation failed" << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & JackNameNotUnique) {
|
||||||
|
std::cout << "Name was taken: assigned " << jack_get_client_name(client) << "instead" << '\n';
|
||||||
|
}
|
||||||
|
if (status & JackServerStarted) {
|
||||||
|
std::cout << "Connected to JACK" << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
synth.setSamplesPerBlock(jack_get_buffer_size(client));
|
||||||
|
synth.setSampleRate(jack_get_sample_rate(client));
|
||||||
|
|
||||||
|
jack_set_sample_rate_callback(client, sampleRateChanged, &synth);
|
||||||
|
jack_set_buffer_size_callback(client, sampleBlockChanged, &synth);
|
||||||
|
jack_set_process_callback(client, process, &synth);
|
||||||
|
|
||||||
|
midiInputPort = jack_port_register(client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
|
||||||
|
if (midiInputPort == nullptr) {
|
||||||
|
std::cerr << "Could not open MIDI input port" << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputPort1 = jack_port_register(client, "output_1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||||
|
outputPort2 = jack_port_register(client, "output_2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||||
|
if (outputPort1 == nullptr || outputPort2 == nullptr) {
|
||||||
|
std::cerr << "Could not open output ports" << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jack_activate(client) != 0) {
|
||||||
|
std::cerr << "Could not activate client" << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto systemPorts = jack_get_ports(client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput);
|
||||||
|
if (systemPorts == nullptr) {
|
||||||
|
std::cerr << "No physical output ports found" << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jack_connect(client, jack_port_name(outputPort1), systemPorts[0])) {
|
||||||
|
std::cerr << "Cannot connect to physical output ports (0)" << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jack_connect(client, jack_port_name(outputPort2), systemPorts[1])) {
|
||||||
|
std::cerr << "Cannot connect to physical output ports (1)" << '\n';
|
||||||
|
}
|
||||||
|
jack_free(systemPorts);
|
||||||
|
|
||||||
|
signal(SIGHUP, done);
|
||||||
|
signal(SIGINT, done);
|
||||||
|
signal(SIGTERM, done);
|
||||||
|
signal(SIGQUIT, done);
|
||||||
|
sleep(-1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -33,17 +33,23 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Span<const Type> getSpan(Channel channel) const
|
absl::Span<const Type> getConstSpan(Channel channel) const
|
||||||
{
|
{
|
||||||
switch (channel) {
|
switch (channel) {
|
||||||
case Channel::left:
|
case Channel::left:
|
||||||
return leftBuffer;
|
return leftBuffer;
|
||||||
case Channel::right:
|
case Channel::right:
|
||||||
return rightBuffer;
|
return rightBuffer;
|
||||||
// Should not be here by construction...
|
}
|
||||||
default:
|
}
|
||||||
ASSERTFALSE;
|
|
||||||
return {};
|
absl::Span<Type> getSpan(Channel channel)
|
||||||
|
{
|
||||||
|
switch (channel) {
|
||||||
|
case Channel::left:
|
||||||
|
return absl::MakeSpan(leftBuffer);
|
||||||
|
case Channel::right:
|
||||||
|
return absl::MakeSpan(rightBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,10 +61,6 @@ public:
|
||||||
return leftBuffer[sampleIndex];
|
return leftBuffer[sampleIndex];
|
||||||
case Channel::right:
|
case Channel::right:
|
||||||
return rightBuffer[sampleIndex];
|
return rightBuffer[sampleIndex];
|
||||||
// Should not be here by construction...
|
|
||||||
default:
|
|
||||||
ASSERTFALSE;
|
|
||||||
return trash;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,9 +154,6 @@ private:
|
||||||
static constexpr auto TypeAlignmentMask { TypeAlignment - 1 };
|
static constexpr auto TypeAlignmentMask { TypeAlignment - 1 };
|
||||||
static_assert(TypeAlignment * sizeof(Type) == Alignment, "The alignment does not appear to be divided by the size of the Type");
|
static_assert(TypeAlignment * sizeof(Type) == Alignment, "The alignment does not appear to be divided by the size of the Type");
|
||||||
int numFrames { 0 };
|
int numFrames { 0 };
|
||||||
int totalSize { 0 };
|
|
||||||
int padding { 0 };
|
|
||||||
Buffer<Type, Alignment> leftBuffer {};
|
Buffer<Type, Alignment> leftBuffer {};
|
||||||
Buffer<Type, Alignment> rightBuffer {};
|
Buffer<Type, Alignment> rightBuffer {};
|
||||||
Type trash { 0 };
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
131
sources/StereoSpan.h
Normal file
131
sources/StereoSpan.h
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Helpers.h"
|
||||||
|
#include "SIMDHelpers.h"
|
||||||
|
#include "StereoBuffer.h"
|
||||||
|
#include <absl/types/span.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
template <class Type>
|
||||||
|
class StereoSpan {
|
||||||
|
public:
|
||||||
|
using ValueType = std::remove_cv_t<Type>;
|
||||||
|
template <typename U>
|
||||||
|
StereoSpan() = delete;
|
||||||
|
StereoSpan(Type* leftBuffer, Type* rightBuffer, size_t numFrames)
|
||||||
|
: numFrames(numFrames)
|
||||||
|
, leftBuffer(leftBuffer, numFrames)
|
||||||
|
, rightBuffer(rightBuffer, numFrames)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan(void* leftBuffer, void* rightBuffer, size_t numFrames)
|
||||||
|
: numFrames(numFrames)
|
||||||
|
, leftBuffer(static_cast<Type*>(leftBuffer), numFrames)
|
||||||
|
, rightBuffer(static_cast<Type*>(rightBuffer), numFrames)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan(absl::Span<Type> leftBuffer, absl::Span<Type> rightBuffer)
|
||||||
|
: numFrames(std::min(leftBuffer.size(), rightBuffer.size()))
|
||||||
|
, leftBuffer(leftBuffer.first(numFrames))
|
||||||
|
, rightBuffer(rightBuffer.first(numFrames))
|
||||||
|
{
|
||||||
|
// Buffer really should be the same size here
|
||||||
|
ASSERT(leftBuffer.size() == rightBuffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan(StereoBuffer<const ValueType>& buffer)
|
||||||
|
: leftBuffer(buffer.getConstSpan(Channel::left))
|
||||||
|
, rightBuffer(buffer.getConstSpan(Channel::right))
|
||||||
|
, numFrames(buffer.getNumFrames())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan(StereoBuffer<ValueType>& buffer, size_t numFrames)
|
||||||
|
: numFrames(numFrames)
|
||||||
|
, leftBuffer(buffer.getSpan(Channel::left).first(numFrames))
|
||||||
|
, rightBuffer(buffer.getSpan(Channel::right).first(numFrames))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan(const StereoBuffer<const ValueType>& buffer, size_t numFrames)
|
||||||
|
: numFrames(numFrames)
|
||||||
|
, leftBuffer(buffer.getConstSpan(Channel::left).first(numFrames))
|
||||||
|
, rightBuffer(buffer.getConstSpan(Channel::right).first(numFrames))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan(StereoSpan<ValueType>& span)
|
||||||
|
: numFrames(span.size())
|
||||||
|
, leftBuffer(span.left())
|
||||||
|
, rightBuffer(span.right())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan(const StereoSpan<const ValueType>& span)
|
||||||
|
: numFrames(span.size())
|
||||||
|
, leftBuffer(span.left())
|
||||||
|
, rightBuffer(span.right())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill(Type value) noexcept
|
||||||
|
{
|
||||||
|
::fill<Type>(leftBuffer, value);
|
||||||
|
::fill<Type>(rightBuffer, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void readInterleaved(absl::Span<const Type> input) noexcept
|
||||||
|
{
|
||||||
|
ASSERT(input.size() <= static_cast<size_t>(numChannels * numFrames));
|
||||||
|
::readInterleaved<Type>(input, absl::MakeSpan(leftBuffer), absl::MakeSpan(rightBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeInterleaved(absl::Span<Type> output) noexcept
|
||||||
|
{
|
||||||
|
ASSERT(output.size() >= static_cast<size_t>(numChannels * numFrames));
|
||||||
|
::writeInterleaved<Type>(leftBuffer, rightBuffer, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(StereoSpan<const Type> buffer)
|
||||||
|
{
|
||||||
|
::add<Type>(buffer.left(), leftBuffer);
|
||||||
|
::add<Type>(buffer.right(), rightBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Span<Type> left()
|
||||||
|
{
|
||||||
|
return leftBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Span<Type> right()
|
||||||
|
{
|
||||||
|
return rightBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan<Type> first(size_t length)
|
||||||
|
{
|
||||||
|
return { leftBuffer.first(length), rightBuffer.first(length) };
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan<Type> last(size_t length)
|
||||||
|
{
|
||||||
|
return { leftBuffer.last(length), rightBuffer.last(length) };
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoSpan<Type> subspan(size_t pos, size_t length = absl::Span<Type>::npos)
|
||||||
|
{
|
||||||
|
return { leftBuffer.subspan(pos, length), rightBuffer.subspan(pos, length) };
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size()
|
||||||
|
{
|
||||||
|
return numFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int numChannels { 2 };
|
||||||
|
size_t numFrames { 0 };
|
||||||
|
absl::Span<Type> leftBuffer;
|
||||||
|
absl::Span<Type> rightBuffer;
|
||||||
|
};
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "FilePool.h"
|
#include "FilePool.h"
|
||||||
#include "Parser.h"
|
#include "Parser.h"
|
||||||
#include "Region.h"
|
#include "Region.h"
|
||||||
|
#include "StereoSpan.h"
|
||||||
#include "SfzHelpers.h"
|
#include "SfzHelpers.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
@ -28,20 +29,27 @@ public:
|
||||||
auto getUnknownOpcodes() { return unknownOpcodes; }
|
auto getUnknownOpcodes() { return unknownOpcodes; }
|
||||||
size_t getNumPreloadedSamples() { return filePool.getNumPreloadedSamples(); }
|
size_t getNumPreloadedSamples() { return filePool.getNumPreloadedSamples(); }
|
||||||
|
|
||||||
void prepareToPlay(int samplesPerBlock, double sampleRate)
|
void setSamplesPerBlock(int samplesPerBlock)
|
||||||
{
|
{
|
||||||
|
DBG("[Synth] Samples per block set to " << samplesPerBlock);
|
||||||
this->samplesPerBlock = samplesPerBlock;
|
this->samplesPerBlock = samplesPerBlock;
|
||||||
this->sampleRate = sampleRate;
|
this->tempBuffer.resize(samplesPerBlock);
|
||||||
this->tempBuffer = StereoBuffer<float>(samplesPerBlock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderBlock(StereoBuffer<float>& buffer)
|
void setSampleRate(double sampleRate)
|
||||||
|
{
|
||||||
|
DBG("[Synth] Sample rate set to " << sampleRate);
|
||||||
|
this->sampleRate = sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderBlock(StereoSpan<float> buffer)
|
||||||
{
|
{
|
||||||
buffer.fill(0.0f);
|
buffer.fill(0.0f);
|
||||||
|
|
||||||
|
StereoSpan<float> tempSpan { tempBuffer, buffer.size() };
|
||||||
for (auto& voice : voices) {
|
for (auto& voice : voices) {
|
||||||
voice->renderBlock(tempBuffer);
|
voice->renderBlock(tempSpan);
|
||||||
buffer.add(tempBuffer);
|
buffer.add(tempSpan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +91,22 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void cc(int delay, int channel, int ccNumber, uint8_t ccValue);
|
void cc(int delay, int channel, int ccNumber, uint8_t ccValue)
|
||||||
|
{
|
||||||
|
for (auto& voice : voices)
|
||||||
|
voice->registerCC(delay, channel, ccNumber, ccValue);
|
||||||
|
|
||||||
|
for (auto& region : regions) {
|
||||||
|
if (region->registerCC(channel, ccNumber, ccValue)) {
|
||||||
|
auto voice = findFreeVoice();
|
||||||
|
if (voice == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
voice->startVoice(region.get(), channel, ccNumber, ccValue, Voice::TriggerType::NoteOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pitchWheel(int delay, int channel, int pitch);
|
void pitchWheel(int delay, int channel, int pitch);
|
||||||
void aftertouch(int delay, int channel, uint8_t aftertouch);
|
void aftertouch(int delay, int channel, uint8_t aftertouch);
|
||||||
void tempo(int delay, float secondsPerQuarter);
|
void tempo(int delay, float secondsPerQuarter);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "StereoBuffer.h"
|
#include "StereoBuffer.h"
|
||||||
|
#include "StereoSpan.h"
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "Region.h"
|
#include "Region.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
@ -32,14 +33,20 @@ public:
|
||||||
return (region == nullptr);
|
return (region == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity);
|
void registerNoteOff(int delay [[maybe_unused]], int channel [[maybe_unused]], int noteNumber [[maybe_unused]], uint8_t velocity [[maybe_unused]])
|
||||||
bool registerCC(int delay, int channel, int ccNumber, uint8_t ccValue);
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
void registerCC(int delay [[maybe_unused]], int channel [[maybe_unused]], int ccNumber [[maybe_unused]], uint8_t ccValue [[maybe_unused]])
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
void registerPitchWheel(int delay, int channel, int pitch);
|
void registerPitchWheel(int delay, int channel, int pitch);
|
||||||
void registerAftertouch(int delay, int channel, uint8_t aftertouch);
|
void registerAftertouch(int delay, int channel, uint8_t aftertouch);
|
||||||
void registerTempo(int delay, float secondsPerQuarter);
|
void registerTempo(int delay, float secondsPerQuarter);
|
||||||
|
|
||||||
void prepareToPlay(int samplesPerBlock, double sampleRate);
|
void prepareToPlay(int samplesPerBlock, double sampleRate);
|
||||||
void renderBlock(StereoBuffer<float>& buffer)
|
void renderBlock(StereoSpan<float> buffer)
|
||||||
{
|
{
|
||||||
buffer.fill(0.0f);
|
buffer.fill(0.0f);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue