Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderVocke committed Feb 10, 2025
1 parent 1dee1b6 commit bf70a44
Show file tree
Hide file tree
Showing 26 changed files with 156 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/backend/internal/AudioMidiDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class AudioMidiDriver : public WithCommandQueue,
bool get_active() const;
uint32_t get_last_processed() const;

void wait_process();
virtual void wait_process();

virtual std::vector<ExternalPortDescriptor> find_external_ports(
const char* maybe_name_regex,
Expand Down
20 changes: 20 additions & 0 deletions src/backend/internal/ObjectPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@

#include "LoggingEnabled.h"

#ifdef _WIN32
#include <windows.h>
#elif defined(__linux__) || defined(__APPLE__)
#include <pthread.h>
#endif


// A class which manages a queue of audio objects which can be
// consumed lock-free. The queue is continuously replenished with newly allocated
// objects asynchronously.
Expand Down Expand Up @@ -40,6 +47,19 @@ class ObjectPool : public ModuleLoggingEnabled<"Backend.ObjectPool"> {
// Start auto-replenishment
m_replenish_thread = std::thread(
[this]() { this->replenish_thread_fn(); });

#ifdef _WIN32
HANDLE handle = m_replenish_thread.native_handle();
int winPriority = THREAD_PRIORITY_ABOVE_NORMAL;
SetThreadPriority(handle, winPriority);
#elif defined(__linux__) || defined(__APPLE__)
pthread_t handle = m_replenish_thread.native_handle();
struct sched_param sch;
int policy;
pthread_getschedparam(handle, &policy, &sch);
sch.sched_priority = 4; // "above normal" priority
pthread_setschedparam(handle, SCHED_FIFO, &sch);
#endif
}

~ObjectPool() {
Expand Down
2 changes: 2 additions & 0 deletions src/backend/internal/jack/JackApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
// TODO: this does not contain the complete API, only what we use
class JackApi {
public:
static constexpr bool supports_processing = true;

static auto get_ports(auto ...args) { return jack_get_ports(args...); }
static auto port_by_name(auto ...args) { return jack_port_by_name(args...); }
static auto port_flags(auto ...args) { return jack_port_flags(args...); }
Expand Down
11 changes: 11 additions & 0 deletions src/backend/internal/jack/JackAudioMidiDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
#include "LoggingBackend.h"
#include "MidiPort.h"
#include "PortInterface.h"
#include <chrono>
#include <cstring>
#include <stdexcept>
#include <memory>
#include <atomic>
#include <thread>
#include "jack/types.h"
#include "run_in_thread_with_timeout.h"
#include "JackAudioPort.h"
Expand Down Expand Up @@ -242,5 +244,14 @@ std::vector<ExternalPortDescriptor> GenericJackAudioMidiDriver<API>::find_extern
return rval;
}

template<typename API>
void GenericJackAudioMidiDriver<API>::wait_process() {
if (API::supports_processing) {
AudioMidiDriver::wait_process();
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}

template class GenericJackAudioMidiDriver<JackApi>;
template class GenericJackAudioMidiDriver<JackTestApi>;
2 changes: 2 additions & 0 deletions src/backend/internal/jack/JackAudioMidiDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class GenericJackAudioMidiDriver :
void maybe_update_buffer_size() override;
void maybe_update_dsp_load() override;

void wait_process() override;

public:
GenericJackAudioMidiDriver();
~GenericJackAudioMidiDriver() override;
Expand Down
5 changes: 5 additions & 0 deletions src/backend/internal/jack/JackTestApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ namespace jacktestapi_globals {
// Note that only creation is supported. Closing clients and ports will not do anything.
class JackTestApi {
public:
static constexpr bool supports_processing = false;

enum class Type {
Audio,
Midi
Expand Down Expand Up @@ -194,6 +196,9 @@ class JackTestApi {
static const char** port_get_connections(const jack_port_t* port);
static int set_port_registration_callback(jack_client_t* client, JackPortRegistrationCallback cb, void* arg);

// FIXME?
// static int set_process_callback(jack_client_t* client, JackProcessCallback cb, void* arg);

static void set_error_function(void (*fn)(const char*));
static void set_info_function(void (*fn)(const char*));

Expand Down
2 changes: 1 addition & 1 deletion src/backend/internal/shoop_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ constexpr uint32_t initial_max_loops = 512;
constexpr uint32_t initial_max_ports = 1024;
constexpr uint32_t initial_max_fx_chains = 128;
constexpr uint32_t initial_max_decoupled_midi_ports = 512;
constexpr uint32_t n_buffers_in_pool = 512;
constexpr uint32_t n_buffers_in_pool = 2048;
constexpr uint32_t audio_buffer_size = 4096;
constexpr uint32_t command_queue_size = 2048;
constexpr uint32_t audio_channel_initial_buffers = 128;
Expand Down
4 changes: 3 additions & 1 deletion src/backend/libshoopdaloop_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,9 @@ shoop_external_port_descriptors_t *find_external_ports(
shoop_port_direction_t maybe_port_direction_filter,
shoop_port_data_type_t maybe_data_type_filter)
{
return api_impl<shoop_external_port_descriptors_t*>("find_external_ports", [&]() -> shoop_external_port_descriptors_t* {
std::string name_regex = maybe_name_regex ? maybe_name_regex : "";
std::string fn_name = "find_external_ports (regex \"" + name_regex + "\")";
return api_impl<shoop_external_port_descriptors_t*>(fn_name.c_str(), [&]() -> shoop_external_port_descriptors_t* {
auto _driver = internal_audio_driver(driver);
if (!_driver) { return nullptr; }
auto ports = _driver->find_external_ports(
Expand Down
2 changes: 2 additions & 0 deletions src/python/shoopdaloop/lib/q_objects/Backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

all_active_backend_objs = set()

# FIXME delete this file

# Wraps the back-end session + driver in a single object.
class Backend(ShoopQQuickItem):
def __init__(self, parent=None):
Expand Down
2 changes: 1 addition & 1 deletion src/python/shoopdaloop/lib/q_objects/CompositeLoop.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ def do_triggers(self, iteration, mode, trigger_callback = lambda self,loop,mode:
self.do_triggers(0, self.mode, trigger_callback, True)

def maybe_initialize(self):
if self._backend and self._backend.initialized and not self._initialized:
if self._backend and self._backend.property('ready') and not self._initialized:
self.logger.debug(lambda: 'Found backend, initializing')
self.initializedChanged.emit(True)

Expand Down
2 changes: 1 addition & 1 deletion src/python/shoopdaloop/lib/q_objects/FXChain.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def restore_state(self, state_str):
self.logger.throw_error("Restoring internal state of uninitialized FX chain")

def maybe_initialize(self):
if self._backend and self._backend.initialized and self._chain_type != None and not self._backend_object:
if self._backend and self._backend.property('ready') and self._chain_type != None and not self._backend_object:
self._backend_object = self._backend.get_backend_session_obj().create_fx_chain(self._chain_type, self._title)
if self._backend_object:
self._initialized = True
Expand Down
1 change: 1 addition & 0 deletions src/qml/CompositeLoop.qml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Item {
property alias sync_position : py_loop.sync_position
property alias length : py_loop.length
property alias py_loop : py_loop
property alias backend : py_loop.backend
PythonCompositeLoop {
id: py_loop
iteration: 0
Expand Down
29 changes: 1 addition & 28 deletions src/qml/LoopWidget.qml
Original file line number Diff line number Diff line change
Expand Up @@ -497,38 +497,11 @@ Item {
}
}

// function create_backend_loop_impl() {
// if (!maybe_loop) {
// if (backend_loop_factory.status == Component.Error) {
// throw new Error("BackendLoopWithChannels: Failed to load factory: " + backend_loop_factory.errorString())
// } else if (backend_loop_factory.status != Component.Ready) {
// throw new Error("BackendLoopWithChannels: Factory not ready: " + backend_loop_factory.status.toString())
// } else {
// let gain = last_pushed_gain
// let balance = last_pushed_stereo_balance
// maybe_loop = backend_loop_factory.createObject(root, {
// 'initial_descriptor': root.initial_descriptor,
// 'sync_source': Qt.binding(() => (!is_sync && root.sync_loop && root.sync_loop.maybe_backend_loop) ? root.sync_loop.maybe_backend_loop : null),
// })
// push_stereo_balance(balance)
// push_gain(gain)
// maybe_loop.onCycled.connect(root.cycled)
// }
// }
// }

// ExecuteNextCycle {
// id: create_loop_next_cycle
// onExecute: root.create_backend_loop_impl()
// }
// function create_backend_loop() {
// create_loop_next_cycle.trigger()
// }

Component {
id: composite_loop_factory
CompositeLoop {
loop_widget: root
backend: root.backend
}
}
function create_composite_loop(composition={
Expand Down
4 changes: 2 additions & 2 deletions src/qml/ProfilingWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ApplicationWindow {
var _data = {}

var bufsize = root.backend.get_buffer_size()
var samplerate = root.backend && root.backend.initialized ? root.backend.get_sample_rate() : 1
var samplerate = root.backend && root.backend.ready ? root.backend.get_sample_rate() : 1
cycle_us = bufsize / samplerate * 1000000.0

Object.entries(profiling_data).forEach((p) => {
Expand Down Expand Up @@ -58,7 +58,7 @@ ApplicationWindow {
function update() {
var data = root.backend.get_profiling_report()
var bufsize = root.backend.get_buffer_size()
var samplerate = root.backend && root.backend.initialized ? root.backend.get_sample_rate() : 1
var samplerate = root.backend && root.backend.ready ? root.backend.get_sample_rate() : 1
cycle_us = bufsize / samplerate * 1000000.0

root.profiling_data = data
Expand Down
2 changes: 1 addition & 1 deletion src/qml/test/ShoopSessionTestCase.qml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ShoopTestCase {
when: timer.done

function check_backend() {
verify(backend && backend.initialized, "backend not initialized")
verify(backend && backend.ready, "backend not initialized")
wait_updated(backend)
}

Expand Down
2 changes: 1 addition & 1 deletion src/qml/test/tst_Backend.qml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ShoopTestFile {

test_fns: ({
'test_backend': () => {
verify(backend.initialized)
verify(backend.ready)
backend.close()
}
})
Expand Down
8 changes: 6 additions & 2 deletions src/qml/test/tst_Backend_jack.qml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ ShoopTestFile {
ShoopTestCase {
name: 'JackBackend'
filename : TestFilename.test_filename()
when: backend.initialized || backend.backend_type == null
when: {
let ready = backend.ready || backend.backend_type == null
console.log("Backend ready: " + ready)
return ready
}

test_fns: ({
'test_backend_jack': () => {
Expand All @@ -27,7 +31,7 @@ ShoopTestFile {
return
}

verify(backend.initialized)
verify(backend.ready)
wait(1000)
verify_eq(
backend.actual_backend_type,
Expand Down
7 changes: 7 additions & 0 deletions src/qml/test/tst_CompositeLoop_scheduling.qml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ ShoopTestFile {
property int n_cycles: 1
property int position: 0

signal positionChangedUnsafe(position : int)
signal lengthChangedUnsafe(length : int)
signal cycledUnsafe()

property var maybe_loop: this

signal cycled(int cycle_nr)
Expand Down Expand Up @@ -77,6 +81,9 @@ ShoopTestFile {

CompositeLoop {
id: sequential_sched_lut
backend: Item {
property bool ready : true
}

ShoopTestCase {
name: 'CompositeLoop_sequential_sched'
Expand Down
6 changes: 5 additions & 1 deletion src/qml/test/tst_Jack_ports.qml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ ShoopTestFile {
'min_n_ringbuffer_samples': 0
})

backend: backend
is_internal: false
id: audio_in
}
Expand All @@ -51,6 +52,7 @@ ShoopTestFile {

is_internal: false
id: audio_out
backend: backend
}
MidiPort {
descriptor: ({
Expand All @@ -69,6 +71,7 @@ ShoopTestFile {

is_internal: false
id: midi_in
backend: backend
}
MidiPort {
descriptor: ({
Expand All @@ -87,6 +90,7 @@ ShoopTestFile {

is_internal: false
id: midi_out
backend: backend
}

ShoopTestCase {
Expand All @@ -102,7 +106,7 @@ ShoopTestFile {
skip("Backend was built without Jack support")
return
}
verify(backend.initialized)
verify(backend.ready)

wait(100)

Expand Down
2 changes: 1 addition & 1 deletion src/qml/test/tst_LuaScriptWithEngine.qml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ ShoopTestFile {
name: 'Lua_autoconnect'
session: session
filename : TestFilename.test_filename()
when: backend.initialized || backend.backend_type == null
when: backend.ready || backend.backend_type == null

testcase_deinit_fn: () => { backend.close() }

Expand Down
2 changes: 1 addition & 1 deletion src/qml/test/tst_MidiControlPort.qml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ ShoopTestFile {
ShoopTestCase {
name: 'MidiControlPort'
filename : TestFilename.test_filename()
when: backend.initialized || backend.backend_type == null
when: backend.ready || backend.backend_type == null

testcase_deinit_fn: () => { backend.close() }

Expand Down
2 changes: 1 addition & 1 deletion src/qml/test/tst_Session_save_load.qml
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ ShoopTestFile {

"test_save_load_session_audio_and_midi_resampled": () => {
check_backend()
verify(other_session.backend && other_session.backend.initialized, "resampled backend not initialized")
verify(other_session.backend && other_session.backend.ready, "resampled backend not initialized")

let midichan = [
{ 'time': 120, 'data': [0x90, 70, 70] },
Expand Down
16 changes: 15 additions & 1 deletion src/rust/backend_bindings/src/audio_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,22 @@ impl AudioDriver {

pub fn find_external_ports(&self, maybe_name_regex: Option<&str>, port_direction: u32, data_type: u32) -> Vec<ExternalPortDescriptor> {
let obj = self.lock();
let regex_ptr = maybe_name_regex.map_or(std::ptr::null(), |s| s.as_ptr() as *const i8);
let maybe_name_regex_updated = match maybe_name_regex {
Some(s) => match s {
"" => None,
_ => Some(std::ffi::CString::new(s).unwrap()),
}
None => None,
};
let regex_ptr = maybe_name_regex_updated
.as_ref()
.map_or(std::ptr::null(), |s| {
s.as_ptr() as *const i8
});
let result = unsafe { ffi::find_external_ports(*obj, regex_ptr, port_direction as ffi::shoop_port_direction_t, data_type as ffi::shoop_port_data_type_t) };
if result.is_null() {
return Vec::new();
}
let ports = unsafe { std::slice::from_raw_parts((*result).ports, (*result).n_ports as usize) };
let mut port_descriptors = Vec::new();
unsafe {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include <QTimer>
#include <QMetaObject>
#include "connect.h"

inline void qtimerSetSingleShot(QTimer &timer, bool singleShot) {
Expand All @@ -18,6 +19,15 @@ inline void qtimerStop(QTimer &timer) {
timer.stop();
}

inline void qtimerStopQueued(QTimer &timer) {
QMetaMethod method = timer.metaObject()->method(timer.metaObject()->indexOfMethod("stop()"));
method.invoke(&timer, Qt::QueuedConnection);
}

inline bool qtimerIsActive(QTimer const& timer) {
return timer.isActive();
}

template<typename A>
inline void qtimerConnectTimeout(QTimer &timer, A *receiver, ::rust::String member) {
connect(&timer, "timeout()", receiver, member);
Expand Down
Loading

0 comments on commit bf70a44

Please sign in to comment.