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"
55 #include <sys/resource.h>
57 tX_engine *tX_engine :: engine=NULL;
59 tX_engine *tX_engine :: get_instance() {
61 engine=new tX_engine();
67 void tX_engine :: set_grab_request() {
71 int16_t* tX_engine :: render_cycle() {
72 /* Checking whether to grab or not */
73 if (grab_request!=grab_active) {
75 /* Activating grab... */
76 int result=mouse->grab();
78 tX_error("tX_engine::loop(): failed to grab mouse - error %i", result);
80 /* Reseting grab_request, too - doesn't help keeping it, does it ? ;) */
88 /* Deactivating grab... */
91 grab_off(); // for the mastergui this is...
95 /* Handling input events... */
97 if (mouse->check_event()) {
98 /* If we're here the user pressed ESC */
103 /* Forward the sequencer... */
106 /* Render the next block... */
107 int16_t *data=vtt_class::render_all_turntables();
109 /* Record the audio if necessary... */
110 if (is_recording()) tape->eat(data);
115 void tX_engine :: loop() {
116 while (!thread_terminate) {
117 /* Waiting for the trigger */
118 pthread_mutex_lock(&start);
120 pthread_mutex_unlock(&start);
122 if (!stop_flag) device->start(); // Hand flow control over to the device
124 /* Stopping engine... */
125 loop_is_active=false;
130 void *engine_thread_entry(void *engine_void) {
131 tX_engine *engine=(tX_engine*) engine_void;
134 /* Dropping root privileges for the engine thread - if running suid. */
136 if ((!geteuid()) && (getuid() != geteuid())) {
137 tX_debug("engine_thread_entry() - Running suid root - dropping privileges.");
139 result=setuid(getuid());
142 tX_error("engine_thread_entry() - Failed to drop root privileges.");
149 tX_debug("engine_thread_entry() - Engine thread terminating.");
154 tX_engine :: tX_engine() {
157 pthread_mutex_init(&start, NULL);
158 pthread_mutex_lock(&start);
159 thread_terminate=false;
161 /* Creating the actual engine thread.. */
164 pthread_attr_t pattr;
165 struct sched_param sparm;
167 tX_debug("tX_engine() - Have root privileges - using SCHED_FIFO.");
169 pthread_attr_init(&pattr);
170 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
171 pthread_attr_setschedpolicy(&pattr, SCHED_FIFO);
173 sched_getparam(getpid(), &sparm);
174 sparm.sched_priority=sched_get_priority_max(SCHED_FIFO);
176 pthread_attr_setschedparam(&pattr, &sparm);
177 pthread_attr_setinheritsched(&pattr, PTHREAD_EXPLICIT_SCHED);
178 pthread_attr_setscope(&pattr, PTHREAD_SCOPE_SYSTEM);
180 result=pthread_create(&thread, &pattr, engine_thread_entry, (void *) this);
182 tX_debug("tX_engine() - Lacking root privileges - no realtime scheduling!");
184 result=pthread_create(&thread, NULL, engine_thread_entry, (void *) this);
190 tX_error("tX_engine() - Failed to create engine thread. Errno is %i.", errno);
194 /* Dropping root privileges for the main thread - if running suid. */
196 if ((!geteuid()) && (getuid() != geteuid())) {
197 tX_debug("tX_engine() - Running suid root - dropping privileges.");
199 result=setuid(getuid());
202 tX_error("tX_engine() - Failed to drop root privileges.");
207 mouse=new tx_mouse();
208 #ifdef USE_ALSA_MIDI_IN
209 midi=new tX_midiin();
211 tape=new tx_tapedeck();
215 recording_request=false;
216 loop_is_active=false;
221 void tX_engine :: set_recording_request (bool recording) {
222 this->recording_request=recording;
225 tX_engine_error tX_engine :: run() {
226 list <vtt_class *> :: iterator vtt;
228 if (loop_is_active) return ERROR_BUSY;
230 switch (globals.audiodevice_type) {
233 device=new tX_audiodevice_oss();
239 device=new tX_audiodevice_alsa();
244 device=NULL; return ERROR_AUDIO;
247 if (device->open()) {
248 if (device->get_is_open()) device->close();
254 vtt_class::set_sample_rate(device->get_sample_rate());
256 if (recording_request) {
257 if (tape->start_record(globals.record_filename, vtt_class::get_mix_buffer_size(), device->get_sample_rate())) {
267 for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
268 if ((*vtt)->autotrigger) (*vtt)->trigger();
271 sequencer.forward_to_start_timestamp(1);
273 /* Trigger the engine thread... */
274 pthread_mutex_unlock(&start);
279 void tX_engine :: stop() {
280 list <vtt_class *> :: iterator vtt;
282 if (!loop_is_active) {
283 tX_error("tX_engine::stop() - but loop's not running?");
286 pthread_mutex_lock(&start);
289 tX_debug("tX_engine::stop() - waiting for loop to stop.");
291 while (loop_is_active) {
292 /* Due to gtk+ signal handling this can cause a deadlock
293 on the seqpars' update list. So we need to handle events
296 while (gtk_events_pending()) gtk_main_iteration();
300 tX_debug("tX_engine::stop() - loop has stopped.");
302 if (device->get_is_open()) device->close();
306 for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
308 (*vtt)->ec_clear_buffer();
311 if (is_recording()) tape->stop_record();
315 tX_engine :: ~tX_engine() {
318 thread_terminate=true;
320 pthread_mutex_unlock(&start);
321 tX_debug("~tX_engine() - Waiting for engine thread to terminate.");
322 pthread_join(thread, &dummy);