[vlc-devel] [PATCH 14/18] Add KAI audio output module for OS/2

Rémi Denis-Courmont remi at remlab.net
Fri Oct 21 17:14:49 CEST 2011


Le vendredi 21 octobre 2011 17:22:22 KO Myung-Hun, vous avez écrit :
> Rémi Denis-Courmont wrote:
> > On Thu, 20 Oct 2011 21:30:21 +0900, KO Myung-Hun <komh at chollian.net>
> > 
> > wrote:
> >> ---
> >> 
> >>  configure.ac                    |   23 +++
> >>  modules/audio_output/Modules.am |    8 +
> >>  modules/audio_output/kai.c      |  327
> >>  +++++++++++++++++++++++++++++++++++++++
> >>  3 files changed, 358 insertions(+), 0 deletions(-)
> >>  create mode 100644 modules/audio_output/kai.c
> >> 
> >> diff --git a/configure.ac b/configure.ac
> >> index 4687149..4fcca19 100644
> >> --- a/configure.ac
> >> +++ b/configure.ac
> >> @@ -3591,6 +3591,29 @@ dnl
> >> 
> >>  PKG_ENABLE_MODULES_VLC([SAMPLERATE], [], [samplerate], [Resampler with
> >>  libsamplerate], [auto])
> >>  
> >>  dnl
> >> 
> >> +dnl  OS/2 KAI plugin
> >> +dnl
> >> +AC_ARG_ENABLE(kai,
> >> +  [AS_HELP_STRING([--enable-kai],
> >> +    [support the K Audio Interface KAI (default enabled on OS/2)])],, [
> >> +  AS_IF([test "$SYS" = "os2"], [
> >> +    enable_kai="yes"
> >> +  ])
> >> +])
> >> +have_kai="no"
> >> +KAI_LIBS=""
> >> +AS_IF([test "$enable_kai" != "no"], [
> >> +  AC_CHECK_HEADERS([kai.h], [
> >> +    have_kai="yes"
> >> +    AC_CHECK_LIB(kai, main, [
> >> +      KAI_LIBS="-lkai"
> >> +    ])
> >> +  ])
> >> +])
> >> +AC_SUBST(KAI_LIBS)
> >> +AM_CONDITIONAL([HAVE_KAI], [test "${have_kai}" = "yes"])
> >> +
> >> +dnl
> >> 
> >>  dnl  Interface plugins
> >>  dnl
> >> 
> >> diff --git a/modules/audio_output/Modules.am
> >> b/modules/audio_output/Modules.am
> >> index 0cbbbb7..c7d7cca 100644
> >> --- a/modules/audio_output/Modules.am
> >> +++ b/modules/audio_output/Modules.am
> >> @@ -45,3 +45,11 @@ libpulse_plugin_la_DEPENDENCIES =
> >> 
> >>  if HAVE_PULSE
> >>  libvlc_LTLIBRARIES += libpulse_plugin.la
> >>  endif
> >> 
> >> +
> >> +libkai_plugin_la_SOURCES = kai.c
> >> +libkai_plugin_la_CFLAGS = $(AM_CFLAGS)
> >> +libkai_plugin_la_LIBADD = $(AM_LIBADD) $(KAI_LIBS)
> >> +libkai_plugin_la_DEPENDENCIES =
> >> +if HAVE_KAI
> >> +libvlc_LTLIBRARIES += libkai_plugin.la
> >> +endif
> >> diff --git a/modules/audio_output/kai.c b/modules/audio_output/kai.c
> >> new file mode 100644
> >> index 0000000..d691410
> >> --- /dev/null
> >> +++ b/modules/audio_output/kai.c
> >> @@ -0,0 +1,327 @@
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * kai.c : KAI audio output plugin for vlc
> >> +
> > 
> > *************************************************************************
> > ****
> > 
> >> + * Copyright (C) 2010 the VideoLAN team
> >> + *
> >> + * Authors: KO Myung-Hun <komh at chollian.net>
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License as published by
> >> + * the Free Software Foundation; either version 2 of the License, or
> >> + * (at your option) any later version.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> + * GNU General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU General Public License
> >> + * along with this program; if not, write to the Free Software
> >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA
> >> 02110-1301, USA.
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * Preamble
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +#ifdef HAVE_CONFIG_H
> >> +# include "config.h"
> >> +#endif
> >> +
> >> +#include <vlc_common.h>
> >> +#include <vlc_plugin.h>
> >> +#include <vlc_aout.h>
> >> +
> >> +#include <float.h>
> >> +
> >> +#include <kai.h>
> >> +
> >> +#define FRAME_SIZE 2048
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * aout_sys_t: KAI audio output method descriptor
> >> +
> > 
> > *************************************************************************
> > ****
> > 
> >> + * This structure is part of the audio output thread descriptor.
> >> + * It describes the specific properties of an audio device.
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +struct aout_sys_t
> >> +{
> >> +    aout_packet_t   packet;
> >> +    HKAI            hkai;
> >> +};
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * Local prototypes
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +static int  Open  ( vlc_object_t * );
> >> +static void Close ( vlc_object_t * );
> >> +static void Play  ( audio_output_t *_p_aout, block_t *block );
> >> +
> >> +static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * Module descriptor
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +#define KAI_AUDIO_DEVICE_TEXT N_( \
> >> +    "Device:" )
> > 
> > Should not have trailing semicolon.
> 
> Colon ? If so, fixed.
> 
> >> +#define KAI_AUDIO_DEVICE_LONGTEXT N_( \
> >> +    "Select a proper audio device to be used by KAI" )
> > 
> > Sentence should end with a dot.
> 
> Ok.
> 
> >> +
> >> +#define KAI_AUDIO_EXCLUSIVE_MODE_TEXT N_( \
> >> +    "Open audio in exclusive mode" )
> >> +#define KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT N_( \
> >> +    "Enable this option if you want your audio not to be interrupted by
> >> the " \
> >> +    "other audio" )
> > 
> > Same here.
> 
> Fixed.
> 
> >> +
> >> +static const char *const ppsz_kai_audio_device[] = {
> >> +    "auto", "dart", "uniaud" };
> >> +static const char *const ppsz_kai_audio_device_text[] = {
> >> +    N_("Auto"), "DART", "UNIAUD" };
> >> +
> >> +vlc_module_begin ()
> >> +    set_shortname( "KAI" )
> >> +    set_description( N_("K Audio Interface audio output") )
> >> +    set_capability( "audio output", 100 )
> >> +    set_category( CAT_AUDIO )
> >> +    set_subcategory( SUBCAT_AUDIO_AOUT )
> >> +    add_string( "kai-audio-device", ppsz_kai_audio_device[0],
> >> +                KAI_AUDIO_DEVICE_TEXT, KAI_AUDIO_DEVICE_LONGTEXT, false
> > 
> > )
> > 
> >> +        change_string_list( ppsz_kai_audio_device,
> >> ppsz_kai_audio_device_text,
> >> +                            0 )
> >> +    add_bool( "kai-audio-exclusive-mode", false,
> >> +              KAI_AUDIO_EXCLUSIVE_MODE_TEXT,
> >> KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT,
> >> +              true )
> >> +    add_shortcut( "kai" )
> > 
> > This should be superfluous since the module is called kai already.
> 
> Fixed.
> 
> >> +    set_callbacks( Open, Close )
> >> +vlc_module_end ()
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * Open: open the audio device
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +static int Open ( vlc_object_t *p_this )
> >> +{
> >> +    audio_output_t *p_aout = (audio_output_t *)p_this;
> >> +    aout_sys_t *p_sys;
> >> +    char *psz_mode;
> >> +    ULONG i_kai_mode;
> >> +    KAISPEC ks_wanted, ks_obtained;
> >> +    int i_nb_channels;
> >> +    int i_bytes_per_frame;
> >> +    vlc_value_t val, text;
> >> +
> >> +    /* Allocate structure */
> >> +    p_aout->sys = calloc( 1, sizeof( aout_sys_t ) );
> >> +
> >> +    if( p_aout->sys == NULL )
> >> +        return VLC_ENOMEM;
> >> +
> >> +    p_sys = p_aout->sys;
> >> +
> >> +    if( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR )
> >> +    {
> >> +        /* The user has selected an audio device. */
> >> +        if ( val.i_int == AOUT_VAR_STEREO )
> >> +        {
> >> +            p_aout->format.i_physical_channels
> >> +                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
> >> +        }
> >> +        else if ( val.i_int == AOUT_VAR_MONO )
> >> +        {
> >> +            p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
> >> +        }
> >> +    }
> > 
> > This will corrupt p_aout->format.i_physical_channels for the next plug-in
> > if kaiOpen() fails afterward.
> 
> Fixed.
> 
> >> +
> >> +    psz_mode = var_CreateGetString( p_aout, "kai-audio-device" );
> > 
> > var_InheritString() should be enough here.
> 
> Ok.
> 
> >> +    if( !psz_mode )
> >> +        psz_mode = ppsz_kai_audio_device[ 0 ];  // "auto"
> >> +
> >> +    i_kai_mode = KAIM_AUTO;
> >> +    if( strcmp( psz_mode, "dart" ) == 0 )
> >> +        i_kai_mode = KAIM_DART;
> >> +    else if( strcmp( psz_mode, "uniaud" ) == 0 )
> >> +        i_kai_mode = KAIM_UNIAUD;
> >> +    msg_Dbg( p_aout, "selected mode = %s", psz_mode );
> >> +
> >> +    if( psz_mode != ppsz_kai_audio_device[ 0 ])
> >> +        free( psz_mode );
> >> +
> >> +    i_nb_channels = aout_FormatNbChannels( &p_aout->format );
> >> +    if ( i_nb_channels > 2 )
> >> +    {
> >> +        /* KAI doesn't support more than two channels. */
> >> +        i_nb_channels = 2;
> >> +        p_aout->format.i_physical_channels
> >> +            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
> >> +    }
> > 
> > Same problem as earlier.
> 
> Fixed.
> 
> >> +
> >> +    /* Support s16l only */
> >> +    p_aout->format.i_format = VLC_CODEC_S16L;
> > 
> > And again.
> 
> Fixed.
> 
> >> +
> >> +    aout_FormatPrepare( &p_aout->format );
> > 
> > And lastly.
> 
> Fixed.
> 
> >> +
> >> +    i_bytes_per_frame = p_aout->format.i_bytes_per_frame;
> >> +
> >> +    /* Initialize library */
> >> +    if( kaiInit( i_kai_mode ))
> >> +    {
> >> +        msg_Err( p_aout, "cannot initialize KAI");
> >> +
> >> +        goto exit_free_sys;
> >> +    }
> >> +
> >> +    ks_wanted.usDeviceIndex   = 0;
> >> +    ks_wanted.ulType          = KAIT_PLAY;
> >> +    ks_wanted.ulBitsPerSample = BPS_16;
> >> +    ks_wanted.ulSamplingRate  = p_aout->format.i_rate;
> >> +    ks_wanted.ulDataFormat    = MCI_WAVE_FORMAT_PCM;
> >> +    ks_wanted.ulChannels      = i_nb_channels;
> >> +    ks_wanted.ulNumBuffers    = 2;
> >> +    ks_wanted.ulBufferSize    = FRAME_SIZE * i_bytes_per_frame;
> >> +    ks_wanted.fShareable      = !var_CreateGetBool( p_aout,
> >> +
> >> "kai-audio-exclusive-mode");
> >> +    ks_wanted.pfnCallBack     = KaiCallback;
> >> +    ks_wanted.pCallBackData   = p_aout;
> >> +    msg_Dbg( p_aout, "requested ulBufferSize = %ld",
> >> ks_wanted.ulBufferSize );
> >> +
> >> +    /* Open the sound device. */
> >> +    if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
> >> +    {
> >> +        msg_Err( p_aout, "cannot open KAI device");
> >> +
> >> +        goto exit_kai_done;
> >> +    }
> >> +
> >> +    msg_Dbg( p_aout, "open in %s mode",
> >> +             ks_obtained.fShareable ? "shareable" : "exclusive" );
> >> +    msg_Dbg( p_aout, "obtained i_nb_samples = %lu",
> >> +             ks_obtained.ulBufferSize / i_bytes_per_frame );
> >> +    msg_Dbg( p_aout, "obtained i_bytes_per_frame = %d",
> >> +             p_aout->format.i_bytes_per_frame );
> >> +
> >> +    p_aout->pf_play = Play;
> >> +    p_aout->pf_pause = aout_PacketPause;
> >> +    p_aout->pf_flush = aout_PacketFlush;
> >> +
> >> +    aout_PacketInit( p_aout, &p_sys->packet,
> >> +                     ks_obtained.ulBufferSize / i_bytes_per_frame );
> >> +    aout_VolumeSoftInit( p_aout );
> >> +
> >> +    if ( var_Type( p_aout, "audio-device" ) == 0 )
> >> +    {
> >> +        /* First launch. */
> >> +        var_Create( p_aout, "audio-device",
> >> +                    VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
> >> +        text.psz_string = _("Audio Device");
> >> +        var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text,
> > 
> > NULL
> > 
> >> );
> >> +
> >> +        val.i_int = AOUT_VAR_STEREO;
> >> +        text.psz_string = _("Stereo");
> >> +        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val,
> >> &text );
> >> +        val.i_int = AOUT_VAR_MONO;
> >> +        text.psz_string = _("Mono");
> >> +        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val,
> >> &text );
> >> +        if ( i_nb_channels == 2 )
> >> +        {
> >> +            val.i_int = AOUT_VAR_STEREO;
> >> +        }
> >> +        else
> >> +        {
> >> +            val.i_int = AOUT_VAR_MONO;
> >> +        }
> >> +        var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val,
> >> NULL );
> >> +        var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
> >> NULL );
> >> +    }
> >> +
> >> +    var_TriggerCallback( p_aout, "intf-change" );
> >> +
> >> +    /* Prevent SIG_FPE */
> >> +    _control87(MCW_EM, MCW_EM);
> >> +
> >> +    return VLC_SUCCESS;
> >> +
> >> +exit_kai_done :
> >> +    kaiDone();
> >> +
> >> +exit_free_sys :
> >> +    free( p_sys );
> >> +
> >> +    return VLC_EGENERIC;
> >> +}
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * Play: play a sound samples buffer
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +static void Play (audio_output_t *p_aout, block_t *block)
> >> +{
> >> +    aout_sys_t *p_sys = p_aout->sys;
> >> +
> >> +    kaiPlay( p_sys->hkai );
> >> +
> >> +    aout_PacketPlay( p_aout, block );
> >> +}
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * Close: close the audio device
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +static void Close ( vlc_object_t *p_this )
> >> +{
> >> +    audio_output_t *p_aout = (audio_output_t *)p_this;
> >> +    aout_sys_t *p_sys = p_aout->sys;
> >> +
> >> +    kaiClose( p_sys->hkai );
> >> +    kaiDone();
> >> +
> >> +    aout_PacketDestroy( p_aout );
> >> +    free( p_sys );
> >> +}
> >> +
> > 
> > +/***********************************************************************
> > ******
> > 
> >> + * KaiCallback: what to do once KAI has played sound samples
> >> +
> > 
> > *************************************************************************
> > ****/
> > 
> >> +static ULONG APIENTRY KaiCallback( PVOID p_cb_data,
> >> +                                   PVOID p_buffer,
> >> +                                   ULONG i_buf_size )
> >> +{
> >> +    audio_output_t *p_aout = (audio_output_t *)p_cb_data;
> >> +    aout_buffer_t  *p_aout_buffer;
> >> +    mtime_t next_date;
> >> +    ULONG i_len = 0;
> >> +
> >> +    next_date = mdate();
> >> +    while ( i_len < i_buf_size )
> >> +    {
> >> +        if( next_date < mdate())
> >> +            next_date = mdate();
> > 
> > mdate() should be cached.
> 
> Ok.
> 
> >> +
> >> +        /* Get the next audio data buffer */
> >> +        p_aout_buffer = aout_PacketNext( p_aout, next_date );
> > 
> > next_date should include the (estimated) latency of the audio hardware
> > output and buffer. Otherwise lip sync will not work.
> 
> What is 'lip sync' ?

http://en.wikipedia.org/wiki/Audio_to_video_synchronization

> >> +
> >> +        if(!p_aout_buffer)
> >> +        {
> >> +            // means we are too early to request a new buffer?
> >> +            mwait( next_date - AOUT_MAX_PTS_ADVANCE/4 );
> > 
> > This should probably 'continue'.
> 
> Just 'continue' ? I think, it will cause CPU load to 99.9% unless we
> give up time slice.

You're calling mwait(). If you mean to say that using mwait() is a bad idea, I 
agree, but it's your code not mine.

> > It should even break out of the loop if
> > i_len is non-zero, assuming KAI tolerates short reads.
> 
> Do you mean that i_len shorter than i_buf_size should be return ?

If it works.

> KAI recognizes a shorter return value than i_buf_size as a end signature
> of a stream.

Then not.

But then aout_Packet*() is not appropriate.

> >> +            next_date = mdate();
> >> +            p_aout_buffer = aout_PacketNext( p_aout, next_date );
> >> +        }
> >> +
> >> +        if ( p_aout_buffer != NULL )
> >> +        {
> >> +            vlc_memcpy( ( uint8_t * ) p_buffer + i_len,
> >> +                        p_aout_buffer->p_buffer,
> >> +                        p_aout_buffer->i_buffer );
> > 
> > I don't understand how you ensure that
> > i_len + p_aout_buffer->i_buffer <= i_buf_size
> 
> I think the following codes ensures this.
> 
>     aout_PacketInit( p_aout, &p_sys->packet,
>                      ks_obtained.ulBufferSize / i_bytes_per_frame );

This ensures that:
p_aout_buffer->i_buffer == ks_obtained.ulBufferSize / i_bytes_per_frame

Maybe:
i_buf_size == ks_obtained.ulBufferSize / i_bytes_per_frame

Though this is not obvious. But even then, this proves:
p_aout_buffer->i_buffer <= i_buf_size

So this assumes i_len == 0. Then the loop is not designed correctly.


> > Hmm, this is VLC audio output cargo cult programming. In practice, most
> > outputs do not need zeroes.
> 
> Even if we could not get any packets from VLC, there is no need to fill
> the audio buffers for audio output such as KAI with zeros ?

That depends on the audio API. Most will just play zeroes implicitly if they 
underrun, which is OK. Some will instead replay the buffer again, which is 
bad.


-- 
Rémi Denis-Courmont
http://www.remlab.net/
http://fi.linkedin.com/in/remidenis



More information about the vlc-devel mailing list