06eb7d28dd3a54457c108cc9881faaef8bf34fe8
[terminatorX.git] / src / tX_engine.cc
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2003  Alexander K├Ânig
4  
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.
9  
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.
14  
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.
18  
19     File: tX_engine.c
20  
21     Description: Contains the code that does the real "Scratching
22                  business": XInput, DGA, Mouse and Keyboardgrabbing
23                  etc.
24
25     02 Jun 1999: Implemented high-priority/rt-FIFO-Scheduling use for
26                  engine-thread.
27                  
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.
32                  
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
35 */    
36
37 #include <config.h>
38 #include "tX_types.h"
39 #include "tX_engine.h"
40 #include "tX_audiodevice.h"
41 #include "tX_mouse.h"
42 #include "tX_vtt.h"
43 #include <pthread.h>
44 #include <gtk/gtk.h>
45 #include <gdk/gdkprivate.h>
46 #include "tX_mastergui.h"
47 #include "tX_global.h"
48 #include "tX_tape.h"
49 #include "tX_widget.h"
50 #include <config.h>
51 #include "tX_sequencer.h"
52 #include <errno.h>
53
54 #include <sys/time.h>
55 #include <sys/resource.h>
56
57 tX_engine *engine=NULL;
58
59 void tX_engine :: set_grab_request() {
60         grab_request=true;
61 }
62
63 void tX_engine :: loop() {
64         int16_t *temp=NULL;
65         int result;
66         
67         while (!thread_terminate) {
68                 /* Waiting for the trigger */
69                 pthread_mutex_lock(&start);
70                 loop_is_active=true;
71                 pthread_mutex_unlock(&start);
72
73                 /* Render first block */
74                 if (!stop_flag) {
75                         sequencer.step();
76                         temp=vtt_class::render_all_turntables();
77                 }
78                 
79                 while (!stop_flag) {
80                         /* Checking whether to grab or not  */
81                         if (grab_request!=grab_active) {
82                                 if (grab_request) {
83                                         /* Activating grab... */
84                                         result=mouse->grab(); 
85                                         if (result!=0) {
86                                                 tX_error("tX_engine::loop(): failed to grab mouse - error %i", result);
87                                                 grab_active=false;
88                                                 /* Reseting grab_request, too - doesn't help keeping it, does it ? ;) */
89                                                 grab_request=false;
90                                                 mouse->ungrab();
91                                                 grab_off();
92                                         } else {
93                                                 grab_active=true;
94                                         }
95                                 } else {
96                                         /* Deactivating grab... */
97                                         mouse->ungrab();
98                                         grab_active=false;
99                                         grab_off(); // for the mastergui this is...
100                                 }
101                         }
102
103                         /* Handling input events... */
104                         if (grab_active) {
105                                 if (mouse->check_event()) {
106                                         /* If we're here the user pressed ESC */
107                                         grab_request=false;
108                                 }
109                         }
110
111 #ifdef USE_ALSA_MIDI_IN                 
112                         if (midi->get_is_open()) midi->check_event();
113 #endif                  
114                 
115                         /* Playback the audio... */
116                         device->play(temp);
117                 
118                         /* Record the audio if necessary... */
119                         if (is_recording()) tape->eat(temp);
120                         
121                         /* Forward the sequencer... */
122                         sequencer.step();
123                         
124                         /* Render the next block... */
125                         temp=vtt_class::render_all_turntables();                                        
126                 }
127                 
128                 /* Stopping engine... */
129                 loop_is_active=false;
130         }
131 }
132
133 void *engine_thread_entry(void *engine_void) {
134         tX_engine *engine=(tX_engine*) engine_void;
135         int result;
136         
137         /* Dropping root privileges for the engine thread - if running suid. */
138         
139         if ((!geteuid()) && (getuid() != geteuid())) {
140                 tX_debug("engine_thread_entry() - Running suid root - dropping privileges.");
141                 
142                 result=setuid(getuid());
143                 
144                 if (result!=0) {
145                         tX_error("engine_thread_entry() - Failed to drop root privileges.");
146                         exit(2);
147                 }
148         }
149         
150         engine->loop();
151         
152         tX_debug("engine_thread_entry() - Engine thread terminating.");
153         
154         pthread_exit(NULL);
155 }
156
157 tX_engine :: tX_engine() {
158         int result;
159         
160         pthread_mutex_init(&start, NULL);
161         pthread_mutex_lock(&start);
162         thread_terminate=false;
163         
164         /* Creating the actual engine thread.. */
165 #ifdef USE_SCHEDULER    
166         if (!geteuid()) {
167                 pthread_attr_t pattr;
168                 struct sched_param sparm;
169                 
170                 tX_debug("tX_engine() - Have root privileges - using SCHED_FIFO.");
171                 
172                 pthread_attr_init(&pattr);
173                 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
174                 pthread_attr_setschedpolicy(&pattr, SCHED_FIFO);
175         
176                 sched_getparam(getpid(), &sparm);
177                 sparm.sched_priority=sched_get_priority_max(SCHED_FIFO);
178         
179                 pthread_attr_setschedparam(&pattr, &sparm);
180                 pthread_attr_setinheritsched(&pattr, PTHREAD_EXPLICIT_SCHED);
181                 pthread_attr_setscope(&pattr, PTHREAD_SCOPE_SYSTEM);
182                 
183                 result=pthread_create(&thread, &pattr, engine_thread_entry, (void *) this);
184         } else {
185                 tX_debug("tX_engine() - Lacking root privileges - no realtime scheduling!");
186 #endif          
187                 result=pthread_create(&thread, NULL, engine_thread_entry, (void *) this);
188 #ifdef USE_SCHEDULER            
189         }
190 #endif
191         
192         if (result!=0) {
193                 tX_error("tX_engine() - Failed to create engine thread. Errno is %i.", errno);
194                 exit(1);
195         }
196         
197         /* Dropping root privileges for the main thread - if running suid. */
198         
199         if ((!geteuid()) && (getuid() != geteuid())) {
200                 tX_debug("tX_engine() - Running suid root - dropping privileges.");
201                 
202                 result=setuid(getuid());
203                 
204                 if (result!=0) {
205                         tX_error("tX_engine() - Failed to drop root privileges.");
206                         exit(2);
207                 }
208         }
209         
210         mouse=new tx_mouse();
211 #ifdef USE_ALSA_MIDI_IN 
212         midi=new tX_midiin();
213 #endif  
214         tape=new tx_tapedeck();
215         device=NULL;
216         recording=false;
217         recording_request=false;
218         loop_is_active=false;
219         grab_request=false;
220         grab_active=false;
221 }
222
223 void tX_engine :: set_recording_request (bool recording) {
224         this->recording_request=recording;
225 }
226
227 tX_engine_error tX_engine :: run() {
228         list <vtt_class *> :: iterator vtt;
229         
230         if (loop_is_active) return ERROR_BUSY;
231         
232         switch (globals.audiodevice_type) {
233 #ifdef USE_OSS  
234                 case OSS:
235                         puts("Using OSS output...");
236                         device=new tX_audiodevice_oss(); 
237                 break;
238 #endif                  
239
240 #ifdef USE_ALSA                 
241                 case ALSA:
242                         puts("Using ALSA output...");
243                         device=new tX_audiodevice_alsa(); 
244                 break;
245 #endif
246                 
247                 default:
248                         device=NULL; return ERROR_AUDIO;
249         }
250         
251         if (device->open()) {
252                 device->close();
253                 delete device;
254                 device=NULL;            
255                 return ERROR_AUDIO;
256         }       
257
258         vtt_class::set_mix_buffer_size(device->get_buffersize()/2); //mixbuffer is mono
259         
260         if (recording_request) {
261                 if (tape->start_record(globals.record_filename, device->get_buffersize()*sizeof(int16_t))) {
262                         device->close();
263                         delete device;
264                         device=NULL;                    
265                         return ERROR_TAPE;                      
266                 } else {
267                         recording=true;
268                 }
269         }
270         
271         for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
272                 if ((*vtt)->autotrigger) (*vtt)->trigger();
273         }
274
275         sequencer.forward_to_start_timestamp(1);        
276         stop_flag=false;
277         /* Trigger the engine thread... */
278         pthread_mutex_unlock(&start);
279         
280         return NO_ERROR;
281 }
282
283 void tX_engine :: stop() {
284         list <vtt_class *> :: iterator vtt;
285         
286         if (!loop_is_active) {
287                 tX_error("tX_engine::stop() - but loop's not running?");
288         }
289         
290         pthread_mutex_lock(&start);
291         stop_flag=true;
292         
293         tX_debug("tX_engine::stop() - waiting for loop to stop.");
294         
295         while (loop_is_active) {
296                 usleep(50);
297         }
298         
299         tX_debug("tX_engine::stop() - loop has stopped.");
300
301         device->close();
302         delete device;
303         device=NULL;
304         
305         for (vtt=vtt_class::main_list.begin(); vtt!=vtt_class::main_list.end(); vtt++) {
306                 (*vtt)->stop();
307                 (*vtt)->ec_clear_buffer();
308         }
309         
310         if (is_recording()) tape->stop_record();
311         recording=false;
312 }
313
314 tX_engine :: ~tX_engine() {
315         void *dummy;
316         
317         thread_terminate=true;
318         stop_flag=true;
319         pthread_mutex_unlock(&start);
320         tX_debug("~tX_engine() - Waiting for engine thread to terminate.");
321         pthread_join(thread, &dummy);   
322 }