2019-08-30 00:49:58 +02:00
|
|
|
// 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.
|
|
|
|
|
|
2020-10-06 02:11:03 +02:00
|
|
|
#include "sfizz.hpp"
|
2021-06-22 21:25:39 +02:00
|
|
|
#include "sfizz/import/sfizz_import.h"
|
2020-04-22 10:08:54 +02:00
|
|
|
#include "MidiHelpers.h"
|
2019-08-22 21:11:59 +02:00
|
|
|
#include <absl/flags/parse.h>
|
2019-12-01 21:40:27 +01:00
|
|
|
#include <absl/flags/flag.h>
|
2019-08-22 21:11:59 +02:00
|
|
|
#include <absl/types/span.h>
|
2021-02-01 23:12:29 +01:00
|
|
|
#include <SpinMutex.h>
|
2019-08-24 02:46:40 +02:00
|
|
|
#include <atomic>
|
|
|
|
|
#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>
|
2019-09-16 14:51:17 +02:00
|
|
|
#include <chrono>
|
2019-08-24 02:46:40 +02:00
|
|
|
#include <thread>
|
2021-02-01 23:12:29 +01:00
|
|
|
#include <mutex>
|
|
|
|
|
#include <algorithm>
|
2021-08-20 03:31:31 +02:00
|
|
|
#include <vector>
|
2019-08-24 02:46:40 +02:00
|
|
|
|
2021-08-19 20:06:37 +02:00
|
|
|
sfz::Sfizz synth;
|
|
|
|
|
|
2019-08-24 02:46:40 +02:00
|
|
|
static jack_port_t* midiInputPort;
|
|
|
|
|
static jack_port_t* outputPort1;
|
|
|
|
|
static jack_port_t* outputPort2;
|
|
|
|
|
static jack_client_t* client;
|
2021-02-01 23:12:29 +01:00
|
|
|
static SpinMutex processMutex;
|
2019-08-24 02:46:40 +02:00
|
|
|
|
2020-03-11 00:18:18 +01:00
|
|
|
int process(jack_nframes_t numFrames, void* arg)
|
2019-08-24 02:46:40 +02:00
|
|
|
{
|
2020-10-06 02:11:03 +02:00
|
|
|
auto* synth = reinterpret_cast<sfz::Sfizz*>(arg);
|
2019-08-24 02:46:40 +02:00
|
|
|
|
2020-10-06 02:11:03 +02:00
|
|
|
auto* buffer = jack_port_get_buffer(midiInputPort, numFrames);
|
2019-08-24 02:46:40 +02:00
|
|
|
assert(buffer);
|
|
|
|
|
|
2021-02-01 23:12:29 +01:00
|
|
|
auto* leftOutput = reinterpret_cast<float*>(jack_port_get_buffer(outputPort1, numFrames));
|
|
|
|
|
auto* rightOutput = reinterpret_cast<float*>(jack_port_get_buffer(outputPort2, numFrames));
|
|
|
|
|
|
|
|
|
|
std::unique_lock<SpinMutex> lock { processMutex, std::try_to_lock };
|
|
|
|
|
if (!lock.owns_lock()) {
|
|
|
|
|
std::fill_n(leftOutput, numFrames, 0.0f);
|
|
|
|
|
std::fill_n(rightOutput, numFrames, 0.0f);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 02:46:40 +02:00
|
|
|
auto numMidiEvents = jack_midi_get_event_count(buffer);
|
|
|
|
|
jack_midi_event_t event;
|
|
|
|
|
|
2019-08-30 12:07:57 +02:00
|
|
|
// Midi dispatching
|
2019-08-24 02:46:40 +02:00
|
|
|
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])) {
|
2020-05-04 04:57:20 +02:00
|
|
|
case midi::noteOff: noteoff:
|
2019-12-23 01:25:20 +01:00
|
|
|
synth->noteOff(event.time, event.buffer[1], event.buffer[2]);
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
case midi::noteOn:
|
2020-05-04 04:57:20 +02:00
|
|
|
if (event.buffer[2] == 0)
|
|
|
|
|
goto noteoff;
|
2019-12-23 01:25:20 +01:00
|
|
|
synth->noteOn(event.time, event.buffer[1], event.buffer[2]);
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
case midi::polyphonicPressure:
|
2021-04-01 23:40:47 +02:00
|
|
|
synth->polyAftertouch(event.time, event.buffer[1], event.buffer[2]);
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
case midi::controlChange:
|
2019-12-23 01:25:20 +01:00
|
|
|
synth->cc(event.time, event.buffer[1], event.buffer[2]);
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
case midi::programChange:
|
2021-02-05 00:39:07 +01:00
|
|
|
// Not implemented
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
case midi::channelPressure:
|
2021-04-15 23:14:55 +02:00
|
|
|
synth->channelAftertouch(event.time, event.buffer[1]);
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
case midi::pitchBend:
|
2019-12-23 01:25:20 +01:00
|
|
|
synth->pitchWheel(event.time, midi::buildAndCenterPitch(event.buffer[1], event.buffer[2]));
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
case midi::systemMessage:
|
2021-02-05 00:39:07 +01:00
|
|
|
// Not implemented
|
2019-08-24 02:46:40 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-30 12:07:57 +02:00
|
|
|
|
2020-10-06 02:11:03 +02:00
|
|
|
float* stereoOutput[] = { leftOutput, rightOutput };
|
|
|
|
|
synth->renderBlock(stereoOutput, numFrames);
|
2019-08-29 02:23:33 +02:00
|
|
|
|
2019-08-24 02:46:40 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-11 00:18:18 +01:00
|
|
|
int sampleBlockChanged(jack_nframes_t nframes, void* arg)
|
2019-08-24 02:46:40 +02:00
|
|
|
{
|
|
|
|
|
if (arg == nullptr)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2020-10-06 02:11:03 +02:00
|
|
|
auto* synth = reinterpret_cast<sfz::Sfizz*>(arg);
|
2019-12-08 17:10:46 +01:00
|
|
|
// DBG("Sample per block changed to " << nframes);
|
2021-02-01 23:12:29 +01:00
|
|
|
std::lock_guard<SpinMutex> lock { processMutex };
|
2019-08-24 02:46:40 +02:00
|
|
|
synth->setSamplesPerBlock(nframes);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-11 00:18:18 +01:00
|
|
|
int sampleRateChanged(jack_nframes_t nframes, void* arg)
|
2019-08-24 02:46:40 +02:00
|
|
|
{
|
|
|
|
|
if (arg == nullptr)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2020-10-06 02:11:03 +02:00
|
|
|
auto* synth = reinterpret_cast<sfz::Sfizz*>(arg);
|
2019-12-08 17:10:46 +01:00
|
|
|
// DBG("Sample rate changed to " << nframes);
|
2021-02-01 23:12:29 +01:00
|
|
|
std::lock_guard<SpinMutex> lock { processMutex };
|
2019-08-24 02:46:40 +02:00
|
|
|
synth->setSampleRate(nframes);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 02:12:59 +02:00
|
|
|
static volatile sig_atomic_t shouldClose { false };
|
2019-08-25 00:00:50 +02:00
|
|
|
|
2020-03-11 00:18:18 +01:00
|
|
|
static void done(int sig)
|
2019-08-24 02:46:40 +02:00
|
|
|
{
|
2019-08-25 00:00:50 +02:00
|
|
|
std::cout << "Signal received" << '\n';
|
|
|
|
|
shouldClose = true;
|
2020-10-06 02:11:03 +02:00
|
|
|
(void)sig;
|
2019-08-25 00:00:50 +02:00
|
|
|
// if (client != nullptr)
|
2019-09-07 01:12:20 +02:00
|
|
|
|
2019-08-25 00:00:50 +02:00
|
|
|
// exit(0);
|
2019-08-24 02:46:40 +02:00
|
|
|
}
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2021-09-10 14:04:24 +02:00
|
|
|
bool loadInstrument(const char* fpath)
|
|
|
|
|
{
|
|
|
|
|
const char* importFormat = nullptr;
|
2021-08-19 20:06:37 +02:00
|
|
|
if (!sfizz_load_or_import_file(synth.handle(), fpath, &importFormat)) {
|
|
|
|
|
std::cout << "Could not load the instrument file: " << fpath << '\n';
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "Instrument loaded: " << fpath << '\n';
|
|
|
|
|
std::cout << "===========================" << '\n';
|
|
|
|
|
std::cout << "Total:" << '\n';
|
|
|
|
|
std::cout << "\tMasters: " << synth.getNumMasters() << '\n';
|
|
|
|
|
std::cout << "\tGroups: " << synth.getNumGroups() << '\n';
|
|
|
|
|
std::cout << "\tRegions: " << synth.getNumRegions() << '\n';
|
|
|
|
|
std::cout << "\tCurves: " << synth.getNumCurves() << '\n';
|
|
|
|
|
std::cout << "\tPreloadedSamples: " << synth.getNumPreloadedSamples() << '\n';
|
|
|
|
|
#if 0 // not currently in public API
|
|
|
|
|
std::cout << "===========================" << '\n';
|
|
|
|
|
std::cout << "Included files:" << '\n';
|
|
|
|
|
for (auto& file : synth.getParser().getIncludedFiles())
|
|
|
|
|
std::cout << '\t' << file << '\n';
|
|
|
|
|
std::cout << "===========================" << '\n';
|
|
|
|
|
std::cout << "Defines:" << '\n';
|
|
|
|
|
for (auto& define : synth.getParser().getDefines())
|
|
|
|
|
std::cout << '\t' << define.first << '=' << define.second << '\n';
|
|
|
|
|
#endif
|
|
|
|
|
std::cout << "===========================" << '\n';
|
|
|
|
|
std::cout << "Unknown opcodes:";
|
|
|
|
|
for (auto& opcode : synth.getUnknownOpcodes())
|
|
|
|
|
std::cout << opcode << ',';
|
|
|
|
|
std::cout << '\n';
|
|
|
|
|
if (importFormat) {
|
|
|
|
|
std::cout << "===========================" << '\n';
|
|
|
|
|
std::cout << "Import format: " << importFormat << '\n';
|
|
|
|
|
}
|
|
|
|
|
// std::cout << std::flush;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-10 14:04:24 +02:00
|
|
|
std::vector<std::string> stringTokenize(const std::string& str)
|
|
|
|
|
{
|
2021-08-20 03:31:31 +02:00
|
|
|
std::vector<std::string> tokens;
|
|
|
|
|
std::string part = "";
|
2021-09-10 14:04:24 +02:00
|
|
|
for (size_t i = 0; i < str.length(); i++) {
|
2021-08-20 03:31:31 +02:00
|
|
|
char c = str[i];
|
2021-09-10 14:04:24 +02:00
|
|
|
if (c == ' ' && part != "") {
|
2021-08-20 03:31:31 +02:00
|
|
|
tokens.push_back(part);
|
2021-09-10 14:04:24 +02:00
|
|
|
part = "";
|
|
|
|
|
} else if (c == '\"') {
|
2021-08-20 03:31:31 +02:00
|
|
|
i++;
|
2021-09-10 14:04:24 +02:00
|
|
|
while (str[i] != '\"') {
|
|
|
|
|
part += str[i];
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2021-08-20 03:31:31 +02:00
|
|
|
tokens.push_back(part);
|
2021-09-10 14:04:24 +02:00
|
|
|
part = "";
|
2021-08-20 03:31:31 +02:00
|
|
|
} else {
|
|
|
|
|
part += c;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-10 14:04:24 +02:00
|
|
|
if (part != "") {
|
2021-08-20 03:31:31 +02:00
|
|
|
tokens.push_back(part);
|
|
|
|
|
}
|
|
|
|
|
return tokens;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-10 14:04:24 +02:00
|
|
|
void cliThreadProc()
|
|
|
|
|
{
|
2024-05-29 15:19:28 -04:00
|
|
|
const std::string whitespace = " \t";
|
2021-08-19 20:06:37 +02:00
|
|
|
while (!shouldClose) {
|
|
|
|
|
std::cout << "\n> ";
|
|
|
|
|
|
|
|
|
|
std::string command;
|
|
|
|
|
std::getline(std::cin, command);
|
2024-05-29 15:19:28 -04:00
|
|
|
std::size_t start = command.length()? command.find_first_not_of(whitespace): 0;
|
|
|
|
|
std::size_t pos = command.find_first_of(whitespace, start);
|
2024-05-29 01:06:13 -04:00
|
|
|
std::string kw = command.substr(start, pos - start);
|
2021-09-10 14:04:24 +02:00
|
|
|
std::string args = command.substr(pos + 1);
|
|
|
|
|
std::vector<std::string> tokens = stringTokenize(args);
|
2021-08-19 20:06:37 +02:00
|
|
|
|
2021-09-21 11:30:23 +02:00
|
|
|
if (kw == "load_instrument") {
|
2021-08-26 12:54:21 +02:00
|
|
|
try {
|
|
|
|
|
std::lock_guard<SpinMutex> lock { processMutex };
|
2021-09-10 14:04:24 +02:00
|
|
|
loadInstrument(tokens[0].c_str());
|
2021-08-26 12:54:21 +02:00
|
|
|
} catch (...) {
|
|
|
|
|
std::cout << "ERROR: Can't load instrument!\n";
|
|
|
|
|
}
|
2021-09-10 14:04:24 +02:00
|
|
|
} else if (kw == "set_oversampling") {
|
2021-08-20 02:33:11 +02:00
|
|
|
try {
|
2021-08-26 12:54:21 +02:00
|
|
|
std::lock_guard<SpinMutex> lock { processMutex };
|
2021-08-20 02:33:11 +02:00
|
|
|
synth.setOversamplingFactor(stoi(args));
|
|
|
|
|
} catch (...) {
|
|
|
|
|
std::cout << "ERROR: Can't set oversampling!\n";
|
|
|
|
|
}
|
2021-09-10 14:04:24 +02:00
|
|
|
} else if (kw == "set_preload_size") {
|
2021-08-19 20:06:37 +02:00
|
|
|
try {
|
2021-08-26 12:54:21 +02:00
|
|
|
std::lock_guard<SpinMutex> lock { processMutex };
|
2021-08-19 20:06:37 +02:00
|
|
|
synth.setPreloadSize(stoi(args));
|
|
|
|
|
} catch (...) {
|
|
|
|
|
std::cout << "ERROR: Can't set preload size!\n";
|
|
|
|
|
}
|
2021-09-10 14:04:24 +02:00
|
|
|
} else if (kw == "set_voices") {
|
2021-08-19 20:06:37 +02:00
|
|
|
try {
|
2021-08-26 12:54:21 +02:00
|
|
|
std::lock_guard<SpinMutex> lock { processMutex };
|
2021-08-19 20:06:37 +02:00
|
|
|
synth.setNumVoices(stoi(args));
|
|
|
|
|
} catch (...) {
|
|
|
|
|
std::cout << "ERROR: Can't set num of voices!\n";
|
|
|
|
|
}
|
2024-05-29 14:27:05 -04:00
|
|
|
} else if (kw == "quit" || !std::cin) {
|
2021-08-19 20:06:37 +02:00
|
|
|
shouldClose = true;
|
2021-09-10 14:04:24 +02:00
|
|
|
} else if (kw.size() > 0) {
|
|
|
|
|
std::cout << "ERROR: Unknown command '" << kw << "'!\n";
|
2021-08-19 20:06:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-23 17:35:22 +01:00
|
|
|
ABSL_FLAG(std::string, client_name, "sfizz", "Jack client name");
|
2019-12-23 01:25:20 +01:00
|
|
|
ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are x1, x2, x4, x8)");
|
2021-08-19 14:31:47 +02:00
|
|
|
ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded size");
|
|
|
|
|
ABSL_FLAG(uint32_t, num_voices, 32, "Num of voices");
|
2021-08-19 20:06:37 +02:00
|
|
|
ABSL_FLAG(bool, jack_autoconnect, false, "Autoconnect audio output");
|
2020-05-06 13:35:26 +02:00
|
|
|
ABSL_FLAG(bool, state, false, "Output the synth state in the jack loop");
|
2019-12-01 21:40:27 +01:00
|
|
|
|
2019-07-29 02:13:03 +02:00
|
|
|
int main(int argc, char** argv)
|
|
|
|
|
{
|
2019-08-22 21:11:59 +02:00
|
|
|
auto arguments = absl::ParseCommandLine(argc, argv);
|
2021-08-19 20:06:37 +02:00
|
|
|
|
2019-08-22 21:11:59 +02:00
|
|
|
auto filesToParse = absl::MakeConstSpan(arguments).subspan(1);
|
2020-02-23 17:35:22 +01:00
|
|
|
const std::string clientName = absl::GetFlag(FLAGS_client_name);
|
2019-12-01 21:40:27 +01:00
|
|
|
const std::string oversampling = absl::GetFlag(FLAGS_oversampling);
|
|
|
|
|
const uint32_t preload_size = absl::GetFlag(FLAGS_preload_size);
|
2021-08-19 14:31:47 +02:00
|
|
|
const uint32_t num_voices = absl::GetFlag(FLAGS_num_voices);
|
2021-08-19 20:06:37 +02:00
|
|
|
const bool jack_autoconnect = absl::GetFlag(FLAGS_jack_autoconnect);
|
2020-05-06 13:35:26 +02:00
|
|
|
const bool verboseState = absl::GetFlag(FLAGS_state);
|
2019-12-01 21:40:27 +01:00
|
|
|
|
|
|
|
|
std::cout << "Flags" << '\n';
|
2020-02-23 17:35:22 +01:00
|
|
|
std::cout << "- Client name: " << clientName << '\n';
|
2019-12-01 21:40:27 +01:00
|
|
|
std::cout << "- Oversampling: " << oversampling << '\n';
|
2021-08-19 14:31:47 +02:00
|
|
|
std::cout << "- Preloaded size: " << preload_size << '\n';
|
|
|
|
|
std::cout << "- Num of voices: " << num_voices << '\n';
|
2021-08-19 20:06:37 +02:00
|
|
|
std::cout << "- Audio Autoconnect: " << jack_autoconnect << '\n';
|
|
|
|
|
std::cout << "- Verbose State: " << verboseState << '\n';
|
2021-08-19 14:31:47 +02:00
|
|
|
|
2019-12-01 21:40:27 +01:00
|
|
|
const auto factor = [&]() {
|
2020-10-06 02:11:03 +02:00
|
|
|
if (oversampling == "x1") return 1;
|
|
|
|
|
if (oversampling == "x2") return 2;
|
|
|
|
|
if (oversampling == "x4") return 4;
|
|
|
|
|
if (oversampling == "x8") return 8;
|
|
|
|
|
return 1;
|
2019-12-01 21:40:27 +01:00
|
|
|
}();
|
|
|
|
|
|
2019-08-22 21:11:59 +02:00
|
|
|
std::cout << "Positional arguments:";
|
2019-08-24 02:46:40 +02:00
|
|
|
for (auto& file : filesToParse)
|
2019-08-22 21:11:59 +02:00
|
|
|
std::cout << " " << file << ',';
|
|
|
|
|
std::cout << '\n';
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2019-12-01 21:40:27 +01:00
|
|
|
synth.setOversamplingFactor(factor);
|
|
|
|
|
synth.setPreloadSize(preload_size);
|
2021-08-19 14:31:47 +02:00
|
|
|
synth.setNumVoices(num_voices);
|
2021-06-22 21:25:39 +02:00
|
|
|
|
2019-08-24 02:46:40 +02:00
|
|
|
jack_status_t status;
|
2020-02-23 17:35:22 +01:00
|
|
|
client = jack_client_open(clientName.c_str(), JackNullOption, &status);
|
2019-08-24 02:46:40 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-19 20:06:37 +02:00
|
|
|
if (jack_autoconnect) {
|
|
|
|
|
auto systemPorts = jack_get_ports(client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput);
|
|
|
|
|
if (systemPorts == nullptr) {
|
|
|
|
|
std::cerr << "No physical output ports found" << '\n';
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2019-08-24 02:46:40 +02:00
|
|
|
|
2021-08-19 20:06:37 +02:00
|
|
|
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);
|
2019-08-24 02:46:40 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-20 23:53:34 +02:00
|
|
|
if (!filesToParse.empty() && filesToParse[0]) {
|
2021-09-10 14:04:24 +02:00
|
|
|
loadInstrument(filesToParse[0]);
|
2019-08-24 02:46:40 +02:00
|
|
|
}
|
2021-08-19 20:06:37 +02:00
|
|
|
|
2021-09-10 14:04:24 +02:00
|
|
|
std::thread cli_thread(cliThreadProc);
|
2019-08-24 02:46:40 +02:00
|
|
|
|
|
|
|
|
signal(SIGHUP, done);
|
|
|
|
|
signal(SIGINT, done);
|
|
|
|
|
signal(SIGTERM, done);
|
|
|
|
|
signal(SIGQUIT, done);
|
2019-08-25 00:00:50 +02:00
|
|
|
|
2021-09-10 14:04:24 +02:00
|
|
|
while (!shouldClose) {
|
2020-05-06 13:35:26 +02:00
|
|
|
if (verboseState) {
|
|
|
|
|
std::cout << "Active voices: " << synth.getNumActiveVoices() << '\n';
|
2019-12-02 12:35:14 +01:00
|
|
|
#ifndef NDEBUG
|
2021-09-10 14:04:24 +02:00
|
|
|
std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n';
|
|
|
|
|
std::cout << "Total size: " << synth.getAllocatedBytes() << '\n';
|
2019-12-02 12:24:54 +01:00
|
|
|
#endif
|
2020-05-06 13:35:26 +02:00
|
|
|
}
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2019-09-16 14:51:17 +02:00
|
|
|
}
|
2019-09-07 01:12:20 +02:00
|
|
|
|
2019-08-25 00:00:50 +02:00
|
|
|
std::cout << "Closing..." << '\n';
|
|
|
|
|
jack_client_close(client);
|
2022-07-09 15:29:12 +01:00
|
|
|
cli_thread.join();
|
2019-07-29 02:13:03 +02:00
|
|
|
return 0;
|
2019-12-01 21:40:27 +01:00
|
|
|
}
|