Commit bd8d2bad authored by Felix Paul Kühne's avatar Felix Paul Kühne

auhal: switch analog playback to the 2.1 API

This makes use of a 3rd party circular buffer licensed under MIT.

TODO: Pause, TimeGet, Flush
parent f74db532
SOURCES_waveout = waveout.c windows_audio_common.h packet.c
SOURCES_auhal = auhal.c packet.c
SOURCES_auhal = TPCircularBuffer.h TPCircularBuffer.c auhal.c packet.c
SOURCES_audioqueue = audioqueue.c packet.c
SOURCES_opensles_android = opensles_android.c
......
//
// TPCircularBuffer.c
// Circular/Ring buffer implementation
//
// Created by Michael Tyson on 10/12/2011.
// Copyright 2011-2012 A Tasty Pixel. All rights reserved.
#include "TPCircularBuffer.h"
#include <mach/mach.h>
#include <stdio.h>
#define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) {
if ( result != ERR_SUCCESS ) {
printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
return false;
}
return true;
}
bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
// Keep trying until we get our buffer, needed to handle race conditions
int retries = 3;
while ( true ) {
buffer->length = round_page(length); // We need whole page sizes
// Temporarily allocate twice the length, so we have the contiguous address space to
// support a second instance of the buffer directly after
vm_address_t bufferAddress;
kern_return_t result = vm_allocate(mach_task_self(),
&bufferAddress,
buffer->length * 2,
VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Buffer allocation");
return false;
}
// Try again if we fail
continue;
}
// Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half...
result = vm_deallocate(mach_task_self(),
bufferAddress + buffer->length,
buffer->length);
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Buffer deallocation");
return false;
}
// If this fails somehow, deallocate the whole region and try again
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
continue;
}
// Re-map the buffer to the address space immediately after the buffer
vm_address_t virtualAddress = bufferAddress + buffer->length;
vm_prot_t cur_prot, max_prot;
result = vm_remap(mach_task_self(),
&virtualAddress, // mirror target
buffer->length, // size of mirror
0, // auto alignment
0, // force remapping to virtualAddress
mach_task_self(), // same task
bufferAddress, // mirror source
0, // MAP READ-WRITE, NOT COPY
&cur_prot, // unused protection struct
&max_prot, // unused protection struct
VM_INHERIT_DEFAULT);
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Remap buffer memory");
return false;
}
// If this remap failed, we hit a race condition, so deallocate and try again
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
continue;
}
if ( virtualAddress != bufferAddress+buffer->length ) {
// If the memory is not contiguous, clean up both allocated buffers and try again
if ( retries-- == 0 ) {
printf("Couldn't map buffer memory to end of buffer\n");
return false;
}
vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
continue;
}
buffer->buffer = (void*)bufferAddress;
buffer->fillCount = 0;
buffer->head = buffer->tail = 0;
return true;
}
return false;
}
void TPCircularBufferCleanup(TPCircularBuffer *buffer) {
vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
memset(buffer, 0, sizeof(TPCircularBuffer));
}
void TPCircularBufferClear(TPCircularBuffer *buffer) {
int32_t fillCount;
if ( TPCircularBufferTail(buffer, &fillCount) ) {
TPCircularBufferConsume(buffer, fillCount);
}
}
//
// TPCircularBuffer.h
// Circular/Ring buffer implementation
//
// https://github.com/michaeltyson/TPCircularBuffer
//
// Created by Michael Tyson on 10/12/2011.
// Copyright 2011-2012 A Tasty Pixel. All rights reserved.
//
//
// This implementation makes use of a virtual memory mapping technique that inserts a virtual copy
// of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around
// logic. Clients can simply use the returned memory address as if it were contiguous space.
//
// The implementation is thread-safe in the case of a single producer and single consumer.
//
// Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and
// adapted to Darwin by Kurt Revis (http://www.snoize.com,
// http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz)
//
#ifndef TPCircularBuffer_h
#define TPCircularBuffer_h
#include <libkern/OSAtomic.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *buffer;
int32_t length;
int32_t tail;
int32_t head;
volatile int32_t fillCount;
} TPCircularBuffer;
/*!
* Initialise buffer
*
* Note that the length is advisory only: Because of the way the
* memory mirroring technique works, the true buffer length will
* be multiples of the device page size (e.g. 4096 bytes)
*
* @param buffer Circular buffer
* @param length Length of buffer
*/
bool TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length);
/*!
* Cleanup buffer
*
* Releases buffer resources.
*/
void TPCircularBufferCleanup(TPCircularBuffer *buffer);
/*!
* Clear buffer
*
* Resets buffer to original, empty state.
*
* This is safe for use by consumer while producer is accessing
* buffer.
*/
void TPCircularBufferClear(TPCircularBuffer *buffer);
// Reading (consuming)
/*!
* Access end of buffer
*
* This gives you a pointer to the end of the buffer, ready
* for reading, and the number of available bytes to read.
*
* @param buffer Circular buffer
* @param availableBytes On output, the number of bytes ready for reading
* @return Pointer to the first bytes ready for reading, or NULL if buffer is empty
*/
static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) {
*availableBytes = buffer->fillCount;
if ( *availableBytes == 0 ) return NULL;
return (void*)((char*)buffer->buffer + buffer->tail);
}
/*!
* Consume bytes in buffer
*
* This frees up the just-read bytes, ready for writing again.
*
* @param buffer Circular buffer
* @param amount Number of bytes to consume
*/
static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) {
buffer->tail = (buffer->tail + amount) % buffer->length;
OSAtomicAdd32Barrier(-amount, &buffer->fillCount);
}
/*!
* Version of TPCircularBufferConsume without the memory barrier, for more optimal use in single-threaded contexts
*/
static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) {
buffer->tail = (buffer->tail + amount) % buffer->length;
buffer->fillCount -= amount;
}
/*!
* Access front of buffer
*
* This gives you a pointer to the front of the buffer, ready
* for writing, and the number of available bytes to write.
*
* @param buffer Circular buffer
* @param availableBytes On output, the number of bytes ready for writing
* @return Pointer to the first bytes ready for writing, or NULL if buffer is full
*/
static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) {
*availableBytes = (buffer->length - buffer->fillCount);
if ( *availableBytes == 0 ) return NULL;
return (void*)((char*)buffer->buffer + buffer->head);
}
// Writing (producing)
/*!
* Produce bytes in buffer
*
* This marks the given section of the buffer ready for reading.
*
* @param buffer Circular buffer
* @param amount Number of bytes to produce
*/
static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int amount) {
buffer->head = (buffer->head + amount) % buffer->length;
OSAtomicAdd32Barrier(amount, &buffer->fillCount);
}
/*!
* Version of TPCircularBufferProduce without the memory barrier, for more optimal use in single-threaded contexts
*/
static __inline__ __attribute__((always_inline)) void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int amount) {
buffer->head = (buffer->head + amount) % buffer->length;
buffer->fillCount += amount;
}
/*!
* Helper routine to copy bytes to buffer
*
* This copies the given bytes to the buffer, and marks them ready for writing.
*
* @param buffer Circular buffer
* @param src Source buffer
* @param len Number of bytes in source buffer
* @return true if bytes copied, false if there was insufficient space
*/
static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) {
int32_t space;
void *ptr = TPCircularBufferHead(buffer, &space);
if ( space < len ) return false;
memcpy(ptr, src, len);
TPCircularBufferProduce(buffer, len);
return true;
}
#ifdef __cplusplus
}
#endif
#endif
......@@ -40,6 +40,8 @@
#import <AudioToolbox/AudioFormat.h> // AudioFormatGetProperty
#import <CoreServices/CoreServices.h>
#import "TPCircularBuffer.h"
#ifndef verify_noerr
# define verify_noerr(a) assert((a) == noErr)
#endif
......@@ -55,6 +57,8 @@
#define BUFSIZE (FRAMESIZE * 8) * 8
#define AOUT_VAR_SPDIF_FLAG 0xf00000
#define kBufferLength BUFSIZE
#define AOUT_VOLUME_DEFAULT 256
#define AOUT_VOLUME_MAX 512
......@@ -68,31 +72,31 @@
struct aout_sys_t
{
aout_packet_t packet;
AudioDeviceID i_default_dev; /* DeviceID of defaultOutputDevice */
AudioDeviceID i_selected_dev; /* DeviceID of the selected device */
AudioDeviceIOProcID i_procID; /* DeviceID of current device */
UInt32 i_devices; /* Number of CoreAudio Devices */
bool b_digital; /* Are we running in digital mode? */
mtime_t clock_diff; /* Difference between VLC clock and Device clock */
uint8_t chans_to_reorder; /* do we need channel reordering */
AudioDeviceID i_default_dev; /* DeviceID of defaultOutputDevice */
AudioDeviceID i_selected_dev; /* DeviceID of the selected device */
AudioDeviceIOProcID i_procID; /* DeviceID of current device */
UInt32 i_devices; /* Number of CoreAudio Devices */
bool b_digital; /* Are we running in digital mode? */
mtime_t clock_diff; /* Difference between VLC clock and Device clock */
uint8_t chans_to_reorder; /* do we need channel reordering */
uint8_t chan_table[AOUT_CHAN_MAX];
UInt32 i_numberOfChannels;
TPCircularBuffer circular_buffer; /* circular buffer to swap the audio data */
/* AUHAL specific */
Component au_component; /* The Audiocomponent we use */
AudioUnit au_unit; /* The AudioUnit we use */
uint8_t p_remainder_buffer[BUFSIZE];
uint32_t i_read_bytes;
uint32_t i_total_bytes;
Component au_component; /* The Audiocomponent we use */
AudioUnit au_unit; /* The AudioUnit we use */
/* CoreAudio SPDIF mode specific */
pid_t i_hog_pid; /* The keep the pid of our hog status */
AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */
int i_stream_index; /* The index of i_stream_id in an AudioBufferList */
AudioStreamBasicDescription stream_format; /* The format we changed the stream to */
AudioStreamBasicDescription sfmt_revert; /* The original format of the stream */
bool b_revert; /* Wether we need to revert the stream format */
bool b_changed_mixing; /* Wether we need to set the mixing mode back */
pid_t i_hog_pid; /* The keep the pid of our hog status */
AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */
int i_stream_index; /* The index of i_stream_id in an AudioBufferList */
AudioStreamBasicDescription stream_format; /* The format we changed the stream to */
AudioStreamBasicDescription sfmt_revert; /* The original format of the stream */
bool b_revert; /* Wether we need to revert the stream format */
bool b_changed_mixing; /* Wether we need to set the mixing mode back */
};
/*****************************************************************************
......@@ -103,6 +107,10 @@ static int OpenAnalog (audio_output_t *, audio_sample_format_t
static int OpenSPDIF (audio_output_t *, audio_sample_format_t *);
static void Close (vlc_object_t *);
static void PlayAnalog (audio_output_t *, block_t *);
static void FlushAnalog (audio_output_t *aout, bool wait);
static int TimeGet (audio_output_t *aout, mtime_t *);
static void Probe (audio_output_t *);
static int AudioDeviceHasOutput (AudioDeviceID);
......@@ -110,8 +118,8 @@ static int AudioDeviceSupportsDigital(audio_output_t *, AudioDeviceID);
static int AudioStreamSupportsDigital(audio_output_t *, AudioStreamID);
static int AudioStreamChangeFormat (audio_output_t *, AudioStreamID, AudioStreamBasicDescription);
static OSStatus RenderCallbackAnalog (vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
unsigned int, unsigned int, AudioBufferList *);
static OSStatus RenderCallbackAnalog (vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *, unsigned int, unsigned int, AudioBufferList *);
static OSStatus RenderCallbackSPDIF (AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
AudioBufferList *, const AudioTimeStamp *, void *);
static OSStatus HardwareListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
......@@ -142,9 +150,9 @@ vlc_module_begin ()
set_subcategory(SUBCAT_AUDIO_AOUT)
set_callbacks(Open, Close)
add_integer("macosx-audio-device", 0, ADEV_TEXT, ADEV_LONGTEXT, false)
add_integer( "auhal-volume", AOUT_VOLUME_DEFAULT,
VOLUME_TEXT, VOLUME_LONGTEXT, true )
change_integer_range( 0, AOUT_VOLUME_MAX )
add_integer("auhal-volume", AOUT_VOLUME_DEFAULT,
VOLUME_TEXT, VOLUME_LONGTEXT, true)
change_integer_range(0, AOUT_VOLUME_MAX)
vlc_module_end ()
/*****************************************************************************
......@@ -169,19 +177,11 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
p_sys->au_component = NULL;
p_sys->au_unit = NULL;
p_sys->clock_diff = (mtime_t) 0;
p_sys->i_read_bytes = 0;
p_sys->i_total_bytes = 0;
p_sys->i_hog_pid = -1;
p_sys->i_stream_id = 0;
p_sys->i_stream_index = -1;
p_sys->b_revert = false;
p_sys->b_changed_mixing = false;
memset(p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE);
p_aout->time_get = aout_PacketTimeGet;
p_aout->play = aout_PacketPlay;
p_aout->pause = NULL;
p_aout->flush = aout_PacketFlush;
aout_FormatPrint(p_aout, "VLC is looking for:", fmt);
......@@ -254,6 +254,9 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
/* If we change the device we want to use, we should renegotiate the audio chain */
var_AddCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
/* setup circular buffer */
TPCircularBufferInit(&p_sys->circular_buffer, kBufferLength);
/* Check for Digital mode or Analog output mode */
if (AOUT_FMT_SPDIF (fmt) && b_supports_digital) {
if (OpenSPDIF (p_aout, fmt)) {
......@@ -438,6 +441,7 @@ static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(fmt));
msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
p_sys->i_numberOfChannels = aout_FormatNbChannels(fmt);
memset (&new_layout, 0, sizeof(new_layout));
uint32_t chans_out[AOUT_CHAN_MAX];
......@@ -486,9 +490,9 @@ static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
chans_out[5] = AOUT_CHAN_REARRIGHT;
chans_out[6] = AOUT_CHAN_REARCENTER;
p_aout->sys->chans_to_reorder = aout_CheckChannelReorder( NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table );
p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
if (p_aout->sys->chans_to_reorder)
msg_Dbg( p_aout, "channel reordering needed" );
msg_Dbg(p_aout, "channel reordering needed");
break;
case 8:
......@@ -503,9 +507,9 @@ static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
chans_out[6] = AOUT_CHAN_REARLEFT;
chans_out[7] = AOUT_CHAN_REARRIGHT;
p_aout->sys->chans_to_reorder = aout_CheckChannelReorder( NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table );
p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
if (p_aout->sys->chans_to_reorder)
msg_Dbg( p_aout, "channel reordering needed" );
msg_Dbg(p_aout, "channel reordering needed");
break;
}
......@@ -514,7 +518,7 @@ static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
DeviceFormat.mSampleRate = fmt->i_rate;
DeviceFormat.mFormatID = kAudioFormatLinearPCM;
/* We use float 32. It's the best supported format by both VLC and Coreaudio */
/* We use float 32 since this is VLC's endorsed format */
fmt->i_format = VLC_CODEC_FL32;
DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
DeviceFormat.mBitsPerChannel = 32;
......@@ -523,6 +527,7 @@ static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
/* Calculate framesizes and stuff */
DeviceFormat.mFramesPerPacket = 1;
DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
msg_Dbg(p_aout, "bytes per frame %i", DeviceFormat.mBytesPerFrame);
DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
/* Set the desired format */
......@@ -548,7 +553,6 @@ static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
/* Do the last VLC aout setups */
aout_FormatPrepare(fmt);
aout_PacketInit(p_aout, &p_sys->packet, FRAMESIZE, fmt);
/* set the IOproc callback */
input.inputProc = (AURenderCallback) RenderCallbackAnalog;
......@@ -588,6 +592,11 @@ static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
volume * volume * volume,
0));
p_aout->time_get = TimeGet;
p_aout->play = PlayAnalog;
p_aout->pause = NULL;
p_aout->flush = FlushAnalog;
return true;
}
......@@ -883,7 +892,11 @@ static void Stop(audio_output_t *p_aout)
var_DelCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
aout_PacketDestroy(p_aout);
/* clean-up circular buffer */
TPCircularBufferCleanup(&p_sys->circular_buffer);
if (p_sys->b_digital)
aout_PacketDestroy(p_aout);
}
/*****************************************************************************
......@@ -1195,104 +1208,80 @@ static int AudioStreamChangeFormat(audio_output_t *p_aout, AudioStreamID i_strea
return true;
}
static void PlayAnalog (audio_output_t * p_aout, block_t * p_block)
{
struct aout_sys_t *p_sys = p_aout->sys;
if (p_block->i_nb_samples > 0) {
/* Do the channel reordering */
if (p_sys->chans_to_reorder)
{
aout_ChannelReorder(p_block->p_buffer,
p_block->i_buffer,
p_sys->chans_to_reorder,
p_sys->chan_table,
32);
}
/* Render audio into buffer */
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mNumberChannels = p_sys->i_numberOfChannels;
bufferList.mBuffers[0].mData = malloc(p_block->i_nb_samples * sizeof(Float32) * p_sys->i_numberOfChannels);
bufferList.mBuffers[0].mDataByteSize = p_block->i_nb_samples * sizeof(Float32) * p_sys->i_numberOfChannels;
memcpy(bufferList.mBuffers[0].mData, p_block->p_buffer, p_block->i_buffer);
/* move data to buffer */
TPCircularBufferProduceBytes(&p_sys->circular_buffer, bufferList.mBuffers[0].mData, bufferList.mBuffers[0].mDataByteSize);
}
block_Release(p_block);
}
static void FlushAnalog(audio_output_t *p_aout, bool wait)
{
if (!wait)
AudioUnitReset(p_aout->sys->au_unit, kAudioUnitScope_Global, 0);
}
static int TimeGet(audio_output_t *p_aout, mtime_t *delay)
{
VLC_UNUSED(p_aout);
VLC_UNUSED(delay);
return -1;
}
/*****************************************************************************
* RenderCallbackAnalog: This function is called everytime the AudioUnit wants
* us to provide some more audio data.
* Don't print anything during normal playback, calling blocking function from
* this callback is not allowed.
*****************************************************************************/
static OSStatus RenderCallbackAnalog(vlc_object_t *_p_aout,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
unsigned int inBusNumber,
unsigned int inNumberFrames,
AudioBufferList *ioData)
{
AudioTimeStamp host_time;
mtime_t current_date = 0;
uint32_t i_mData_bytes = 0;
audio_output_t * p_aout = (audio_output_t *)_p_aout;
struct aout_sys_t * p_sys = p_aout->sys;
static OSStatus RenderCallbackAnalog(vlc_object_t *p_obj,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
VLC_UNUSED(ioActionFlags);
VLC_UNUSED(inTimeStamp);
VLC_UNUSED(inBusNumber);
VLC_UNUSED(inNumberFrames);
host_time.mFlags = kAudioTimeStampHostTimeValid;
AudioDeviceTranslateTime(p_sys->i_selected_dev, inTimeStamp, &host_time);
/* Check for the difference between the Device clock and mdate */
p_sys->clock_diff = - (mtime_t)
AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
p_sys->clock_diff += mdate();
current_date = p_sys->clock_diff +
AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
//- ((mtime_t) 1000000 / p_aout->format.i_rate * 31); // 31 = Latency in Frames. retrieve somewhere
if (ioData == NULL || ioData->mNumberBuffers < 1) {
msg_Err(p_aout, "no iodata or buffers");
return 0;
}
if (ioData->mNumberBuffers > 1)
msg_Err(p_aout, "well this is weird. seems like there is more than one buffer...");
if (p_sys->i_total_bytes > 0) {
i_mData_bytes = __MIN(p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize);
memcpy(ioData->mBuffers[0].mData,
&p_sys->p_remainder_buffer[p_sys->i_read_bytes],
i_mData_bytes);
p_sys->i_read_bytes += i_mData_bytes;
current_date += (mtime_t) ((mtime_t) 1000000 / p_sys->packet.format.i_rate) *
(i_mData_bytes / 4 / aout_FormatNbChannels(&p_sys->packet.format)); // 4 is fl32 specific
if (p_sys->i_read_bytes >= p_sys->i_total_bytes)
p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
}
while(i_mData_bytes < ioData->mBuffers[0].mDataByteSize) {
/* We don't have enough data yet */
block_t * p_buffer;
p_buffer = aout_PacketNext(p_aout, current_date);
if (p_buffer != NULL)
{
/* Do the channel reordering */
if (p_buffer && p_sys->chans_to_reorder)
{
aout_ChannelReorder(p_buffer->p_buffer,
p_buffer->i_buffer,
p_sys->chans_to_reorder,
p_sys->chan_table,
32);
}
audio_output_t * p_aout = (audio_output_t *)p_obj;
struct aout_sys_t * p_sys = p_aout->sys;
uint32_t i_second_mData_bytes = __MIN(p_buffer->i_buffer, ioData->mBuffers[0].mDataByteSize - i_mData_bytes);
int bytesToCopy = ioData->mBuffers[0].mDataByteSize;
Float32 *targetBuffer = (Float32*)ioData->mBuffers[0].mData;
memcpy((uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes,
p_buffer->p_buffer, i_second_mData_bytes);
i_mData_bytes += i_second_mData_bytes;
/* Pull audio from buffer */
int32_t availableBytes;
Float32 *buffer = TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
memcpy(targetBuffer, buffer, __MIN(bytesToCopy, availableBytes));
TPCircularBufferConsume(&p_sys->circular_buffer, __MIN(bytesToCopy, availableBytes));
if (i_mData_bytes >= ioData->mBuffers[0].mDataByteSize)
{
p_sys->i_total_bytes = p_buffer->i_buffer - i_second_mData_bytes;
memcpy(p_sys->p_remainder_buffer,
&p_buffer->p_buffer[i_second_mData_bytes],
p_sys->i_total_bytes);
block_Release(p_buffer);
break;
} else
/* update current_date */
current_date += (mtime_t) ((mtime_t) 1000000 / p_sys->packet.format.i_rate) *
(i_second_mData_bytes / 4 / aout_FormatNbChannels(&p_sys->packet.format)); // 4 is fl32 specific
block_Release(p_buffer);
} else {
memset((uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes,
0,ioData->mBuffers[0].mDataByteSize - i_mData_bytes);
i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
}
}
return noErr;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment