]> git.sesse.net Git - vlc/blobdiff - modules/gui/macosx/aout.m
Audio output 3. Expect major breakages.
[vlc] / modules / gui / macosx / aout.m
index 4f192832ac2889bce8eaa7e53c64e7676fa7fc26..4d11cdb329582361c2f3e7f30a3162e036c96abf 100644 (file)
@@ -1,11 +1,12 @@
 /*****************************************************************************
  * aout.m: CoreAudio output plugin
  *****************************************************************************
- * Copyright (C) 2001 VideoLAN
- * $Id: aout.m,v 1.1 2002/08/04 17:23:43 sam Exp $
+ * Copyright (C) 2002 VideoLAN
+ * $Id: aout.m,v 1.2 2002/08/07 21:36:56 massiot Exp $
  *
  * Authors: Colin Delacroix <colin@zoy.org>
  *          Jon Lech Johansen <jon-vl@nanocrew.net>
+ *          Christophe Massiot <massiot@via.ecp.fr>
  *
  * 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
  * Preamble
  *****************************************************************************/
 #include <string.h>
+#include <stdlib.h>
 
 #include <vlc/vlc.h>
 #include <vlc/aout.h>
+#include "aout_internal.h"
 
 #include <Carbon/Carbon.h>
 #include <CoreAudio/AudioHardware.h>
 struct aout_sys_t
 {
     AudioDeviceID       device;         // the audio device
-    AudioConverterRef   s_converter;    // the AudioConverter
-    int                 b_format;       // format begun 
-
-    AudioStreamBasicDescription s_src_stream_format;
-    AudioStreamBasicDescription s_dst_stream_format;
-
-    Ptr                 p_buffer;       // ptr to the 32 bit float data
-    UInt32              ui_buffer_size; // audio device buffer size
-    vlc_bool_t          b_buffer_data;  // available buffer data?
-    vlc_mutex_t         mutex_lock;     // pthread locks for sync of
-    vlc_cond_t          cond_sync;      // Play and callback
-    mtime_t             clock_diff;     // diff between system clock & audio
+
+    AudioStreamBasicDescription stream_format;
+
+    UInt32              i_buffer_size;  // audio device buffer size
+    mtime_t             clock_diff;
 };
 
 /*****************************************************************************
  * Local prototypes.
  *****************************************************************************/
-static int      SetFormat       ( aout_thread_t * );
-static int      GetBufInfo      ( aout_thread_t *, int );
-static void     Play            ( aout_thread_t *, byte_t *, int );
-
-static int      CABeginFormat   ( aout_thread_t * );
-static int      CAEndFormat     ( aout_thread_t * );
+static int      SetFormat       ( aout_instance_t *p_aout );
+static void     Play            ( aout_instance_t *p_aout,
+                                  aout_buffer_t *p_buffer );
 
