1 // nova server
2 // Copyright (C) 2008, 2009, 2010 Tim Blechmann
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; see the file COPYING. If not, write to
16 // the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 // Boston, MA 02111-1307, USA.
18
19 #pragma once
20
21 #include <atomic>
22
23 #include "buffer_manager.hpp"
24 #include "memory_pool.hpp"
25 #include "node_graph.hpp"
26 #include "../sc/sc_osc_handler.hpp"
27 #include "server_args.hpp"
28 #include "server_scheduler.hpp"
29 #include "synth_factory.hpp"
30 #include "../sc/sc_ugen_factory.hpp"
31 #include "../utilities/callback_interpreter.hpp"
32 #include "../utilities/static_pooled_class.hpp"
33 #include "../utilities/asynchronous_log.hpp"
34 #include "../../common/server_shm.hpp"
35
36 #ifdef PORTAUDIO_BACKEND
37 # include "audio_backend/portaudio_backend.hpp"
38 #elif defined(JACK_BACKEND)
39 # include "audio_backend/jack_backend.hpp"
40 #endif
41
42 #include "../../scsynth/SC_TimeDLL.hpp"
43 #include "../../scsynth/SC_Time.hpp"
44 namespace nova {
45
46 struct realtime_engine_functor {
47 static inline void sync_clock(void);
48 static void init_thread(void);
49 static inline void run_tick(void);
50 static void log_(const char*);
51 static void log_printf_(const char*, ...);
52 };
53
54 extern class nova_server* instance;
55
56 /** callback class for audio-thread to system-thread communcation
57 *
58 * the system_callback uses an internal memory pool for real-time safe
59 * memory management. objects are only allowed to be allocated from
60 * the real-time thread.
61 * */
62
63 class system_callback : public static_pooled_class<system_callback, 1 << 20, /* 1mb pool of realtime memory */
64 false, 5> {
65 public:
66 virtual ~system_callback(void) = default;
67
68 virtual void run(void) = 0;
69 };
70
71 /** system_callback to delete object in the system thread. useful to avoid hitting the memory allocator
72 * from within the real-time threads
73 */
74 template <typename T> class delete_callback : public system_callback {
75 public:
delete_callback(T * ptr)76 delete_callback(T* ptr): ptr_(ptr) {}
77
78 private:
run(void)79 virtual void run(void) override { delete ptr_; }
80
81 const T* const ptr_;
82 };
83
84
85 /** dsp thread init functor
86 *
87 * for the real-time use, it should acquire real-time scheduling and pin the thread to a certain CPU
88 *
89 * */
90 struct thread_init_functor {
thread_init_functornova::thread_init_functor91 thread_init_functor(bool real_time): rt(real_time) {}
92
93 void operator()(int thread_index);
94
95 private:
96 const bool rt;
97 };
98
99 struct io_thread_init_functor {
100 void operator()() const;
101 };
102
103 namespace detail {
104 #if defined(PORTAUDIO_BACKEND)
105 typedef portaudio_backend<realtime_engine_functor, float, false> audio_backend;
106 #elif defined(JACK_BACKEND)
107 typedef jack_backend<realtime_engine_functor, float, false> audio_backend;
108 #else
109 # error "no audio backend selected"
110 #endif
111
112 } // detail
113
114 class nova_server : public asynchronous_log_thread,
115 public node_graph,
116 public server_shared_memory_creator,
117 public scheduler<thread_init_functor>,
118 public detail::audio_backend,
119 public synth_factory,
120 public buffer_manager,
121 public sc_osc_handler {
122 public:
123 SC_TimeDLL mDLL;
124 bool use_system_clock;
125 double smooth_samplerate;
126
127 typedef detail::audio_backend audio_backend;
128
129 /* main nova_server function */
130 nova_server(server_arguments const& args);
131
132 ~nova_server(void);
133
134 void prepare_backend(void);
135
136 /* @{ */
137 /** io interpreter */
add_io_callback(system_callback * cb)138 void add_io_callback(system_callback* cb) { io_interpreter.add_callback(cb); }
139 /* @} */
140
141 /* @{ */
142 /** system interpreter
143 * \note: should only be called from the main audio thread
144 */
add_system_callback(system_callback * cb)145 void add_system_callback(system_callback* cb) { system_interpreter.add_callback(cb); }
146
run(void)147 void run(void) {
148 start_dsp_threads();
149 system_interpreter.run();
150 }
151
prepare_to_terminate()152 void prepare_to_terminate() { server_shared_memory_creator::disconnect(); }
153
terminate(void)154 void terminate(void) {
155 system_interpreter.terminate();
156 quit_requested_ = true;
157 }
158 /* @} */
159
160 /** non-rt synthesis */
161 void run_nonrt_synthesis(server_arguments const& arguments);
162
163 public:
164 /* @{ */
165 /** node control */
166 abstract_synth* add_synth(const char* name, int id, node_position_constraint const& constraints);
167 group* add_group(int id, node_position_constraint const& constraints);
168 parallel_group* add_parallel_group(int id, node_position_constraint const& constraints);
169
170 void free_node(server_node* node);
171 void group_free_all(abstract_group* group);
172 void group_free_deep(abstract_group* group);
173
node_pause(server_node * node)174 void node_pause(server_node* node) {
175 node->pause();
176 notification_node_turned_off(node);
177 }
178
node_resume(server_node * node)179 void node_resume(server_node* node) {
180 node->resume();
181 notification_node_turned_on(node);
182 }
183
184 void set_node_slot(int node_id, slot_index_t slot, float value);
185 void set_node_slot(int node_id, const char* slot, float value);
186 /* @} */
187
cpu_load(float & peak,float & average) const188 void cpu_load(float& peak, float& average) const { audio_backend::get_cpuload(peak, average); }
189
increment_logical_time(void)190 void increment_logical_time(void) { sc_osc_handler::increment_logical_time(time_per_tick); }
191
set_last_now(time_tag const & lasts,time_tag const & nows)192 void set_last_now(time_tag const& lasts, time_tag const& nows) { sc_osc_handler::set_last_now(lasts, nows); }
193
compensate_latency(void)194 void compensate_latency(void) {
195 sc_osc_handler::add_last_now(
196 time_tag::from_samples(audio_backend::get_latency(), audio_backend::get_samplerate()));
197 }
198
199 public:
tick()200 HOT void tick() {
201 sc_factory->apply_done_actions();
202 run_callbacks();
203 execute_scheduled_bundles();
204
205 if (unlikely(dsp_queue_dirty))
206 rebuild_dsp_queue();
207
208 compute_audio();
209 }
210
211 void rebuild_dsp_queue(void);
212
request_dsp_queue_update(void)213 void request_dsp_queue_update(void) { dsp_queue_dirty = true; }
214
quit_requested()215 bool quit_requested() { return quit_requested_.load(); }
216
217 private:
218 void perform_node_add(server_node* node, node_position_constraint const& constraints, bool update_dsp_queue);
219 void finalize_node(server_node& node);
220 std::atomic<bool> quit_requested_ = { false };
221 bool dsp_queue_dirty = false;
222
223 callback_interpreter<system_callback, false> system_interpreter; // rt to system thread
224 threaded_callback_interpreter<system_callback, io_thread_init_functor> io_interpreter; // for network IO
225 };
226
run_scheduler_tick(void)227 inline void run_scheduler_tick(void) {
228 const int blocksize = sc_factory->world.mBufLength;
229 const int input_channels = sc_factory->world.mNumInputs;
230 const int output_channels = sc_factory->world.mNumOutputs;
231 const int buf_counter = ++sc_factory->world.mBufCounter;
232
233 /* touch all input buffers */
234 for (int channel = 0; channel != input_channels; ++channel)
235 sc_factory->world.mAudioBusTouched[output_channels + channel] = buf_counter;
236
237 instance->tick();
238
239 /* wipe all untouched output buffers */
240 for (int channel = 0; channel != output_channels; ++channel) {
241 if (sc_factory->world.mAudioBusTouched[channel] != buf_counter)
242 zerovec(sc_factory->world.mAudioBus + blocksize * channel, blocksize);
243 }
244 }
245
log_printf(const char * fmt,...)246 inline bool log_printf(const char* fmt, ...) {
247 va_list vargs;
248 va_start(vargs, fmt);
249 return instance->log_printf(fmt, vargs);
250 }
251
252
sync_clock(void)253 inline void realtime_engine_functor::sync_clock(void) {
254 if (instance->use_system_clock) {
255 double nows = (uint64)(OSCTime(std::chrono::system_clock::now())) * kOSCtoSecs;
256 instance->mDLL.Reset(sc_factory->world.mSampleRate, sc_factory->world.mBufLength, SC_TIME_DLL_BW, nows);
257 time_tag oscTime = time_tag((uint64)((instance->mDLL.PeriodTime()) * kSecondsToOSCunits + .5));
258 time_tag oscInc = time_tag((uint64)((instance->mDLL.Period()) * kSecondsToOSCunits + .5));
259 instance->smooth_samplerate = instance->mDLL.SampleRate();
260 instance->set_last_now(oscTime, oscTime + oscInc);
261 } else
262 instance->update_time_from_system();
263
264 instance->compensate_latency();
265 }
266
267
run_tick(void)268 inline void realtime_engine_functor::run_tick(void) {
269 /* // debug timedll
270 static int count = 0;
271 if(count >= 44100/64){
272 count = 0;
273 log_printf("DLL: t %.6f p %.9f sr %.6f e %.9f avg(e) %.9f \n",
274 instance->mDLL.PeriodTime(), instance->mDLL.Period(), instance->mDLL.SampleRate(),
275 instance->mDLL.Error(), instance->mDLL.AvgError());
276 }
277 count++;
278 */
279 run_scheduler_tick();
280
281 if (instance->use_system_clock) {
282 // time_tag nows =
283 // time_tag::from_ptime(boost::date_time::microsec_clock<boost::posix_time::ptime>::universal_time());
284 double nows = (uint64)(OSCTime(std::chrono::system_clock::now())) * kOSCtoSecs;
285 instance->mDLL.Update(nows);
286 time_tag oscTime = time_tag((uint64)((instance->mDLL.PeriodTime()) * kSecondsToOSCunits + .5));
287 time_tag oscInc = time_tag((uint64)((instance->mDLL.Period()) * kSecondsToOSCunits + .5));
288 instance->smooth_samplerate = instance->mDLL.SampleRate();
289 instance->set_last_now(oscTime, oscTime + oscInc);
290 } else
291 instance->increment_logical_time();
292 }
293
294
log(const char * string)295 inline bool log(const char* string) { return instance->log(string); }
296
log(const char * string,size_t length)297 inline bool log(const char* string, size_t length) { return instance->log(string, length); }
298
299 } /* namespace nova */
300