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