-static OSStatus CAIOCallback    ( AudioDeviceID inDevice,
+static OSStatus IOCallback      ( AudioDeviceID inDevice,
                                   const AudioTimeStamp *inNow, 
                                   const void *inInputData, 
                                   const AudioTimeStamp *inInputTime,
@@ -77,415 +70,192 @@ static OSStatus CAIOCallback    ( AudioDeviceID inDevice,
                                   void *threadGlobals );
 
 /*****************************************************************************
- * OpenAudio: opens a CoreAudio HAL device
+ * Open: open a CoreAudio HAL device
  *****************************************************************************/
-int E_(OpenAudio) ( vlc_object_t *p_this )
+int E_(OpenAudio)( vlc_object_t * p_this )
 {
-    aout_thread_t * p_aout = (aout_thread_t *)p_this;
     OSStatus err;
-    UInt32 ui_param_size;
-
-    /* allocate instance */
-    p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
-    if( p_aout->p_sys == NULL )
+    UInt32 i_param_size;
+    aout_instance_t * p_aout = (aout_instance_t *)p_this;
+    struct aout_sys_t * p_sys;
+
+    /* Allocate instance */
+    p_sys = p_aout->output.p_sys = malloc( sizeof( struct aout_sys_t ) );
+    memset( p_sys, 0, sizeof( struct aout_sys_t ) );
+    if( p_aout->output.p_sys == NULL )
     {
         msg_Err( p_aout, "out of memory" );
         return( 1 );
     }
 
-    /* initialize members */
-    memset( p_aout->p_sys, 0, sizeof( aout_sys_t ) );
-
-    /* get the default output device */
-    ui_param_size = sizeof( p_aout->p_sys->device );
+    /* Get the default output device */
+    /* FIXME : be more clever in choosing from several devices */
+    i_param_size = sizeof( p_sys->device );
     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
-                                    &ui_param_size, 
-                                    (void *)&p_aout->p_sys->device );
-
+                                    &i_param_size, 
+                                    (void *)&p_sys->device );
     if( err != noErr ) 
     {
         msg_Err( p_aout, "failed to get the device: %d", err );
         return( -1 );
     }
 
-    /* get the buffer size that the device uses for IO */
-    ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
-    err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false, 
-                                  kAudioDevicePropertyBufferSize, 
-                                  &ui_param_size,
-                                  &p_aout->p_sys->ui_buffer_size );
-
-    if( err != noErr )
-    {
-        msg_Err( p_aout, "failed to get device buffer size: %d", err );
-        return( -1 );
-    }
-
-    /* get a description of the data format used by the device */
-    ui_param_size = sizeof( p_aout->p_sys->s_dst_stream_format ); 
-    err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false, 
-                                  kAudioDevicePropertyStreamFormat, 
-                                  &ui_param_size,
-                                  &p_aout->p_sys->s_dst_stream_format );
-
-    if( err != noErr )
-    {
-        msg_Err( p_aout, "failed to get dst stream format: %d", err );
-        return( -1 );
-    }
+    p_aout->output.pf_setformat = SetFormat;
+    p_aout->output.pf_play = Play;
 
-    if( p_aout->p_sys->s_dst_stream_format.mFormatID != kAudioFormatLinearPCM )
-    {
-        msg_Err( p_aout, "kAudioFormatLinearPCM required" );
-        return( -1 );
-    }
-
-    /* initialize mutex and cond */
-    vlc_mutex_init( p_aout, &p_aout->p_sys->mutex_lock );
-    vlc_cond_init( p_aout, &p_aout->p_sys->cond_sync );
-
-    /* initialize source stream format */
-    memcpy( &p_aout->p_sys->s_src_stream_format,
-            &p_aout->p_sys->s_dst_stream_format,
-            sizeof( p_aout->p_sys->s_src_stream_format ) );
-
-    if( CABeginFormat( p_aout ) )
-    {
-        msg_Err( p_aout, "CABeginFormat failed" );
-        return( -1 );
-    }
-
-    p_aout->pf_setformat = SetFormat;
-    p_aout->pf_getbufinfo = GetBufInfo;
-    p_aout->pf_play = Play;
-
-    return( 0 );
+    return 0;
 }
 
 /*****************************************************************************
- * SetFormat: pretends to set the dsp output format
+ * SetFormat: find the closest available format from p_format
  *****************************************************************************/
