1 /*
2  * Copyright (C) 2003 2004 2005 2006 2008 2009 2010, 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 
22 #include <config.h>
23 
24 #include <glib.h>
25 #include "inifile.h"
26 #include "sound.h"
27 #include "player.h"
28 #include "main.h"
29 #include "um.h"
30 #include "rateest.h"
31 #include "rateconv.h"
32 #include "gettext.h"
33 #include "mainloop.h"
34 
35 Dataformat player_fallback_format = { DATAFORMAT_PCM, 44100, 2, 2, 4, TRUE,
36 				      IS_BIGENDIAN };
37 
38 gboolean varispeed_smooth_flag = FALSE;
39 
40 /* Info about currently playing sound. */
41 static ChunkHandle *ch=NULL; /* NULL == stopped */
42 static rateconv *varispeed_conv = NULL;
43 /* Note speed is inverse relative to output sample rate */
44 /* file_speed is the speed which the user "hears" */
45 static gfloat file_speed = 1.0; /* Speed relative to file's sample rate (1.0=Normal speed)*/
46 static gfloat true_speed = 1.0; /* Speed relative to playback sample rate (file_speed* file_rate/output_rate) */
47 static gfloat file_output_ratio = 1.0; /* file_rate/output_rate */
48 static off_t loopstart, loopend, curpos;
49 static off_t realpos_offset;
50 static gboolean loop, small_loop;
51 
52 /* After changing speed with the "smooth" method, the data that has already
53  * been sent to the driver will be played with the old speed. So we need to
54  * keep track of how much data that is. */
55 static off_t oldspeed_samples = 0;
56 static gfloat oldspeed_speed = 1.0;
57 
58 
59 /* Info after stopping */
60 static off_t realpos_atstop = 0;
61 
62 /* Output buffer */
63 static gchar varispeed_buf[8192];
64 static gint varispeed_bufsize,varispeed_bufpos;
65 static gchar player_buf[4096],small_loop_buf[4096];
66 static gint player_bufsize,player_bufpos,small_loop_bufsize;
67 
68 static player_notify_func notify_func;
69 
70 static gpointer notify_watchdog = NULL;
71 
72 static off_t get_realpos_main(off_t frames_played);
73 static void player_stop_main(gboolean read_stoppos);
74 
notify_wd_cb(gpointer timesource,GTimeVal * current_time,gpointer user_data)75 static gint notify_wd_cb(gpointer timesource, GTimeVal *current_time,
76 			 gpointer user_data)
77 {
78      if (ch == NULL) return 0;
79      notify_func(get_realpos_main(rateest_frames_played()),curpos,TRUE);
80      return 50;
81 }
82 
get_frames(void * buffer,int maxsize)83 static int get_frames(void *buffer, int maxsize)
84 {
85      off_t frames,sz;
86      int read;
87      if (curpos < loopstart)
88 	  frames=loopstart-curpos;
89      else if (curpos == loopstart && small_loop) {
90 	  memcpy(buffer, small_loop_buf, small_loop_bufsize);
91 	  return small_loop_bufsize;
92      } else if (curpos < loopend)
93 	  frames = loopend-curpos;
94      else if (!loop && curpos == loopend)
95 	  frames = 0;
96      else
97 	  frames = ch->length - curpos;
98 
99      if (frames == 0) return 0;
100 
101      sz = frames * ch->format.samplebytes;
102      if (sz > maxsize) sz = maxsize;
103      read = chunk_read_array(ch,curpos,sz,buffer,dither_playback,NULL);
104      g_assert(read <= sz);
105      curpos += read / ch->format.samplebytes;
106      if (loop && curpos == loopend) curpos = loopstart;
107      return read;
108 }
109 
get_new_data(void)110 static void get_new_data(void)
111 {
112 
113      if (varispeed_conv != NULL) {
114 	  if (varispeed_bufsize != 0)
115 	       do {
116 		    if (varispeed_bufpos == varispeed_bufsize) {
117 			 varispeed_bufsize = get_frames(varispeed_buf,
118 							sizeof(varispeed_buf));
119 			 varispeed_bufpos = 0;
120 			 if (varispeed_bufsize == 0) {
121 			      rateconv_write(varispeed_conv,NULL,0);
122 			      return;
123 			 }
124 		    }
125 		    if (varispeed_bufpos < varispeed_bufsize)
126 			 varispeed_bufpos +=
127 			      rateconv_write(varispeed_conv,
128 					     varispeed_buf+varispeed_bufpos,
129 					     varispeed_bufsize-
130 					     varispeed_bufpos);
131 	       } while (varispeed_bufpos == varispeed_bufsize &&
132 			varispeed_bufsize > 0);
133 	  player_bufsize = rateconv_read(varispeed_conv,player_buf,
134 					 sizeof(player_buf));
135      } else {
136 	  player_bufsize = get_frames(player_buf,sizeof(player_buf));
137      }
138      player_bufpos = 0;
139 }
140 
141 /* Check if the sound driver wants more data and send it.
142  * Returns FALSE if it had nothing to do. */
143 
player_work(void)144 static gboolean player_work(void)
145 {
146      guint32 i;
147      off_t o;
148      GTimeVal tv;
149      if (!ch || !output_want_data()) return FALSE;
150      if (player_bufpos == player_bufsize) {
151 	  /* puts("Hey 1"); */
152 	  get_new_data();
153 	  /* Still no new data ? Then we should stop.. */
154 	  if (player_bufpos == player_bufsize) {
155 	       /* puts("Calling output_play(NULL,0)"); */
156 	       i = output_play(NULL,0);
157 	       /* puts("output_play done"); */
158 	       o = rateest_frames_played();
159 	       if (i > 0 || o<rateest_frames_written()) {
160 		    if (notify_func != NULL)
161 			 notify_func(get_realpos_main(o),curpos,TRUE);
162 		    /* The sound layer will not call us again so we must
163 		     * do it ourselves */
164 		    mainloop_defer_once((defer_once_cb)player_work, 50, NULL);
165 		    return FALSE;
166 	       }
167 	       /* puts("Calling output_stop"); */
168 	       output_stop(TRUE);
169 	       chunk_close(ch);
170 	       gtk_object_unref(GTK_OBJECT(ch));
171 	       ch=NULL;
172 	       if (notify_func != NULL) notify_func(loopstart,curpos,FALSE);
173 	       return FALSE;
174 	  }
175      }
176      /* printf("Calling output_play with %d bytes\n",
177 	player_bufsize-player_bufpos); */
178      i = output_play(player_buf+player_bufpos,player_bufsize-player_bufpos);
179      player_bufpos += i;
180      rateest_log_data(i/ch->format.samplebytes);
181      if (i>0 && notify_func!=NULL) {
182 	  notify_func(player_get_real_pos(),curpos,TRUE);
183 	  g_get_current_time(&tv);
184 	  tv.tv_usec += 50000;
185 	  if (tv.tv_usec >= 1000000) { tv.tv_sec++; tv.tv_usec-=1000000; }
186 	  if (notify_watchdog == NULL) {
187 	       notify_watchdog = mainloop_time_source_add(&tv,notify_wd_cb,
188 							  NULL);
189 	  } else {
190 	       mainloop_time_source_restart(notify_watchdog, &tv);
191 	  }
192      }
193      return (i>0);
194 }
195 
196 
check_small_loop(void)197 static void check_small_loop(void)
198 {
199      off_t o;
200      guint32 u,v;
201      small_loop = FALSE;
202      if (loop) {
203 	  o = (loopend-loopstart)*ch->format.samplebytes;
204 	  if (o <= sizeof(small_loop_buf)) {
205 	       u = (guint32) o;
206 	       small_loop = TRUE;
207 	       chunk_read_array(ch,loopstart,u,small_loop_buf,dither_playback,
208 				NULL);
209 	       for (v=1; v < sizeof(player_buf)/u; v++)
210 		    memcpy(small_loop_buf+u*v,small_loop_buf,u);
211 	       /* printf("check_small_loop: u=%d, v=%d\n",u,v); */
212 	       small_loop_bufsize = v*u;
213 	  }
214      }
215 }
216 
restart_converter(void)217 static void restart_converter(void)
218 {
219      guint c,i;
220      if (varispeed_conv != NULL)
221 	  rateconv_destroy(varispeed_conv);
222 
223      if (inifile_get_gboolean("varispeed",TRUE)) {
224 	  c = rateconv_driver_count(TRUE);
225 	  i = inifile_get_guint32("varispeedConv",c-1);
226 	  if (i >= c) i = c-1;
227 	  varispeed_conv = rateconv_new(TRUE,rateconv_driver_id(TRUE,i),
228 					&(ch->format),
229 					(gfloat)ch->format.samplerate /
230 					true_speed, dither_playback, TRUE);
231      } else
232 	  varispeed_conv = NULL;
233 }
234 
player_play_main(Chunk * chk,off_t spos,off_t epos,gboolean lp,gint recursed)235 static gboolean player_play_main(Chunk *chk, off_t spos, off_t epos,
236 				 gboolean lp, gint recursed)
237 {
238      gint i;
239      gboolean b;
240      Dataformat fmt;
241      gchar *c;
242      player_stop();
243      if (spos == epos) return TRUE;
244      true_speed = file_speed;
245      file_output_ratio = 1.0;
246      memcpy(&fmt,&(chk->format),sizeof(Dataformat));
247      i = output_select_format(&(chk->format),(recursed<2),(GVoidFunc)player_work);
248      if (i != 0) {
249 	  if (recursed<2) {
250 	       if (!output_suggest_format(&(chk->format),&fmt)) {
251 		    memcpy(&fmt,&player_fallback_format,sizeof(Dataformat));
252 	       }
253 
254 	       b = !dataformat_samples_equal(&(chk->format),&fmt);
255 	       if (b) {
256 		    chk = chunk_convert_sampletype(chk,&fmt);
257 		    b = player_play_main(chk,spos,epos,lp,1);
258 		    gtk_object_sink(GTK_OBJECT(chk));
259 		    return b;
260 
261 	       } else if (chk->format.channels != fmt.channels) {
262 		    chk = chunk_convert_channels(chk,fmt.channels);
263 		    b = player_play_main(chk,spos,epos,lp,1);
264 		    gtk_object_sink(GTK_OBJECT(chk));
265 		    return b;
266 
267 	       } else if (chk->format.samplerate != fmt.samplerate) {
268 
269 		    file_output_ratio = ((float)chk->format.samplerate) / ((float)fmt.samplerate);
270 		    if (!inifile_get_gboolean("varispeed",TRUE)) {
271 			 /* If we don't have varispeed, still play the sound
272 			  * with the wrong speed */
273 
274 			 true_speed = 1.0;/* the only possible w/o varispeed */
275 			 file_speed = 1.0 / file_output_ratio;
276 		    } else {
277 			 /* Resample to get correct playback speed */
278 			 file_speed = 1.0;
279 			 true_speed = 1.0 * file_output_ratio;
280 		    }
281 		    /* Select the format with different sample rate */
282 		    i = output_select_format(&fmt,FALSE,(GVoidFunc)player_work);
283 		    if (i < 0)
284 			 return player_play_main(chk,spos,epos,lp,2);
285 		    else if (i > 0)
286 			 return TRUE;
287 		    /* Fall out to other init stuff below */
288 		    recursed++;
289 	       } else {
290 		    return player_play_main(chk,spos,epos,lp,2);
291 	       }
292 	  } else {
293 	       if (i < 0)
294 		    user_error(_("The sound format of this file is not "
295 			       "supported for playing."));
296 	       return TRUE;
297 	  }
298      }
299 
300      /* printf("true_speed: %f\n",(float)true_speed); */
301      if (recursed > 0) {
302 	  c = g_strdup_printf("playing file as %d Hz, %s, %s",fmt.samplerate,
303 			      sampletype_name(&(chk->format)),
304 			      channel_format_name(chk->format.channels));
305 	  console_message(c);
306 	  g_free(c);
307      }
308 
309      ch = chunk_open(chk);
310      gtk_object_ref(GTK_OBJECT(chk));
311      rateest_init(fmt.samplerate);
312 
313      restart_converter();
314 
315      loopstart = spos;
316      loopend = epos;
317      loop = lp;
318      player_bufsize = 1024; /* Anything >0 */
319      player_bufpos = player_bufsize;
320      varispeed_bufsize = 1024;
321      varispeed_bufpos = varispeed_bufsize;
322      check_small_loop();
323      curpos = spos;
324      realpos_offset = spos;
325      oldspeed_samples = 0;
326      oldspeed_speed = 1.0;
327      player_work();
328      return FALSE;
329 }
330 
player_play(Chunk * chk,off_t spos,off_t epos,gboolean lp,player_notify_func nf)331 gboolean player_play(Chunk *chk, off_t spos, off_t epos, gboolean lp,
332 		     player_notify_func nf)
333 {
334      player_stop();
335      notify_func = nf;
336      return player_play_main(chk,spos,epos,lp,0);
337 }
338 
player_get_buffer_pos(void)339 off_t player_get_buffer_pos(void)
340 {
341      return curpos;
342 }
343 
get_realpos_main(off_t frames_played)344 static off_t get_realpos_main(off_t frames_played)
345 {
346      gdouble fp;
347      off_t o,x;
348      if (frames_played < oldspeed_samples) {
349 	  fp = (gdouble)frames_played * oldspeed_speed;
350      } else {
351 	  fp = (gdouble)oldspeed_samples*oldspeed_speed +
352 	       (gdouble)(frames_played-oldspeed_samples) * true_speed;
353      }
354      o = (off_t)(fp) + realpos_offset;
355      /* printf("fp = %f, realpos_offset = %d -> o = %d\n",fp,
356 	(int)realpos_offset,
357 	(int)o);  */
358      if (loop && curpos < loopend && o >= loopend) {
359 	  /* printf("o f�re: %Ld",o); */
360 	  x = o-loopstart;
361 	  o = loopstart + x%(loopend-loopstart);
362 	  /* printf(", efter: %Ld\n",o); */
363      }
364      if (o >= ch->length) o = ch->length;
365      return o;
366 }
367 
368 /* This function calculates at what sample the listener is.
369  * Since for most drivers the amount of buffering is unknown, I use clock time
370  * to find out where we should be. After some time has passed, we should be
371  * able to estimate the buffer amount by comparing the "clock" position and
372  * the buffer position. Also, we check for overruns by checking for
373  * unreasonable values */
player_get_real_pos(void)374 off_t player_get_real_pos(void)
375 {
376      off_t o;
377      if (ch == NULL) return realpos_atstop;
378      o = get_realpos_main(rateest_frames_played());
379      /* printf("get_realpos_main returned: %Ld\n",o); */
380      return o;
381 }
382 
player_get_range(off_t * startpos,off_t * endpos)383 void player_get_range(off_t *startpos, off_t *endpos)
384 {
385      *startpos = loopstart;
386      *endpos = loopend;
387 }
388 
player_change_range(off_t start,off_t end)389 void player_change_range(off_t start, off_t end)
390 {
391      off_t rp;
392 
393      /* Special case - we're playing a loop and the sound has looped, but
394       * because of the output delay the cursor and the audible sound hasn't
395       * come there yet. In that case, we're forced to flush buffers etc
396       * to avoid sending out the old loop to the listener. */
397      if (loop) {
398 	  rp = player_get_real_pos();
399 	  if (rp > curpos) {
400 	       /* Fixme: Should handle error return (although unlikely) */
401 	       player_play(ch,rp,end,TRUE,notify_func);
402 	       loopstart = start;
403 	       return;
404 	  }
405      }
406 
407      loopstart = start;
408      loopend = end;
409      check_small_loop();
410 
411 }
412 
player_set_buffer_pos(off_t pos)413 void player_set_buffer_pos(off_t pos)
414 {
415      if (pos != curpos) {
416 	  /* if (curpos == loopend && !loop && varispeed_conv != NULL) */
417 	  restart_converter();
418 	  curpos = pos;
419 	  output_clear_buffers();
420 	  rateest_init(rateest_real_samplerate());
421 	  oldspeed_samples = 0;
422 	  realpos_offset = pos;
423 	  player_bufsize = player_bufpos = 1;
424 	  player_work();
425      }
426 }
427 
player_switch(Chunk * chunk,off_t movestart,off_t movedist)428 void player_switch(Chunk *chunk, off_t movestart, off_t movedist)
429 {
430      ChunkHandle *c;
431      off_t oldpos = curpos;
432      off_t newpos, newstart, newend;
433      Chunk *x;
434 
435      if (ch == NULL) return;
436 
437      g_assert(chunk->format.samplerate == ch->format.samplerate);
438      if (!dataformat_samples_equal(&(chunk->format),&(ch->format))) {
439 	  x = chunk_convert_sampletype(chunk, &(ch->format));
440 	  player_switch(x,movestart,movedist);
441 	  gtk_object_sink(GTK_OBJECT(x));
442 	  return;
443      }
444      if (chunk->format.channels != ch->format.channels) {
445 	  x = chunk_convert_channels(chunk, ch->format.channels);
446 	  player_switch(x,movestart,movedist);
447 	  gtk_object_sink(GTK_OBJECT(x));
448 	  return;
449      }
450 
451      newpos = curpos;
452      if (newpos >= movestart) {
453 	  newpos += movedist;
454 	  if (newpos < movestart) {
455 	       realpos_atstop = movestart;
456 	       player_stop_main(FALSE);
457 	       return;
458 	  }
459 	  if (newpos > chunk->length) {
460 	       realpos_atstop = chunk->length;
461 	       player_stop_main(FALSE);
462 	       return;
463 	  }
464      }
465 
466      newstart = loopstart;
467      if (newstart >= movestart) {
468 	  newstart += movedist;
469 	  if (newstart < movestart) newstart = movestart;
470 	  if (newstart >= chunk->length) newstart = chunk->length;
471      }
472      newend = loopend;
473      if (newend >= movestart) {
474 	  newend += movedist;
475 	  if (newend < movestart) newend = movestart;
476 	  if (newend >= chunk->length) newend = chunk->length;
477      }
478 
479      c = chunk_open(chunk);
480      if (c == NULL) return;
481      chunk_close(ch);
482      gtk_object_unref(GTK_OBJECT(ch));
483      ch = c;
484      gtk_object_ref(GTK_OBJECT(ch));
485 
486      loopstart = newstart;
487      loopend = newend;
488      curpos = newpos;
489      if (newstart == newend) loop = FALSE;
490 
491      check_small_loop();
492 
493      realpos_offset += newpos - oldpos;
494 }
495 
player_playing(void)496 gboolean player_playing(void)
497 {
498      return (ch != NULL);
499 }
500 
player_stop_main(gboolean read_stoppos)501 static void player_stop_main(gboolean read_stoppos)
502 {
503      gboolean b;
504      if (ch == NULL) return;
505      chunk_close(ch);
506      b = output_stop(FALSE);
507      if (read_stoppos)
508 	  realpos_atstop = get_realpos_main(b ? rateest_frames_written() :
509 					    rateest_frames_played());
510      if (notify_func) notify_func(realpos_atstop,curpos,FALSE);
511      gtk_object_unref(GTK_OBJECT(ch));
512      ch = NULL;
513 }
514 
player_stop(void)515 void player_stop(void)
516 {
517      player_stop_main(TRUE);
518 }
519 
player_looping(void)520 gboolean player_looping(void)
521 {
522      return loop;
523 }
524 
player_nudge(gfloat seconds)525 void player_nudge(gfloat seconds)
526 {
527      off_t samps,newpos;
528      samps = seconds * ch->format.samplerate;
529      newpos = player_get_real_pos() + samps;
530      if (newpos < 0) newpos = 0;
531      player_set_buffer_pos(newpos);
532 }
533 
player_set_speed(gfloat s)534 void player_set_speed(gfloat s)
535 {
536      off_t wr,pl,rp;
537      gfloat os;
538 
539      if (file_speed == s) return;
540 
541      if (ch == NULL || varispeed_conv==NULL) {
542 	  file_speed = true_speed = s;
543 	  return;
544      }
545 
546      /* We must calculate these before changing the speed variable */
547      wr = rateest_frames_written();
548      pl = rateest_frames_played();
549      rp = get_realpos_main(pl);
550 
551      os = true_speed;
552      true_speed = s * file_output_ratio;
553      file_speed = s;
554 
555      if (varispeed_smooth_flag) {
556 	  /* Low latency version - just change the converter */
557 
558 	  rateconv_set_outrate(varispeed_conv,
559 			       (gfloat)ch->format.samplerate / true_speed);
560 
561 	  /* Update oldspeed_speed and oldspeed_samples. */
562 	  if (pl < oldspeed_samples) {
563 	       /* This if case is just to avoid getting Inf in speed,
564 		* not strictly necessary */
565 	       if (wr != pl)
566 		    oldspeed_speed = (oldspeed_speed*
567 				      (gfloat)(oldspeed_samples-pl) +
568 				      os*(gfloat)(wr-oldspeed_samples)) /
569 		    (gfloat)(wr-pl);
570 	       oldspeed_samples = wr-pl;
571 	  } else {
572 	       oldspeed_speed = os;
573 	       oldspeed_samples = wr-pl;
574 	  }
575 
576 	  rateest_init(rateest_real_samplerate());
577 	  rateest_prelog_data(wr-pl);
578 	  realpos_offset = rp;
579 
580 	  /* printf("After rateest_init: rateest_frames_played=%Ld\n",
581 	     rateest_frames_played()); */
582      } else {
583 	  /* High latency version - clears buffers, restarts converter */
584 	  output_clear_buffers();
585 	  restart_converter();
586 	  curpos = rp;
587 	  rateest_init(rateest_real_samplerate());
588 	  realpos_offset = curpos;
589 	  oldspeed_samples = 0;
590 	  player_bufsize = player_bufpos = 1;
591 	  player_work();
592      }
593 
594 }
595 
player_get_speed(void)596 gfloat player_get_speed(void)
597 {
598      return file_speed;
599 }
600