2
0

sndfile-jackplay.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. ** Copyright (c) 2007-2009 Erik de Castro Lopo <erikd@mega-nerd.com>
  3. ** Copyright (C) 2007 Jonatan Liljedahl <lijon@kymatica.com>
  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. #include "sfconfig.h"
  20. #include <stdio.h>
  21. #include <errno.h>
  22. #include <unistd.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #if HAVE_JACK
  26. #include <math.h>
  27. #include <pthread.h>
  28. #include <jack/jack.h>
  29. #include <jack/ringbuffer.h>
  30. #include <sndfile.h>
  31. #define RB_SIZE (1 << 16)
  32. typedef struct _thread_info
  33. { pthread_t thread_id ;
  34. SNDFILE *sndfile ;
  35. jack_nframes_t pos ;
  36. jack_client_t *client ;
  37. unsigned int channels ;
  38. volatile int can_process ;
  39. volatile int read_done ;
  40. volatile int play_done ;
  41. } thread_info_t ;
  42. pthread_mutex_t disk_thread_lock = PTHREAD_MUTEX_INITIALIZER ;
  43. pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER ;
  44. static jack_ringbuffer_t *ringbuf ;
  45. static jack_port_t **output_port ;
  46. static jack_default_audio_sample_t ** outs ;
  47. const size_t sample_size = sizeof (jack_default_audio_sample_t) ;
  48. static int
  49. process (jack_nframes_t nframes, void * arg)
  50. {
  51. thread_info_t *info = (thread_info_t *) arg ;
  52. jack_default_audio_sample_t buf [info->channels] ;
  53. unsigned i, n ;
  54. if (! info->can_process)
  55. return 0 ;
  56. for (n = 0 ; n < info->channels ; n++)
  57. outs [n] = jack_port_get_buffer (output_port [n], nframes) ;
  58. for (i = 0 ; i < nframes ; i++)
  59. { size_t read_cnt ;
  60. /* Read one frame of audio. */
  61. read_cnt = jack_ringbuffer_read (ringbuf, (void*) buf, sample_size*info->channels) ;
  62. if (read_cnt == 0 && info->read_done)
  63. { /* File is done, so stop the main loop. */
  64. info->play_done = 1 ;
  65. return 0 ;
  66. } ;
  67. /* Update play-position counter. */
  68. info->pos += read_cnt / (sample_size*info->channels) ;
  69. /* Output each channel of the frame. */
  70. for (n = 0 ; n < info->channels ; n++)
  71. outs [n][i] = buf [n] ;
  72. } ;
  73. /* Wake up the disk thread to read more data. */
  74. if (pthread_mutex_trylock (&disk_thread_lock) == 0)
  75. { pthread_cond_signal (&data_ready) ;
  76. pthread_mutex_unlock (&disk_thread_lock) ;
  77. } ;
  78. return 0 ;
  79. } /* process */
  80. static void *
  81. disk_thread (void *arg)
  82. { thread_info_t *info = (thread_info_t *) arg ;
  83. sf_count_t buf_avail, read_frames ;
  84. jack_ringbuffer_data_t vec [2] ;
  85. size_t bytes_per_frame = sample_size*info->channels ;
  86. pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL) ;
  87. pthread_mutex_lock (&disk_thread_lock) ;
  88. while (1)
  89. { jack_ringbuffer_get_write_vector (ringbuf, vec) ;
  90. read_frames = 0 ;
  91. if (vec [0].len)
  92. { /* Fill the first part of the ringbuffer. */
  93. buf_avail = vec [0].len / bytes_per_frame ;
  94. read_frames = sf_readf_float (info->sndfile, (float *) vec [0].buf, buf_avail) ;
  95. if (vec [1].len)
  96. { /* Fill the second part of the ringbuffer? */
  97. buf_avail = vec [1].len / bytes_per_frame ;
  98. read_frames += sf_readf_float (info->sndfile, (float *) vec [1].buf, buf_avail) ;
  99. } ;
  100. } ;
  101. if (read_frames == 0)
  102. break ; /* end of file? */
  103. jack_ringbuffer_write_advance (ringbuf, read_frames * bytes_per_frame) ;
  104. /* Tell process that we've filled the ringbuffer. */
  105. info->can_process = 1 ;
  106. /* Wait for the process thread to wake us up. */
  107. pthread_cond_wait (&data_ready, &disk_thread_lock) ;
  108. } ;
  109. /* Tell that we're done reading the file. */
  110. info->read_done = 1 ;
  111. pthread_mutex_unlock (&disk_thread_lock) ;
  112. return 0 ;
  113. } /* disk_thread */
  114. static void
  115. jack_shutdown (void *arg)
  116. { (void) arg ;
  117. exit (1) ;
  118. } /* jack_shutdown */
  119. static void
  120. print_time (jack_nframes_t pos, int jack_sr)
  121. { float sec = pos / (1.0 * jack_sr) ;
  122. int min = sec / 60.0 ;
  123. fprintf (stderr, "%02d:%05.2f", min, fmod (sec, 60.0)) ;
  124. } /* print_time */
  125. int
  126. main (int narg, char * args [])
  127. {
  128. SNDFILE *sndfile ;
  129. SF_INFO sndfileinfo ;
  130. jack_client_t *client ;
  131. thread_info_t info ;
  132. int i, jack_sr ;
  133. if (narg < 2)
  134. { fprintf (stderr, "no soundfile given\n") ;
  135. return 1 ;
  136. } ;
  137. // create jack client
  138. if ((client = jack_client_new ("jackplay")) == 0)
  139. {
  140. fprintf (stderr, "Jack server not running?\n") ;
  141. return 1 ;
  142. } ;
  143. jack_sr = jack_get_sample_rate (client) ;
  144. /* Open the soundfile. */
  145. sndfileinfo.format = 0 ;
  146. sndfile = sf_open (args [1], SFM_READ, &sndfileinfo) ;
  147. if (sndfile == NULL)
  148. { fprintf (stderr, "Could not open soundfile '%s'\n", args [1]) ;
  149. return 1 ;
  150. } ;
  151. fprintf (stderr, "Channels : %d\nSample rate : %d Hz\nDuration : ", sndfileinfo.channels, sndfileinfo.samplerate) ;
  152. print_time (sndfileinfo.frames, sndfileinfo.samplerate) ;
  153. fprintf (stderr, "\n") ;
  154. if (sndfileinfo.samplerate != jack_sr)
  155. fprintf (stderr, "Warning: samplerate of soundfile (%d Hz) does not match jack server (%d Hz).\n", sndfileinfo.samplerate, jack_sr) ;
  156. /* Init the thread info struct. */
  157. memset (&info, 0, sizeof (info)) ;
  158. info.can_process = 0 ;
  159. info.read_done = 0 ;
  160. info.play_done = 0 ;
  161. info.sndfile = sndfile ;
  162. info.channels = sndfileinfo.channels ;
  163. info.client = client ;
  164. info.pos = 0 ;
  165. /* Set up callbacks. */
  166. jack_set_process_callback (client, process, &info) ;
  167. jack_on_shutdown (client, jack_shutdown, 0) ;
  168. /* Allocate output ports. */
  169. output_port = calloc (sndfileinfo.channels, sizeof (jack_port_t *)) ;
  170. outs = calloc (sndfileinfo.channels, sizeof (jack_default_audio_sample_t *)) ;
  171. for (i = 0 ; i < sndfileinfo.channels ; i++)
  172. { char name [16] ;
  173. snprintf (name, sizeof (name), "out_%d", i + 1) ;
  174. output_port [i] = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0) ;
  175. } ;
  176. /* Allocate and clear ringbuffer. */
  177. ringbuf = jack_ringbuffer_create (sizeof (jack_default_audio_sample_t) * RB_SIZE) ;
  178. memset (ringbuf->buf, 0, ringbuf->size) ;
  179. /* Activate client. */
  180. if (jack_activate (client))
  181. { fprintf (stderr, "Cannot activate client.\n") ;
  182. return 1 ;
  183. } ;
  184. /* Auto connect all channels. */
  185. for (i = 0 ; i < sndfileinfo.channels ; i++)
  186. { char name [64] ;
  187. snprintf (name, sizeof (name), "alsa_pcm:playback_%d", i + 1) ;
  188. if (jack_connect (client, jack_port_name (output_port [i]), name))
  189. fprintf (stderr, "Cannot connect output port %d (%s).\n", i, name) ;
  190. } ;
  191. /* Start the disk thread. */
  192. pthread_create (&info.thread_id, NULL, disk_thread, &info) ;
  193. /* Sit in a loop, displaying the current play position. */
  194. while (! info.play_done)
  195. { fprintf (stderr, "\r-> ") ;
  196. print_time (info.pos, jack_sr) ;
  197. fflush (stdout) ;
  198. usleep (50000) ;
  199. } ;
  200. /* Clean up. */
  201. jack_client_close (client) ;
  202. jack_ringbuffer_free (ringbuf) ;
  203. sf_close (sndfile) ;
  204. free (outs) ;
  205. free (output_port) ;
  206. puts ("") ;
  207. return 0 ;
  208. } /* main */
  209. #else
  210. int
  211. main (void)
  212. {
  213. puts (
  214. "Sorry this program was compiled without libjack (which probably\n"
  215. "only exists on Linux and Mac OSX) and hence doesn't work."
  216. ) ;
  217. return 0 ;
  218. } /* main */
  219. #endif