-static int SetFormat( aout_thread_t *p_aout )
+static int SetFormat( aout_instance_t * p_aout )
 {
-    if( CAEndFormat( p_aout ) )
+    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+    OSErr err;
+
+    /* Get a description of the data format used by the device */
+    UInt32 i_param_size = sizeof( p_sys->stream_format ); 
+    err = AudioDeviceGetProperty( p_sys->device, 0, false, 
+                                  kAudioDevicePropertyStreamFormat, 
+                                  &i_param_size,
+                                  &p_sys->stream_format );
+    if( err != noErr )
     {
-        msg_Err( p_aout, "CAEndFormat failed" );
-        return( -1 );
+        msg_Err( p_aout, "failed to get stream format: %d", err );
+        return -1 ;
     }
 
-    switch( p_aout->i_format )
+    if( p_sys->stream_format.mFormatID != kAudioFormatLinearPCM )
     {
-        case AOUT_FMT_S8:
-            msg_Err( p_aout,
-                     "Signed 8 not supported yet, please report stream" );
-            return( -1 );
-                    
-        case AOUT_FMT_U8:
-            msg_Err( p_aout,
-                     "Unsigned 8 not supported yet, please report stream" );
-            return( -1 );
-
-        case AOUT_FMT_S16_LE:
-            p_aout->p_sys->s_src_stream_format.mFormatFlags &=
-                ~kLinearPCMFormatFlagIsBigEndian;
-            p_aout->p_sys->s_src_stream_format.mFormatFlags |=
-                kLinearPCMFormatFlagIsSignedInteger;
-            break;
-
-        case AOUT_FMT_S16_BE:
-            p_aout->p_sys->s_src_stream_format.mFormatFlags |=
-                kLinearPCMFormatFlagIsBigEndian;
-            p_aout->p_sys->s_src_stream_format.mFormatFlags |=
-                kLinearPCMFormatFlagIsSignedInteger;
-            break;
-
-        case AOUT_FMT_U16_LE:
-            p_aout->p_sys->s_src_stream_format.mFormatFlags &=
-                ~kLinearPCMFormatFlagIsBigEndian;
-            p_aout->p_sys->s_src_stream_format.mFormatFlags &=
-                ~kLinearPCMFormatFlagIsSignedInteger;
-            break;
-                    
-        case AOUT_FMT_U16_BE:
-            p_aout->p_sys->s_src_stream_format.mFormatFlags |=
-                kLinearPCMFormatFlagIsBigEndian;
-            p_aout->p_sys->s_src_stream_format.mFormatFlags &=
-                ~kLinearPCMFormatFlagIsSignedInteger;
-            break;
-                    
-        default:
-            msg_Err( p_aout, "audio format (0x%08x) not supported now,"
-                             "please report stream", p_aout->i_format );
-            return( -1 );
+        msg_Err( p_aout, "kAudioFormatLinearPCM required" );
+        return -1 ;
     }
 
-    /* source format is not float */
-    p_aout->p_sys->s_src_stream_format.mFormatFlags &=
-        ~kLinearPCMFormatFlagIsFloat;
-
-    /* if destination format is float, take size diff into account */
-    if( p_aout->p_sys->s_dst_stream_format.mFormatFlags & 
-        kLinearPCMFormatFlagIsFloat )
+    /* We only deal with floats */
+    if ( p_aout->output.output.i_format != AOUT_FMT_FLOAT32 )
     {
-        p_aout->p_sys->s_src_stream_format.mBytesPerPacket =
-            p_aout->p_sys->s_dst_stream_format.mBytesPerPacket / 2;
-        p_aout->p_sys->s_src_stream_format.mBytesPerFrame =
-            p_aout->p_sys->s_src_stream_format.mBytesPerFrame / 2;
-        p_aout->p_sys->s_src_stream_format.mBitsPerChannel =
-            p_aout->p_sys->s_src_stream_format.mBitsPerChannel / 2;
+        msg_Err( p_aout, "cannot set format 0x%x",
+                 p_aout->output.output.i_format );
+        return -1;
     }
-
-    /* set sample rate and channels per frame */
-    p_aout->p_sys->s_src_stream_format.mSampleRate = p_aout->i_rate; 
-    p_aout->p_sys->s_src_stream_format.mChannelsPerFrame = p_aout->i_channels;
-
-    if( CABeginFormat( p_aout ) )
+    p_sys->stream_format.mFormatFlags |=
+        kLinearPCMFormatFlagIsFloat;
+
+    /* Set sample rate and channels per frame */
+    p_sys->stream_format.mSampleRate
+                 = p_aout->output.output.i_rate; 
+    p_sys->stream_format.mChannelsPerFrame
+                 = p_aout->output.output.i_channels;
+
+    /* Get the buffer size that the device uses for IO */
+    i_param_size = sizeof( p_sys->i_buffer_size );
+#if 0
+    err = AudioDeviceGetProperty( p_sys->device, 0, false, 
+                                  kAudioDevicePropertyBufferSize, 
+                                  &i_param_size, &p_sys->i_buffer_size );
+msg_Dbg( p_aout, "toto : %d", p_sys->i_buffer_size );
+#else
+    p_sys->i_buffer_size = sizeof(float) * p_aout->output.output.i_channels
+                            * 1536;
+    err = AudioDeviceSetProperty( p_sys->device, 0, 0, false,
+                                  kAudioDevicePropertyBufferSize,
+                                  i_param_size, &p_sys->i_buffer_size );
+#endif
+    if( err != noErr )
     {
-        msg_Err( p_aout, "CABeginFormat failed" );
+        msg_Err( p_aout, "failed to set device buffer size: %d", err );
         return( -1 );
     }
 
-    return( 0 );
-}
-
-/*****************************************************************************
- * GetBufInfo: returns available bytes in buffer
- *****************************************************************************/
-static int GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
-{
-    return( 0 ); /* send data as soon as possible */
-}
+    p_aout->output.i_nb_samples = p_sys->i_buffer_size / sizeof(float)
+                                   / p_aout->output.output.i_channels;
 
