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