2 terminatorX - realtime audio scratching software
3 Copyright (C) 1999-2003 Alexander König
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 Description: Contains the code that does the real "Scratching
22 business": XInput, DGA, Mouse and Keyboardgrabbing
25 02 Jun 1999: Implemented high-priority/rt-FIFO-Scheduling use for
28 04 Jun 1999: Changed warp-feature behaviour: still connected to
29 mouse-speed (should be changed to maybe) but now
30 depends on sample size -> you can warp through all
31 samples with the same mouse-distance.
33 12 Aug 2002: Complete rewrite - tX_engine is now a class and the thread
34 is created on startup and kept alive until termination
39 #include "tX_engine.h"
40 #include "tX_audiodevice.h"
45 #include <gdk/gdkprivate.h>
46 #include "tX_mastergui.h"
47 #include "tX_global.h"
49 #include "tX_widget.h"
51 #include "tX_sequencer.h"
54 #include "tX_capabilities.h"
57 #include <sys/resource.h>
59 tX_engine *tX_engine::engine=NULL;
61 tX_engine *tX_engine::get_instance() {
63 engine=new tX_engine();
69 void tX_engine::set_grab_request() {
73 int16_t* tX_engine::render_cycle() {
74 /* Checking whether to grab or not */
75 if (grab_request!=grab_active) {
77 /* Activating grab... */
78 int result=mouse->grab();
80 tX_error("tX_engine::render_cycle(): failed to grab mouse - error %i", result);
82 /* Reseting grab_request, too - doesn't help keeping it, does it ? ;) */
90 /* Deactivating grab... */
93 grab_off(); // for the mastergui this is...
97 /* Handling input events... */
99 if (mouse->check_event()) {
100 /* If we're here the user pressed ESC */
105 /* Forward the sequencer... */
108 /* Render the next block... */
109 int16_t *data=vtt_class::render_all_turntables();
111 /* Record the audio if necessary... */
112 if (is_recording()) tape->eat(data);
117 void tX_engine::loop() {
118 while (!thread_terminate) {
119 /* Waiting for the trigger */
120 pthread_mutex_lock(&start);
122 pthread_mutex_unlock(&start);
124 if (!stop_flag) device->start(); // Hand flow control over to the device
126 /* Stopping engine... */
127 loop_is_active=false;
131 void *engine_thread_entry(void *engine_void) {
132 tX_engine *engine=(tX_engine*) engine_void;
135 /* Dropping root privileges for the engine thread - if running suid. */
137 if ((!geteuid()) && (getuid() != geteuid())) {
138 #ifdef USE_CAPABILITIES
139 tX_error("engine_thread_entry(): using capabilities but still suid root?");
140 tX_error("engine_thread_entry(): Please report this.");
144 #ifndef ALLOW_SUID_ROOT
145 tX_error("This binary doesn't support running suid-root.");
146 tX_error("Reconfigure with --enable-suidroot if you really want that.");
149 tX_debug("engine_thread_entry() - Running suid root - dropping privileges.");
151 result=setuid(getuid());
154 tX_error("engine_thread_entry() - Failed to drop root privileges.");
162 #ifdef USE_CAPABILITIES
163 if (have_nice_capability()) {
164 if (globals.use_realtime) {
165 struct sched_param parm;
167 sched_getparam(pid, &parm);
168 parm.sched_priority=sched_get_priority_max(SCHED_FIFO);
169 if (sched_setscheduler(pid, SCHED_FIFO, &parm)) {
170 tX_error("engine_thread_entry(): failed to set realtime priority.");
174 tX_warning("engine_thread_entry(): can't set SCHED_FIFO -> lacking capabilities.");
176 #endif //USE_CAPABILITIES
177 engine->set_pid(pid);
179 if (sched_getscheduler(pid)!=SCHED_FIFO) {
180 tX_warning("engine_thread_entry() - engine has no realtime priority scheduling.");
182 #endif //USE_SCHEDULER
185 /* Create the client now, so the user has something to connect to. */
186 tX_jack_client::get_instance();
191 tX_debug("engine_thread_entry() - Engine thread terminating.");
196 tX_engine :: tX_engine() {
199 pthread_mutex_init(&start, NULL);
200 pthread_mutex_lock(&start);
201 thread_terminate=false;
206 /* Creating the actual engine thread.. */
208 if (!geteuid() && globals.use_realtime) {
209 pthread_attr_t pattr;
210 struct sched_param sparm;
212 tX_debug("tX_engine() - Have root privileges - using SCHED_FIFO.");
214 pthread_attr_init(&pattr);
215 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
216 pthread_attr_setschedpolicy(&pattr, SCHED_FIFO);
218 sched_getparam(getpid(), &sparm);
219 sparm.sched_priority=sched_get_priority_max(SCHED_FIFO);
221 pthread_attr_setschedparam(&pattr, &sparm);
222 pthread_attr_setinheritsched(&pattr, PTHREAD_EXPLICIT_SCHED);
223 pthread_attr_setscope(&pattr, PTHREAD_SCOPE_SYSTEM);
225 result=pthread_create(&thread, &pattr, engine_thread_entry, (void *) this);
227 #endif // USE_SCHEDULER
228 result=pthread_create(&thread, NULL, engine_thread_entry, (void *) this);
234 tX_error("tX_engine() - Failed to create engine thread. Errno is %i.", errno);
238 /* Dropping root privileges for the main thread - if running suid. */
240 if ((!geteuid()) && (getuid() != geteuid())) {
241 tX_debug("tX_engine() - Running suid root - dropping privileges.");
243 result=setuid(getuid());
246 tX_error("tX_engine() - Failed to drop root privileges.");
251 mouse=new tx_mouse();
252 #ifdef USE_ALSA_MIDI_IN
253 midi=new tX_midiin();
255 tape=new tx_tapedeck();
259 recording_request=false;
260 loop_is_active=false;
265 void tX_engine :: set_recording_request (bool recording) {
266 this->recording_request=recording;
269 tX_engine_error tX_engine :: run() {
270 list <vtt_class *> :: iterator vtt;
272 if (loop_is_active) return ERROR_BUSY;
274 switch (globals.audiodevice_type) {
277 device=new tX_audiodevice_oss();
283 device=new tX_audiodevice_alsa();
289 device=new tX_audiodevice_jack();
294 device=NULL; return ERROR_AUDIO;
297 if (device->open()) {
298 if (device->get_is_open()) device->close();
304 vtt_class::set_sample_rate(device->get_sample_rate());
306 if (recording_request) {
307 if (tape->start_record(globals.record_filename, vtt_class::get_mix_buffer_size(), device->get_sample_rate())) {
317 for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
318 (*vtt)->sync_countdown=0;
319 if ((*vtt)->autotrigger) (*vtt)->trigger();
322 sequencer.forward_to_start_timestamp(1);
324 /* Trigger the engine thread... */
325 pthread_mutex_unlock(&start);
330 void tX_engine :: stop() {
331 list <vtt_class *> :: iterator vtt;
333 if (!loop_is_active) {
334 tX_error("tX_engine::stop() - but loop's not running?");
337 pthread_mutex_lock(&start);
340 tX_debug("tX_engine::stop() - waiting for loop to stop.");
342 while (loop_is_active) {
343 /* Due to gtk+ signal handling this can cause a deadlock
344 on the seqpars' update list. So we need to handle events
347 while (gtk_events_pending()) gtk_main_iteration();
351 tX_debug("tX_engine::stop() - loop has stopped.");
353 if (device->get_is_open()) device->close();
357 for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
359 (*vtt)->ec_clear_buffer();
362 if (is_recording()) tape->stop_record();
366 tX_engine :: ~tX_engine() {
369 thread_terminate=true;
371 pthread_mutex_unlock(&start);
372 tX_debug("~tX_engine() - Waiting for engine thread to terminate.");
373 pthread_join(thread, &dummy);
376 #ifdef USE_ALSA_MIDI_IN