-/*****************************************************************************
- * CAIOCallback : callback for audio output
- *****************************************************************************/
-static OSStatus CAIOCallback( AudioDeviceID inDevice,
-                              const AudioTimeStamp *inNow, 
-                              const void *inInputData,
-                              const AudioTimeStamp *inInputTime, 
-                              AudioBufferList *outOutputData,
-                              const AudioTimeStamp *inOutputTime, 
-                              void *threadGlobals )
-{
-    aout_thread_t *p_aout = (aout_thread_t *)threadGlobals;
-    aout_sys_t *p_sys = p_aout->p_sys;
-
-    AudioTimeStamp host_time;
-
-    host_time.mFlags = kAudioTimeStampHostTimeValid;
-    AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
-    //intf_Msg( "%lld", AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000 + p_aout->p_sys->clock_diff - p_aout->date );
-    p_aout->date = p_aout->p_sys->clock_diff + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
+    /* Add callback */
+    err = AudioDeviceAddIOProc( p_sys->device,
+                                (AudioDeviceIOProc)IOCallback,
+                                (void *)p_aout );
 
-    /* move data into output data buffer */
-    if( p_sys->b_buffer_data )
-    {
-        BlockMoveData( p_sys->p_buffer,
-                       outOutputData->mBuffers[ 0 ].mData, 
-                       p_sys->ui_buffer_size );
-    }
-    else
+    /* Open the output with callback IOCallback */
+    err = AudioDeviceStart( p_sys->device,
+                            (AudioDeviceIOProc)IOCallback );
+    if( err != noErr )
     {
-        memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->ui_buffer_size);
-//X        msg_Warn( p_aout, "audio output is starving, expect glitches" );
+        msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
+        return -1;
     }
 
-    /* see Play below */
-    vlc_mutex_lock( &p_sys->mutex_lock );
-    p_sys->b_buffer_data = 0;
-    vlc_cond_signal( &p_sys->cond_sync );
-    vlc_mutex_unlock( &p_sys->mutex_lock );
+    /* Let's pray for the following operation to be atomic... */
+    p_sys->clock_diff = mdate()
+         - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
 
-    return( noErr );     
+    return 0;
 }
 
 /*****************************************************************************
- * Play: play a sound
+ * Close: close the CoreAudio HAL device
  *****************************************************************************/
