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