18 new shapewipe transitions from rafa, rework savefile/confirm for nested edl edits
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / audiopulse.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008-2019 Adam Williams <broadcast at earthling dot net>
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  * 
19  */
20
21 #ifdef HAVE_PULSE
22 #include "audiopulse.h"
23 #include "adeviceprefs.h"
24 #include "bctimer.h"
25 #include "cstrdup.h"
26 #include "language.h"
27 #include "mutex.h"
28 #include "playbackconfig.h"
29 #include "recordconfig.h"
30
31 #include <pulse/pulseaudio.h>
32 #include <pulse/simple.h>
33
34 AudioPulse::AudioPulse(AudioDevice *device)
35  : AudioLowLevel(device)
36 {
37         buffer_position = 0;
38         timer_position = 0;
39         timer = new Timer;
40         timer_lock = new Mutex("AudioPulse::timer_lock");
41         dsp_in = 0;
42         dsp_out = 0;
43         period_usecs = 1000000;
44         frag_usecs = period_usecs / 32;
45         wr_spec = 0;
46         rd_spec = 0;
47 }
48
49 AudioPulse::~AudioPulse()
50 {
51         delete (pa_sample_spec *)wr_spec;
52         delete (pa_sample_spec *)rd_spec;
53         delete timer_lock;
54         delete timer;
55         close_all();
56 }
57
58 int AudioPulse::open_input()
59 {
60         pa_sample_spec *ss = new pa_sample_spec();
61         rd_spec = (void *)ss;
62
63         ss->format = PA_SAMPLE_S16LE;
64         ss->rate = device->in_samplerate; 
65         ss->channels = device->get_ichannels();   
66         device->in_bits = 16;
67         return init_input();
68 }
69
70 int AudioPulse::init_input()
71 {
72         pa_sample_spec *ss = (pa_sample_spec *)rd_spec;
73         int error = 0;
74         char *server = 0;
75         if( device->in_config->pulse_in_server[0] )
76                 server = device->in_config->pulse_in_server;
77         dsp_in = pa_simple_new(server, PROGRAM_NAME, PA_STREAM_RECORD, 
78                 0, "recording", ss, 0, 0, &error);
79         if( !dsp_in ) {
80                 printf("AudioPulse::open_input %d: failed server=%s %s\n", __LINE__, 
81                         server, pa_strerror(error));
82                 return 1;
83         }
84
85         return 0;
86 }
87
88
89 int AudioPulse::open_output()
90 {
91         pa_sample_spec *ss = new pa_sample_spec();
92         wr_spec = (void *)ss;
93         ss->format = PA_SAMPLE_S16LE;
94         ss->rate = device->out_samplerate;
95         ss->channels = device->get_ochannels();
96         device->out_bits = 16;
97         return init_output();
98 }
99
100 int AudioPulse::init_output()
101 {
102         pa_sample_spec *ss = (pa_sample_spec *)wr_spec;
103         int error = 0;
104         char *server = device->out_config->pulse_out_server[0] ?
105                 device->out_config->pulse_out_server : 0;
106         dsp_out = pa_simple_new(server, PROGRAM_NAME, PA_STREAM_PLAYBACK, 
107                 NULL, "playback", ss, 0, 0, &error);
108         if( !dsp_out ) {
109                 printf("AudioPulse::open_output %d: failed server=%s %s\n", 
110                         __LINE__, server, pa_strerror(error));
111                 return 1;
112         }
113         timer->update();
114         device->device_buffer = 0;
115         buffer_position = 0;
116         return 0;
117 }
118
119
120 int AudioPulse::close_all()
121 {
122         if( dsp_out ) {
123                 int error = 0;
124                 pa_simple_flush((pa_simple*)dsp_out, &error);
125                 pa_simple_free((pa_simple*)dsp_out);
126                 dsp_out = 0;
127         }
128
129         if( dsp_in ) {
130                 pa_simple_free((pa_simple*)dsp_in);
131                 dsp_in = 0;
132         }
133         delete (pa_sample_spec *)wr_spec;  wr_spec = 0;
134         delete (pa_sample_spec *)rd_spec;  rd_spec = 0;
135         buffer_position = 0;
136         timer_position = 0;
137         return 0;
138 }
139
140 int64_t AudioPulse::device_position()
141 {
142         timer_lock->lock("AudioPulse::device_position");
143         int64_t samples = timer->get_scaled_difference(device->out_samplerate);
144         int64_t result = timer_position + samples;
145         timer_lock->unlock();
146         return result;
147 }
148
149 int AudioPulse::write_buffer(char *buffer, int size)
150 {
151         if( !dsp_out && init_output() )
152                 return 1;
153         int error = 0;
154         timer_lock->lock("AudioPulse::write_buffer");
155         int64_t usecs = pa_simple_get_latency((pa_simple*)dsp_out, &error);
156         int64_t delay = device->out_samplerate * usecs / 1000000;
157         timer_position = buffer_position - delay;
158         timer->update();
159         timer_lock->unlock();
160
161         AudioThread *audio_out = device->audio_out;
162         int sample_size = (device->out_bits / 8) * device->get_ochannels();
163         int samples = device->out_samplerate * frag_usecs / 1000000;
164         int64_t frag_bytes = samples * sample_size;
165
166         buffer_position += size / sample_size;
167         int ret = 0;
168         while( !ret && size > 0 && !device->playback_interrupted ) {
169                 audio_out->Thread::enable_cancel();
170                 usecs = pa_simple_get_latency((pa_simple*)dsp_out, &error);
171                 if( usecs < period_usecs ) {
172                         int64_t len = size;
173                         if( len > frag_bytes ) len = frag_bytes;
174                         if( pa_simple_write((pa_simple*)dsp_out, buffer, len, &error) < 0 )
175                                 ret = 1;
176                         buffer += len;  size -= len;
177                 }
178                 else
179                         usleep(frag_usecs);
180                 audio_out->Thread::disable_cancel();
181         }
182         if( ret )
183                 printf("AudioPulse::write_buffer %d: %s\n", 
184                         __LINE__, pa_strerror(error));
185         return ret;
186 }
187
188 int AudioPulse::read_buffer(char *buffer, int size)
189 {
190         if( !dsp_in && init_input() )
191                 return 1;
192
193         int error = 0;
194         int result = pa_simple_read((pa_simple*)dsp_in, buffer, size, &error);
195         if( result < 0 ) {
196                 printf("AudioPulse::read_buffer %d: %s\n", 
197                         __LINE__, pa_strerror(error));
198                 return 1;
199         }
200
201 //printf("AudioPulse::read_buffer %d %d\n", __LINE__, size);
202
203         return 0;
204 }
205
206 int AudioPulse::output_wait()
207 {
208         int error = 0;
209         pa_usec_t latency = pa_simple_get_latency((pa_simple*)dsp_out, &error);
210         int64_t udelay = latency;
211         while( udelay > 0 && !device->playback_interrupted ) {
212                 int64_t usecs = udelay;
213                 if( usecs > 100000 ) usecs = 100000;
214                 usleep(usecs);
215                 udelay -= usecs;
216         }
217         if( device->playback_interrupted &&
218             !device->out_config->interrupt_workaround )
219                 pa_simple_flush((pa_simple*)dsp_out, &error);
220         return 0;
221 }
222
223 int AudioPulse::flush_device()
224 {
225         if( dsp_out ) {
226                 output_wait();
227                 int error = 0;
228                 pa_simple_drain((pa_simple*)dsp_out, &error);
229         }
230         return 0;
231 }
232
233 int AudioPulse::interrupt_playback()
234 {
235         if( !dsp_out ) {
236                 return 1;
237         }
238
239         int error = 0;
240         int result = pa_simple_flush((pa_simple*)dsp_out, &error);
241         if( result < 0 ) {
242                 printf("AudioPulse::interrupt_playback %d: %s\n", 
243                         __LINE__,
244                         pa_strerror(error));
245                 return 1;
246         }
247
248         return 0;
249 }
250
251 #endif