-static void Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
+void E_(CloseAudio)( aout_instance_t * p_aout )
 {
-    OSStatus err;
-    UInt32 ui_buffer_size = p_aout->p_sys->ui_buffer_size;
-
-    /* 
-     * wait for a callback to occur (to flush the buffer), so Play
-     * can't be called twice, losing the data we just wrote. 
-     */
-    vlc_mutex_lock( &p_aout->p_sys->mutex_lock );
-    if ( p_aout->p_sys->b_buffer_data )
-    {
-        vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
-    }
-    vlc_mutex_unlock( &p_aout->p_sys->mutex_lock );
-
-    err = AudioConverterConvertBuffer( p_aout->p_sys->s_converter,
-                                       i_size, buffer,
-                                       &ui_buffer_size,
-                                       p_aout->p_sys->p_buffer );
+    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+    OSStatus err; 
 
+    /* Stop playing sound through the device */
+    err = AudioDeviceStop( p_sys->device,
+                           (AudioDeviceIOProc)IOCallback ); 
     if( err != noErr )
     {
-        msg_Err( p_aout, "ConvertBuffer failed: %d", err );
-    }
-    else
-    {
-        p_aout->p_sys->b_buffer_data = 1;
-    }
-}
-
-/*****************************************************************************
- * CloseAudio: closes the CoreAudio HAL device
- *****************************************************************************/
-void E_(CloseAudio) ( vlc_object_t *p_this )
-{
-    aout_thread_t * p_aout = (aout_thread_t *)p_this;
-
-    if( CAEndFormat( p_aout ) )
-    {
-        msg_Err( p_aout, "CAEndFormat failed" );
+        msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
     }
 
-    /* destroy lock and cond */
-    vlc_mutex_destroy( &p_aout->p_sys->mutex_lock );
-    vlc_cond_destroy( &p_aout->p_sys->cond_sync );
-
-    free( p_aout->p_sys );
+    free( p_sys );
 }
 
 /*****************************************************************************
- * CABeginFormat: creates an AudioConverter 
+ * Play: queue a buffer for playing by IOCallback
  *****************************************************************************/
-static int CABeginFormat( aout_thread_t *p_aout )
+static void Play( aout_instance_t * p_aout, aout_buffer_t * p_buffer )
 {
-    OSStatus err;
-    UInt32 ui_param_size;
-
-    if( p_aout->p_sys->b_format )
-    {
-        msg_Err( p_aout, "CABeginFormat (b_format)" );
-        return( 1 );
-    }
-
-    p_aout->p_sys->ui_buffer_size = 2 * 2 * sizeof(s16) * 
-        ((s64)p_aout->i_rate * AOUT_BUFFER_DURATION) / 1000000; 
-
-    /* set the buffer size that the device uses for IO */
-    ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
-    err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false, 
-                                  kAudioDevicePropertyBufferSize, 
-                                  ui_param_size,
-                                  &p_aout->p_sys->ui_buffer_size );
-    //p_aout->i_latency = p_aout->p_sys->ui_buffer_size / 2;
-
-    if( err != noErr )
-    {
-        msg_Err( p_aout, "AudioDeviceSetProperty failed: %d", err );
-        return( 1 );
-    }
-
-    /* allocate audio buffer */ 
-    p_aout->p_sys->p_buffer = NewPtrClear( p_aout->p_sys->ui_buffer_size );
-
-    if( p_aout->p_sys->p_buffer == nil )
-    {
-        msg_Err( p_aout, "failed to allocate audio buffer" );
-        return( 1 );
-    }
-
-    /* create a new AudioConverter */
-    err = AudioConverterNew( &p_aout->p_sys->s_src_stream_format,
-                             &p_aout->p_sys->s_dst_stream_format,
-                             &p_aout->p_sys->s_converter );
-
-    if( err != noErr )
-    {
-        msg_Err( p_aout, "AudioConverterNew failed: %d", err );
-        DisposePtr( p_aout->p_sys->p_buffer );
-        return( 1 );
-    }
-
-    /* add callback */
-    err = AudioDeviceAddIOProc( p_aout->p_sys->device, 
-                                (AudioDeviceIOProc)CAIOCallback, 
-                                (void *)p_aout );
-
-    if( err != noErr )
-    {
-        msg_Err( p_aout, "AudioDeviceAddIOProc failed: %d", err );
-        AudioConverterDispose( p_aout->p_sys->s_converter );
-        DisposePtr( p_aout->p_sys->p_buffer );
-        return( 1 );
-    } 
-
-    /* open the output */
-    err = AudioDeviceStart( p_aout->p_sys->device,
-                            (AudioDeviceIOProc)CAIOCallback );
-
-    if( err != noErr )
-    {
-        msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
-        AudioConverterDispose( p_aout->p_sys->s_converter );
-        DisposePtr( p_aout->p_sys->p_buffer );
-        return( 1 );
-    }
-
-    /* Let's pray for the following operation to be atomic... */
-    p_aout->p_sys->clock_diff = mdate()
-         - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000
-         + (mtime_t)p_aout->p_sys->ui_buffer_size / 4 * 1000000 / (mtime_t)p_aout->i_rate
-         + p_aout->p_vlc->i_desync;
-
-    p_aout->p_sys->b_format = 1;
-
-    return( 0 );
+    aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer );
 }
 
 /*****************************************************************************
- * CAEndFormat: destroys the AudioConverter 
+ * IOCallback : callback for audio output
  *****************************************************************************/
