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;
129 void *engine_thread_entry(void *engine_void) {
130 tX_engine *engine=(tX_engine*) engine_void;
133 /* Dropping root privileges for the engine thread - if running suid. */
135 if ((!geteuid()) && (getuid() != geteuid())) {
137 #ifndef ALLOW_SUID_ROOT
138 tX_error("This binary doesn't support running suid-root.");
139 tX_error("Reconfigure with --enable-suidroot if you really want that.");
142 tX_debug("engine_thread_entry() - Running suid root - dropping privileges.");
144 result=setuid(getuid());
147 tX_error("engine_thread_entry() - Failed to drop root privileges.");
153 /* Create the client now, so the user has something to connect to. */
154 tX_jack_client *jack_client=tX_jack_client::get_instance();
159 tX_debug("engine_thread_entry() - Engine thread terminating.");
164 tX_engine :: tX_engine() {
167 pthread_mutex_init(&start, NULL);
168 pthread_mutex_lock(&start);
169 thread_terminate=false;
171 /* Creating the actual engine thread.. */
174 pthread_attr_t pattr;
175 struct sched_param sparm;
177 tX_debug("tX_engine() - Have root privileges - using SCHED_FIFO.");
179 pthread_attr_init(&pattr);
180 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
181 pthread_attr_setschedpolicy(&pattr, SCHED_FIFO);
183 sched_getparam(getpid(), &sparm);
184 sparm.sched_priority=sched_get_priority_max(SCHED_FIFO);
186 pthread_attr_setschedparam(&pattr, &sparm);
187 pthread_attr_setinheritsched(&pattr, PTHREAD_EXPLICIT_SCHED);
188 pthread_attr_setscope(&pattr, PTHREAD_SCOPE_SYSTEM);
190 result=pthread_create(&thread, &pattr, engine_thread_entry, (void *) this);
192 tX_debug("tX_engine() - Lacking root privileges - no realtime scheduling.");
194 result=pthread_create(&thread, NULL, engine_thread_entry, (void *) this);
200 tX_error("tX_engine() - Failed to create engine thread. Errno is %i.", errno);
204 /* Dropping root privileges for the main thread - if running suid. */
206 if ((!geteuid()) && (getuid() != geteuid())) {
207 tX_debug("tX_engine() - Running suid root - dropping privileges.");
209 result=setuid(getuid());
212 tX_error("tX_engine() - Failed to drop root privileges.");
217 mouse=new tx_mouse();
218 #ifdef USE_ALSA_MIDI_IN
219 midi=new tX_midiin();
221 tape=new tx_tapedeck();
225 recording_request=false;
226 loop_is_active=false;
231 void tX_engine :: set_recording_request (bool recording) {
232 this->recording_request=recording;
235 tX_engine_error tX_engine :: run() {
236 list <vtt_class *> :: iterator vtt;
238 if (loop_is_active) return ERROR_BUSY;
240 switch (globals.audiodevice_type) {
243 device=new tX_audiodevice_oss();
249 device=new tX_audiodevice_alsa();
255 device=new tX_audiodevice_jack();
260 device=NULL; return ERROR_AUDIO;
263 if (device->open()) {
264 if (device->get_is_open()) device->close();
270 vtt_class::set_sample_rate(device->get_sample_rate());
272 if (recording_request) {
273 if (tape->start_record(globals.record_filename, vtt_class::get_mix_buffer_size(), device->get_sample_rate())) {
283 for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
284 (*vtt)->sync_countdown=0;
285 if ((*vtt)->autotrigger) (*vtt)->trigger();
288 sequencer.forward_to_start_timestamp(1);
290 /* Trigger the engine thread... */
291 pthread_mutex_unlock(&start);
296 void tX_engine :: stop() {
297 list <vtt_class *> :: iterator vtt;
299 if (!loop_is_active) {
300 tX_error("tX_engine::stop() - but loop's not running?");
303 pthread_mutex_lock(&start);
306 tX_debug("tX_engine::stop() - waiting for loop to stop.");
308 while (loop_is_active) {
309 /* Due to gtk+ signal handling this can cause a deadlock
310 on the seqpars' update list. So we need to handle events
313 while (gtk_events_pending()) gtk_main_iteration();
317 tX_debug("tX_engine::stop() - loop has stopped.");
319 if (device->get_is_open()) device->close();
323 for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
325 (*vtt)->ec_clear_buffer();
328 if (is_recording()) tape->stop_record();
332 tX_engine :: ~tX_engine() {
335 thread_terminate=true;
337 pthread_mutex_unlock(&start);
338 tX_debug("~tX_engine() - Waiting for engine thread to terminate.");
339 pthread_join(thread, &dummy);
342 #ifdef USE_ALSA_MIDI_IN