aout.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:11k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * aout.c : QNX audio output
  3.  *****************************************************************************
  4.  * Copyright (C) 2000, 2001 the VideoLAN team
  5.  *
  6.  * Authors: Henri Fallon <henri@videolan.org>
  7.  *          Jon Lech Johansen <jon-vl@nanocrew.net>
  8.  *          Pascal Levesque <pascal.levesque@mindready.com>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. /*****************************************************************************
  25.  * Preamble
  26.  *****************************************************************************/
  27. #include <errno.h>                                                 /* ENOMEM */
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #ifdef HAVE_ALLOCA_H
  33. #   include <alloca.h>
  34. #endif
  35. #include <vlc_aout.h>
  36. #include <sys/asoundlib.h>
  37. struct aout_sys_t
  38. {
  39.     snd_pcm_t  * p_pcm_handle;
  40.     int          i_card;
  41.     int          i_device;
  42.     uint8_t *    p_silent_buffer;
  43. };
  44. #define DEFAULT_FRAME_SIZE 2048
  45. /*****************************************************************************
  46.  * Local prototypes
  47.  *****************************************************************************/
  48. int            OpenAudio    ( vlc_object_t *p_this );
  49. void           CloseAudio   ( vlc_object_t *p_this );
  50. static int     GetBufInfo       ( aout_instance_t * );
  51. static void    Play             ( aout_instance_t * );
  52. static void*   QNXaoutThread    ( vlc_object_t * );
  53. /*****************************************************************************
  54.  * Open : creates a handle and opens an alsa device
  55.  *****************************************************************************
  56.  * This function opens an alsa device, through the alsa API
  57.  *****************************************************************************/
  58. int OpenAudio( vlc_object_t *p_this )
  59. {
  60.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  61.     int i_ret;
  62.     int i_bytes_per_sample;
  63.     int i_nb_channels;
  64.     snd_pcm_channel_info_t pi;
  65.     snd_pcm_channel_params_t pp;
  66.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  67.     /* allocate structure */
  68.     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
  69.     if( p_aout->output.p_sys == NULL )
  70.         return -1;
  71.     /* open audio device */
  72.     if( ( i_ret = snd_pcm_open_preferred( &p_aout->output.p_sys->p_pcm_handle,
  73.                                           &p_aout->output.p_sys->i_card,
  74.                                           &p_aout->output.p_sys->i_device,
  75.                                           SND_PCM_OPEN_PLAYBACK ) ) < 0 )
  76.     {
  77.         msg_Err( p_aout, "unable to open audio device (%s)",
  78.                          snd_strerror( i_ret ) );
  79.         free( p_aout->output.p_sys );
  80.         return -1;
  81.     }
  82.     /* disable mmap */
  83.     if( ( i_ret = snd_pcm_plugin_set_disable( p_aout->output.p_sys->p_pcm_handle,
  84.                                               PLUGIN_DISABLE_MMAP ) ) < 0 )
  85.     {
  86.         msg_Err( p_aout, "unable to disable mmap (%s)", snd_strerror(i_ret) );
  87.         CloseAudio( p_this );
  88.         free( p_aout->output.p_sys );
  89.         return -1;
  90.     }
  91.     p_aout->output.p_sys->p_silent_buffer = malloc( DEFAULT_FRAME_SIZE * 4 );
  92.     p_aout->output.pf_play = Play;
  93.     aout_VolumeSoftInit( p_aout );
  94.     memset( &pi, 0, sizeof(pi) );
  95.     memset( &pp, 0, sizeof(pp) );
  96.     pi.channel = SND_PCM_CHANNEL_PLAYBACK;
  97.     if( ( i_ret = snd_pcm_plugin_info( p_aout->output.p_sys->p_pcm_handle,
  98.                                        &pi ) ) < 0 )
  99.     {
  100.         msg_Err( p_aout, "unable to get plugin info (%s)",
  101.                          snd_strerror( i_ret ) );
  102.         CloseAudio( p_this );
  103.         free( p_aout->output.p_sys );
  104.         return -1;
  105.     }
  106.     pp.mode       = SND_PCM_MODE_BLOCK;
  107.     pp.channel    = SND_PCM_CHANNEL_PLAYBACK;
  108.     pp.start_mode = SND_PCM_START_FULL;
  109.     pp.stop_mode  = SND_PCM_STOP_STOP;
  110.     pp.buf.block.frags_max   = 3;
  111.     pp.buf.block.frags_min   = 1;
  112.     pp.format.interleave     = 1;
  113.     pp.format.rate           = p_aout->output.output.i_rate;
  114.     i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
  115.     if ( i_nb_channels > 2 )
  116.     {
  117.         /* I don't know if QNX supports more than two channels. */
  118.         i_nb_channels = 2;
  119.         p_aout->output.output.i_channels = AOUT_CHAN_STEREO;
  120.     }
  121.     pp.format.voices         = i_nb_channels;
  122.     p_aout->output.output.i_format = AOUT_FMT_S16_NE;
  123.     p_aout->output.i_nb_samples = DEFAULT_FRAME_SIZE;
  124.     pp.format.format = SND_PCM_SFMT_S16;
  125.     i_bytes_per_sample = 2;
  126.     pp.buf.block.frag_size = p_aout->output.i_nb_samples *
  127.                             p_aout->output.output.i_channels *
  128.                             i_bytes_per_sample;
  129.     /* set parameters */
  130.     if( ( i_ret = snd_pcm_plugin_params( p_aout->output.p_sys->p_pcm_handle,
  131.                                          &pp ) ) < 0 )
  132.     {
  133.         msg_Err( p_aout, "unable to set parameters (%s)", snd_strerror(i_ret) );
  134.         CloseAudio( p_this );
  135.         free( p_aout->output.p_sys );
  136.         return -1;
  137.     }
  138.     /* prepare channel */
  139.     if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
  140.                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
  141.     {
  142.         msg_Err( p_aout, "unable to prepare channel (%s)",
  143.                          snd_strerror( i_ret ) );
  144.         CloseAudio( p_this );
  145.         free( p_aout->output.p_sys );
  146.         return -1;
  147.     }
  148.     /* Create audio thread and wait for its readiness. */
  149.     if( vlc_thread_create( p_aout, "aout", QNXaoutThread,
  150.                            VLC_THREAD_PRIORITY_OUTPUT ) )
  151.     {
  152.         msg_Err( p_aout, "cannot create QNX audio thread (%m)" );
  153.         CloseAudio( p_this );
  154.         free( p_aout->output.p_sys );
  155.         return -1;
  156.     }
  157.     return( 0 );
  158. }
  159. /*****************************************************************************
  160.  * GetBufInfo: buffer status query
  161.  *****************************************************************************
  162.  * This function returns the number of used byte in the queue.
  163.  * It also deals with errors : indeed if the device comes to run out
  164.  * of data to play, it switches to the "underrun" status. It has to
  165.  * be flushed and re-prepared
  166.  *****************************************************************************/
  167. static int GetBufInfo( aout_instance_t *p_aout )
  168. {
  169.     int i_ret;
  170.     snd_pcm_channel_status_t status;
  171.     /* get current pcm status */
  172.     memset( &status, 0, sizeof(status) );
  173.     if( ( i_ret = snd_pcm_plugin_status( p_aout->output.p_sys->p_pcm_handle,
  174.                                          &status ) ) < 0 )
  175.     {
  176.         msg_Err( p_aout, "unable to get device status (%s)",
  177.                          snd_strerror( i_ret ) );
  178.         return( -1 );
  179.     }
  180.     /* check for underrun */
  181.     switch( status.status )
  182.     {
  183.         case SND_PCM_STATUS_READY:
  184.         case SND_PCM_STATUS_UNDERRUN:
  185.             if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
  186.                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
  187.             {
  188.                 msg_Err( p_aout, "unable to prepare channel (%s)",
  189.                                  snd_strerror( i_ret ) );
  190.             }
  191.             break;
  192.     }
  193.     return( status.count );
  194. }
  195. /*****************************************************************************
  196.  * Play : plays a sample
  197.  *****************************************************************************
  198.  * Plays a sample using the snd_pcm_write function from the alsa API
  199.  *****************************************************************************/
  200. static void Play( aout_instance_t *p_aout )
  201. {
  202. }
  203. /*****************************************************************************
  204.  * CloseAudio: close the audio device
  205.  *****************************************************************************/
  206. void CloseAudio ( vlc_object_t *p_this )
  207. {
  208.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  209.     int i_ret;
  210.     vlc_object_kill( p_aout );
  211.     vlc_thread_join( p_aout );
  212.     if( ( i_ret = snd_pcm_close( p_aout->output.p_sys->p_pcm_handle ) ) < 0 )
  213.     {
  214.         msg_Err( p_aout, "unable to close audio device (%s)",
  215.                          snd_strerror( i_ret ) );
  216.     }
  217.     free( p_aout->output.p_sys->p_silent_buffer );
  218.     free( p_aout->output.p_sys );
  219. }
  220. /*****************************************************************************
  221.  * QNXaoutThread: asynchronous thread used to DMA the data to the device
  222.  *****************************************************************************/
  223. static void* QNXaoutThread( vlc_object_t *p_this )
  224. {
  225.     aout_instance_t * p_aout = (aout_instance_t*)p_this;
  226.     struct aout_sys_t * p_sys = p_aout->output.p_sys;
  227.     int canc = vlc_savecancel ();
  228.     while ( vlc_object_alive (p_aout) )
  229.     {
  230.         aout_buffer_t * p_buffer;
  231.         int i_tmp, i_size;
  232.         uint8_t * p_bytes;
  233.         if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') )
  234.         {
  235.             mtime_t next_date = 0;
  236.             /* Get the presentation date of the next write() operation. It
  237.              * is equal to the current date + duration of buffered samples.
  238.              * Order is important here, since GetBufInfo is believed to take
  239.              * more time than mdate(). */
  240.             next_date = (mtime_t)GetBufInfo( p_aout ) * 1000000
  241.                       / p_aout->output.output.i_bytes_per_frame
  242.                       / p_aout->output.output.i_rate
  243.                       * p_aout->output.output.i_frame_length;
  244.             next_date += mdate();
  245.             p_buffer = aout_OutputNextBuffer( p_aout, next_date, false );
  246.         }
  247.         else
  248.         {
  249.             p_buffer = aout_OutputNextBuffer( p_aout, 0, true );
  250.         }
  251.         if ( p_buffer != NULL )
  252.         {
  253.             p_bytes = p_buffer->p_buffer;
  254.             i_size = p_buffer->i_nb_bytes;
  255.         }
  256.         else
  257.         {
  258.             i_size = DEFAULT_FRAME_SIZE / p_aout->output.output.i_frame_length
  259.                       * p_aout->output.output.i_bytes_per_frame;
  260.             p_bytes = p_aout->output.p_sys->p_silent_buffer;
  261.             memset( p_bytes, 0, i_size );
  262.         }
  263.         i_tmp = snd_pcm_plugin_write( p_aout->output.p_sys->p_pcm_handle,
  264.                                         (void *) p_bytes,
  265.                                         (size_t) i_size );
  266.         if( i_tmp < 0 )
  267.         {
  268.             msg_Err( p_aout, "write failed (%m)" );
  269.         }
  270.         if ( p_buffer != NULL )
  271.         {
  272.             aout_BufferFree( p_buffer );
  273.         }
  274.     }
  275.     vlc_restorecancel (canc);
  276.     return NULL;
  277. }