-static int CAEndFormat( aout_thread_t *p_aout )
+static OSStatus IOCallback( AudioDeviceID inDevice,
+                            const AudioTimeStamp *inNow, 
+                            const void *inInputData,
+                            const AudioTimeStamp *inInputTime, 
+                            AudioBufferList *outOutputData,
+                            const AudioTimeStamp *inOutputTime, 
+                            void *threadGlobals )
 {
-    OSStatus err; 
+    aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
+    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+    mtime_t         current_date;
+    AudioTimeStamp  host_time;
+    aout_buffer_t * p_buffer;
 
-    if( !p_aout->p_sys->b_format )
-    {
-        msg_Err( p_aout, "CAEndFormat (!b_format)" );
-        return( 1 );
-    }
-
-    /* stop playing sound through the device */
-    err = AudioDeviceStop( p_aout->p_sys->device,
-                           (AudioDeviceIOProc)CAIOCallback ); 
-
-    if( err != noErr )
-    {
-        msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
-        return( 1 );
-    }
+    host_time.mFlags = kAudioTimeStampHostTimeValid;
+    AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
+    current_date = p_sys->clock_diff
+                 + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
 
-    /* remove the callback */
-    err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
-                                   (AudioDeviceIOProc)CAIOCallback ); 
+    p_buffer = aout_OutputNextBuffer( p_aout, current_date );
 
-    if( err != noErr )
+    /* move data into output data buffer */
+    if ( p_buffer != NULL )
     {
-        msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: %d", err );
-        return( 1 );
+        BlockMoveData( p_buffer->p_buffer,
+                       outOutputData->mBuffers[ 0 ].mData, 
+                       p_sys->i_buffer_size );
+        aout_BufferFree( p_buffer );
     }
-
-    /* destroy the AudioConverter */
-    err = AudioConverterDispose( p_aout->p_sys->s_converter );
-
-    if( err != noErr )
+    else
     {
-        msg_Err( p_aout, "AudioConverterDispose failed: %d", err );
-        return( 1 );
+        memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->i_buffer_size);
     }
 
-    /* release audio buffer */
-    DisposePtr( p_aout->p_sys->p_buffer );
-
-    p_aout->p_sys->b_format = 0;
-
-    return( 0 );
+    return noErr;     
 }
+