1 /*
2 * Copyright (C) 2004 2005, Magnus Hjorth
3 *
4 * This file is part of mhWaveEdit.
5 *
6 * mhWaveEdit is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * mhWaveEdit is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with mhWaveEdit; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 /* This module was re-written completely from scratch in Sep 2004.
22 * Thanks to Erica Andrews for contributing the original esound
23 * driver. */
24
25 #include <esd.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include "float_box.h"
29 #include "gettext.h"
30
31 #define ESOUND_CLEARDELAY "EsoundClearDelay"
32 #define ESOUND_CLEARDELAY_DEFAULT 0.4
33
34 /* Playback/Recording connections (<= 0 if not connected) */
35 static int esound_play_fd = -1;
36 static int esound_record_fd = -1;
37 static Dataformat esound_play_format;
38 static float esound_clear_delay;
39 static GTimeVal esound_noflush_stop_time = {};
40
esound_prefs_init(void)41 static void esound_prefs_init(void)
42 {
43 esound_clear_delay = inifile_get_gfloat(ESOUND_CLEARDELAY,
44 ESOUND_CLEARDELAY_DEFAULT);
45 }
46
esound_init(gboolean silent)47 static gboolean esound_init(gboolean silent)
48 {
49 /* Just try once here so the user gets a clear error message if ESD
50 * isn't available */
51 int fd;
52 gchar *c;
53 esound_prefs_init();
54 if (getenv("ESD_NO_SPAWN") == NULL)
55 putenv("ESD_NO_SPAWN=y");
56 fd = esd_open_sound(NULL);
57 if (fd < 0) {
58 if (!silent) {
59 c = g_strdup_printf(_("Couldn't connect to ESD daemon: %s"),
60 strerror(errno));
61 user_error(c);
62 g_free(c);
63 }
64 return FALSE;
65 }
66 esd_close(fd);
67 return TRUE;
68 }
69
esound_quit(void)70 static void esound_quit(void)
71 {
72 }
73
esound_select_format(Dataformat * format,int * fdp,gboolean recording)74 static void esound_select_format(Dataformat *format, int *fdp,
75 gboolean recording)
76 {
77 int fmt = ESD_STREAM;
78 g_assert(*fdp < 0);
79 if (recording) fmt |= ESD_RECORD; else fmt |= ESD_PLAY;
80 if (format->samplesize == 1) fmt |= ESD_BITS8;
81 else if (format->samplesize == 2) fmt |= ESD_BITS16;
82 else return;
83 if (format->channels == 1) fmt |= ESD_MONO;
84 else if (format->channels == 2) fmt |= ESD_STEREO;
85 else return;
86 if (recording)
87 *fdp = esd_record_stream(fmt,format->samplerate,
88 NULL,"mhWaveEdit");
89 else
90 *fdp = esd_play_stream(fmt,format->samplerate,NULL,
91 "mhWaveEdit");
92 }
93
esound_output_select_format(Dataformat * format,gboolean silent,GVoidFunc ready_func)94 static gint esound_output_select_format(Dataformat *format, gboolean silent,
95 GVoidFunc ready_func)
96 {
97 GTimeVal tv,r;
98 int i;
99 unsigned long j,k;
100
101 if (format->type == DATAFORMAT_FLOAT) return -1;
102 g_get_current_time(&tv);
103 i = timeval_subtract(&r,&tv,&esound_noflush_stop_time);
104 if (i == 0) {
105 j = r.tv_sec * 1000000 + r.tv_usec;
106 /* It seems we need a much shorter delay here than when jumping.
107 * Fixing it to 0.1 seconds for now */
108 k = 100000;
109 if (r.tv_sec < 4 && j<k) usleep(k-j);
110 }
111 esound_select_format(format,&esound_play_fd,FALSE);
112 if (esound_play_fd < 0) {
113 if (!silent) {
114 user_perror(_("EsounD: Unable to open playback stream"));
115 return 1;
116 }
117 return -1;
118 }
119 memcpy(&esound_play_format,format,sizeof(Dataformat));
120 return 0;
121 }
122
esound_input_select_format(Dataformat * format,gboolean silent,GVoidFunc ready_func)123 static gint esound_input_select_format(Dataformat *format, gboolean silent,
124 GVoidFunc ready_func)
125 {
126 if (format->type == DATAFORMAT_FLOAT) return -1;
127 esound_select_format(format,&esound_record_fd,TRUE);
128 if (esound_record_fd < 0) {
129 if (!silent)
130 user_perror(_("EsounD: Unable to open recording stream"));
131 return -1;
132 }
133 return 0;
134 }
135
esound_output_want_data(void)136 static gboolean esound_output_want_data(void)
137 {
138 fd_set wfds;
139 int i;
140 struct timeval t = { 0.0, 0.0 };
141 FD_ZERO(&wfds);
142 FD_SET(esound_play_fd,&wfds);
143 i = select(esound_play_fd+1, NULL, &wfds, NULL, &t);
144 if (i == -1) {
145 console_perror(_("EsounD driver: select"));
146 return TRUE;
147 }
148 return (i>0);
149 }
150
esound_output_play(gchar * buf,guint len)151 static guint esound_output_play(gchar *buf, guint len)
152 {
153 ssize_t sst;
154 if (buf == NULL || len == 0) return 0;
155 sst = write(esound_play_fd, buf, len);
156 if (sst < 0) {
157 console_perror(_("EsounD driver: write"));
158 return 0;
159 }
160 /* FIXME: It's theoretically possible that just part of a sample was
161 * written if the socket buffer size isn't a multiple of 4. */
162 return (guint) sst;
163
164 }
165
166 /* Both esound_output_stop and esound_output_clear_buffers
167 * should wait for all data in the pipeline to be played, but I don't know
168 * how to find out how long time that should be. Use a configurable setting
169 * for now. */
170
esound_output_clear_buffers(void)171 static void esound_output_clear_buffers(void)
172 {
173 if (esound_clear_delay > 0)
174 usleep((unsigned long)(esound_clear_delay * 1000000.0));
175 return;
176 }
177
esound_output_stop(gboolean must_flush)178 static gboolean esound_output_stop(gboolean must_flush)
179 {
180 if (esound_play_fd >= 0) {
181 /* If we want to guarantee all data to be sent, we must wait a
182 * while. Otherwise, set the first_play variable so we don't restart
183 * immediately. */
184 if (must_flush)
185 esound_output_clear_buffers(); /* Just waits... */
186 else
187 g_get_current_time(&esound_noflush_stop_time);
188
189 esd_close(esound_play_fd);
190 }
191 esound_play_fd = -1;
192 return must_flush;
193 }
194
esound_input_stop(void)195 static void esound_input_stop(void)
196 {
197 if (esound_record_fd >= 0) esd_close(esound_record_fd);
198 esound_record_fd = -1;
199 }
200
esound_input_store(Ringbuf * buffer)201 static void esound_input_store(Ringbuf *buffer)
202 {
203 gchar buf[4096];
204 int i;
205 fd_set set;
206 struct timeval tv = { 0, 0 };
207 int sz;
208 if (esound_record_fd < 0) return;
209 while (1) {
210 sz = ringbuf_freespace(buffer);
211 if (sz > sizeof(buf)) sz = sizeof(buf);
212 if (sz == 0) return;
213 FD_ZERO(&set);
214 FD_SET(esound_record_fd, &set);
215 i = select(esound_record_fd+1, &set, NULL, NULL, &tv);
216 if (i<0)
217 console_perror(_("EsounD driver: select"));
218 if (i == 0) return;
219 i = read(esound_record_fd,buf,sz);
220 if (i == 0) {
221 user_error(_("Esound connection closed by server"));
222 esound_record_fd = -1;
223 return;
224 }
225 if (i<0) {
226 if (errno == EAGAIN || errno == EINTR) continue;
227 console_perror(_("EsounD driver: read"));
228 return;
229 }
230 ringbuf_enqueue(buffer,buf,i);
231 }
232 }
233
234 struct esound_prefdlg {
235 GtkWindow *wnd;
236 Floatbox *jd;
237 };
238
esound_preferences_ok(GtkButton * button,struct esound_prefdlg * pd)239 static void esound_preferences_ok(GtkButton *button, struct esound_prefdlg *pd)
240 {
241 if (floatbox_check_limit(pd->jd,0.0,4.0,_("jump delay"))) return;
242 esound_clear_delay = pd->jd->val;
243 inifile_set_gfloat(ESOUND_CLEARDELAY,esound_clear_delay);
244 gtk_widget_destroy(GTK_WIDGET(pd->wnd));
245 }
246
esound_preferences(void)247 static void esound_preferences(void)
248 {
249 GtkWidget *a,*b,*c,*d;
250 struct esound_prefdlg *pd;
251 esound_prefs_init();
252 pd = g_malloc(sizeof(*pd));
253 a = gtk_window_new(GTK_WINDOW_DIALOG);
254 gtk_window_set_modal(GTK_WINDOW(a),TRUE);
255 gtk_window_set_title(GTK_WINDOW(a),_("ESD preferences"));
256 gtk_window_set_position(GTK_WINDOW(a),GTK_WIN_POS_CENTER);
257 gtk_container_set_border_width(GTK_CONTAINER(a),5);
258 gtk_signal_connect_object(GTK_OBJECT(a),"destroy",GTK_SIGNAL_FUNC(g_free),
259 (GtkObject *)pd);
260 pd->wnd = GTK_WINDOW(a);
261 b = gtk_vbox_new(FALSE,5);
262 gtk_container_add(GTK_CONTAINER(a),b);
263 c = gtk_label_new(_("If the cursor starts running too early when jumping, "
264 "this delay should be increased."));
265 gtk_label_set_line_wrap(GTK_LABEL(c),TRUE);
266 gtk_container_add(GTK_CONTAINER(b),c);
267 c = gtk_hbox_new(FALSE,3);
268 gtk_container_add(GTK_CONTAINER(b),c);
269 d = gtk_label_new(_("Jump delay:"));
270 gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
271 d = floatbox_new(esound_clear_delay);
272 gtk_box_pack_start(GTK_BOX(c),d,TRUE,TRUE,0);
273 pd->jd = FLOATBOX(d);
274 d = gtk_label_new(_("seconds"));
275 gtk_box_pack_end(GTK_BOX(c),d,FALSE,FALSE,0);
276 c = gtk_hseparator_new();
277 gtk_container_add(GTK_CONTAINER(b),c);
278 c = gtk_label_new(_("Selecting the ESD host to connect to is done through "
279 "the ESPEAKER environment variable."));
280 gtk_label_set_line_wrap(GTK_LABEL(c),TRUE);
281 gtk_container_add(GTK_CONTAINER(b),c);
282 c = gtk_hseparator_new();
283 gtk_container_add(GTK_CONTAINER(b),c);
284 c = gtk_hbutton_box_new();
285 gtk_container_add(GTK_CONTAINER(b),c);
286 d = gtk_button_new_with_label(_("OK"));
287 gtk_signal_connect(GTK_OBJECT(d),"clicked",
288 GTK_SIGNAL_FUNC(esound_preferences_ok),pd);
289 gtk_container_add(GTK_CONTAINER(c),d);
290 d = gtk_button_new_with_label(_("Close"));
291 gtk_signal_connect_object(GTK_OBJECT(d),"clicked",
292 GTK_SIGNAL_FUNC(gtk_widget_destroy),
293 GTK_OBJECT(a));
294 gtk_container_add(GTK_CONTAINER(c),d);
295 gtk_widget_show_all(a);
296 }
297
298