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