first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef __AE2_DMA_H__
|
||||
#define __AE2_DMA_H__
|
||||
|
||||
|
||||
typedef void * dma_object_t;
|
||||
|
||||
/*
|
||||
* dma_object_t is an object that can be used to control a dma
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
kDirectionNone = 0,
|
||||
kDirectionIn = 1,
|
||||
kDirectionOut = 2
|
||||
} DMADirection;
|
||||
|
||||
typedef struct LLI
|
||||
{
|
||||
uint32_t source;
|
||||
uint32_t destination;
|
||||
struct LLI *next;
|
||||
uint32_t control;
|
||||
} DMALinkedListItem;
|
||||
|
||||
typedef enum {
|
||||
kI2S_0 = 0,
|
||||
kI2S_1,
|
||||
kI2S_2,
|
||||
kI2S_3,
|
||||
kMCA_0,
|
||||
kMCA_1,
|
||||
kAudioDevice_Last
|
||||
} AudioDevice_Index;
|
||||
|
||||
// create a dma object that will transfer to/from a buffer from/to a device, depending
|
||||
// on the direction.
|
||||
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer);
|
||||
void destroy_dma_object(dma_object_t dma);
|
||||
|
||||
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg);
|
||||
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg);
|
||||
|
||||
typedef enum {
|
||||
kFrameError = 0,
|
||||
kRXOverrun,
|
||||
kRXUnderrun,
|
||||
kTXOverrun,
|
||||
kTXUnderrun,
|
||||
kError_last
|
||||
} Error_Index;
|
||||
|
||||
static const char* const kErrorTypeStr[kError_last] = {
|
||||
"Frame_Error",
|
||||
"RX_Overrun",
|
||||
"RX_Underrun",
|
||||
"TX_Overrun",
|
||||
"TX_Underrun",
|
||||
};
|
||||
|
||||
uint32_t getErrorCount(dma_object_t dma, Error_Index which);
|
||||
|
||||
void startDMAObject(dma_object_t dma);
|
||||
void stopDMAObject(dma_object_t dma, bool immediate);
|
||||
|
||||
#endif /* __AE2_DMA_H__ */
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The PIO object pretends to be a DMA for MCA.
|
||||
*/
|
||||
|
||||
#include "pio_ae2.h"
|
||||
#include "ae2_i2s.h"
|
||||
#include "ae2_mca.h"
|
||||
#include <drivers/audio/audio.h>
|
||||
#include <debug.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
//Per AE2 Local DMA Controller/Requests
|
||||
static void* sDMAAddressRX[kAudioDevice_Last] = { (void*)rI2S0_RXDB, (void*)rI2S1_RXDB, (void*)rI2S2_RXDB, (void*)rI2S3_RXDB, (void*)rMCA0_RXDATA, (void*)rMCA1_RXDATA };
|
||||
static void* sDMAAddressTX[kAudioDevice_Last] = { (void*)rI2S0_TXDB, (void*)rI2S1_TXDB, (void*)rI2S2_TXDB, (void*)rI2S3_TXDB, (void*)rMCA0_TXDATA, (void*)rMCA1_TXDATA };
|
||||
|
||||
static internal_pio_object_t* sPIOObjects[kNumPIOs] = { NULL };
|
||||
static uint32_t sPIORunning = 0;
|
||||
|
||||
uint32_t readReg(uint32_t address)
|
||||
{
|
||||
return *(volatile uint32_t *)address;
|
||||
}
|
||||
|
||||
void writeReg(uint32_t address, uint32_t value)
|
||||
{
|
||||
*(volatile uint32_t *)address = value;
|
||||
}
|
||||
|
||||
uint32_t readMCA0Reg(uint32_t offset)
|
||||
{
|
||||
return readReg(rMCA0_BASE + offset);
|
||||
}
|
||||
|
||||
void writeMCA0Reg(uint32_t offset, uint32_t value)
|
||||
{
|
||||
writeReg(rMCA0_BASE + offset, value);
|
||||
}
|
||||
|
||||
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Creating a PIO object\n");
|
||||
// hardcoding to support only MCA right now.
|
||||
if ((device >= kAudioDevice_Last) || ((direction != kDirectionIn) && (direction != kDirectionOut)) || (device != kMCA_0))
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
|
||||
return NULL;
|
||||
}
|
||||
uint32_t whichPIO = (direction == kDirectionIn) ? kPIOReceive : kPIOTransmit;
|
||||
// fail if device already exists
|
||||
if (sPIOObjects[whichPIO])
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "PIO already exists\n");
|
||||
return NULL;
|
||||
}
|
||||
internal_pio_object_t *This = (internal_pio_object_t*)malloc(sizeof(internal_pio_object_t));
|
||||
if (This)
|
||||
{
|
||||
This->mWhichDevice = whichPIO;
|
||||
This->mInterruptHandler = NULL;
|
||||
This->mInterruptHandlerData = NULL;
|
||||
for (uint32_t i = kFrameError; i < kError_last; ++i)
|
||||
{
|
||||
This->mErrorCount[i] = 0;
|
||||
}
|
||||
if (direction == kDirectionIn)
|
||||
{
|
||||
setupPIO(This, sDMAAddressRX[device], buffer, chain, bytesToTransfer);
|
||||
}
|
||||
else
|
||||
{
|
||||
setupPIO(This, buffer, sDMAAddressTX[device], chain, bytesToTransfer);
|
||||
}
|
||||
sPIOObjects[This->mWhichDevice] = This;
|
||||
// we can do this here repeatedly
|
||||
install_int_handler(AE2_INT_MCA0, handleAudioDeviceInterrupt, NULL);
|
||||
}
|
||||
return This;
|
||||
}
|
||||
|
||||
void destroy_dma_object(dma_object_t dma)
|
||||
{
|
||||
// preliminary stop
|
||||
stopDMAObject(dma, true);
|
||||
internal_pio_object_t *This = (internal_pio_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
sPIOObjects[This->mWhichDevice] = NULL;
|
||||
free(This);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getErrorCount(dma_object_t dma, Error_Index which)
|
||||
{
|
||||
// preliminary stop
|
||||
internal_pio_object_t *This = (internal_pio_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
return This->mErrorCount[which];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg)
|
||||
{
|
||||
internal_pio_object_t *This = (internal_pio_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
This->mInterruptHandler = handler;
|
||||
This->mInterruptHandlerData = arg;
|
||||
}
|
||||
}
|
||||
|
||||
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg)
|
||||
{
|
||||
}
|
||||
|
||||
void startDMAObject(dma_object_t dma)
|
||||
{
|
||||
internal_pio_object_t *This = (internal_pio_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
startPIO(This);
|
||||
|
||||
// we wait until two of them are ready before we unmask the int
|
||||
sPIORunning |= (1 << This->mWhichDevice);
|
||||
if (sPIORunning == 3)
|
||||
{
|
||||
unmask_int(AE2_INT_MCA0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stopDMAObject(dma_object_t dma, bool immediate)
|
||||
{
|
||||
internal_pio_object_t *This = (internal_pio_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
sPIORunning &= ~(1 << This->mWhichDevice);
|
||||
if (sPIORunning != 3)
|
||||
{
|
||||
mask_int(AE2_INT_MCA0);
|
||||
}
|
||||
stopPIO(This);
|
||||
}
|
||||
}
|
||||
|
||||
bool receivePIOData(DMALinkedListItem * item)
|
||||
{
|
||||
// we do burst sizes of 16-bit data
|
||||
int16_t data[kBurstSize];
|
||||
volatile uint32_t * src = (volatile uint32_t *)item->source;
|
||||
for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
|
||||
{
|
||||
data[i] = *src;
|
||||
}
|
||||
uint8_t * dst = (uint8_t*)item->destination;
|
||||
memcpy(dst, data, sizeof(data));
|
||||
dst += sizeof(data);
|
||||
item->destination = (uint32_t) dst;
|
||||
item->control -= sizeof(data);
|
||||
return !item->control;
|
||||
}
|
||||
|
||||
bool transmitPIOData(DMALinkedListItem * item)
|
||||
{
|
||||
// we do burst sizes of 16-bit data
|
||||
int16_t data[kBurstSize];
|
||||
uint8_t * src = (uint8_t*)item->source;
|
||||
memcpy(data, src, sizeof(data));
|
||||
volatile uint32_t * dst = (volatile uint32_t *)item->destination;
|
||||
for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
|
||||
{
|
||||
*dst = data[i];
|
||||
}
|
||||
src += sizeof(data);
|
||||
item->source = (uint32_t) src;
|
||||
item->control -= sizeof(data);
|
||||
return !item->control;
|
||||
}
|
||||
|
||||
void setupNextLLI(DMALinkedListItem * item)
|
||||
{
|
||||
*item = *(item->next);
|
||||
}
|
||||
|
||||
void handleAudioDeviceInterrupt()
|
||||
{
|
||||
uint32_t status = readMCA0Reg(rMCASTATUS);
|
||||
|
||||
// <rdar://problem/13467070> Panic when setting AppleSongbirdDSP sidetone EQ
|
||||
// clear any sort of sticky-bit errors.
|
||||
uint32_t errors = status & ( rMCASTATUS_FRAMEEEROR_MASK | rMCASTATUS_RXOVERRUN_MASK | rMCASTATUS_RXUNDERRUN_MASK | rMCASTATUS_TXOVERRUN_MASK | rMCASTATUS_TXUNDERRUN_MASK );
|
||||
writeMCA0Reg(rMCASTATUS, errors);
|
||||
if (errors)
|
||||
{
|
||||
if (sPIOObjects[kPIOReceive])
|
||||
{
|
||||
if (status & rMCASTATUS_FRAMEEEROR_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kFrameError];
|
||||
if (status & rMCASTATUS_RXOVERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kRXOverrun];
|
||||
if (status & rMCASTATUS_RXUNDERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kRXUnderrun];
|
||||
if (status & rMCASTATUS_TXOVERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kTXOverrun];
|
||||
if (status & rMCASTATUS_TXUNDERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kTXUnderrun];
|
||||
}
|
||||
}
|
||||
|
||||
if (status & (1 << rMCASTATUS_RXHIGHWATER))
|
||||
{
|
||||
if (sPIOObjects[kPIOReceive])
|
||||
{
|
||||
if (receivePIOData(&sPIOObjects[kPIOReceive]->mCurrentDMAItem))
|
||||
{
|
||||
setupNextLLI(&sPIOObjects[kPIOReceive]->mCurrentDMAItem);
|
||||
if (sPIOObjects[kPIOReceive]->mInterruptHandler)
|
||||
{
|
||||
sPIOObjects[kPIOReceive]->mInterruptHandler(sPIOObjects[kPIOReceive]->mInterruptHandlerData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status & (1 << rMCASTATUS_TXLOWWATER))
|
||||
{
|
||||
if (sPIOObjects[kPIOTransmit])
|
||||
{
|
||||
if (transmitPIOData(&sPIOObjects[kPIOTransmit]->mCurrentDMAItem))
|
||||
{
|
||||
setupNextLLI(&sPIOObjects[kPIOTransmit]->mCurrentDMAItem);
|
||||
if (sPIOObjects[kPIOTransmit]->mInterruptHandler)
|
||||
{
|
||||
sPIOObjects[kPIOTransmit]->mInterruptHandler(sPIOObjects[kPIOTransmit]->mInterruptHandlerData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setupPIO(internal_pio_object_t * This, void *src, void *dst, DMALinkedListItem *chain, size_t bytesToTransfer)
|
||||
{
|
||||
uint32_t transferSize = bytesToTransfer;
|
||||
uint32_t control = transferSize;
|
||||
|
||||
DMALinkedListItem *chainElement = chain;
|
||||
while(chainElement != NULL)
|
||||
{
|
||||
chainElement->control = (uint32_t)control;
|
||||
chainElement = chainElement->next;
|
||||
if(chainElement == chain)
|
||||
{
|
||||
//We've populated all of the control values in this circular linked list, we are done here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
This->mCurrentDMAItem.source = (uint32_t)src;
|
||||
This->mCurrentDMAItem.destination = (uint32_t)dst;
|
||||
This->mCurrentDMAItem.next = chain;
|
||||
This->mCurrentDMAItem.control = control;
|
||||
}
|
||||
|
||||
void startPIO(internal_pio_object_t * pio_object)
|
||||
{
|
||||
if (pio_object)
|
||||
{
|
||||
if (pio_object->mWhichDevice == kPIOReceive)
|
||||
{
|
||||
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
|
||||
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG | (1 << rMCAUNSRXCFG_IRQ_EN));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
|
||||
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG | (1 << rMCAUNSTXCFG_IRQ_EN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stopPIO(internal_pio_object_t * pio_object)
|
||||
{
|
||||
if (pio_object)
|
||||
{
|
||||
if (pio_object->mWhichDevice == kPIOReceive)
|
||||
{
|
||||
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
|
||||
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG & ~((1 << rMCAUNSRXCFG_IRQ_EN)));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
|
||||
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG & ~((1 << rMCAUNSTXCFG_IRQ_EN)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef __PL080DMAC_AE2__
|
||||
#define __PL080DMAC_AE2__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <platform/int.h>
|
||||
#include "ae2_dma.h"
|
||||
|
||||
typedef enum {
|
||||
kPIOReceive = 0,
|
||||
kPIOTransmit = 1,
|
||||
kNumPIOs = 2,
|
||||
} ePIOType;
|
||||
|
||||
enum { kBurstSize = 4 };
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ePIOType mWhichDevice;
|
||||
int_handler mInterruptHandler;
|
||||
void * mInterruptHandlerData;
|
||||
DMALinkedListItem mCurrentDMAItem;
|
||||
uint32_t mErrorCount[kAudioDevice_Last];
|
||||
} internal_pio_object_t;
|
||||
|
||||
void handleAudioDeviceInterrupt();
|
||||
|
||||
/*
|
||||
setup pio. Set the DMA linked list for doing PIO operations.
|
||||
preps the pio_object for doing it's first pio op
|
||||
*/
|
||||
void setupPIO(internal_pio_object_t * pio_object, void *src, void *dst, DMALinkedListItem *chain, size_t bytesToTransfer);
|
||||
|
||||
/*
|
||||
Startup PIO
|
||||
*/
|
||||
void startPIO(internal_pio_object_t * pio_object);
|
||||
|
||||
/*
|
||||
Stop PIO
|
||||
*/
|
||||
void stopPIO(internal_pio_object_t * pio_object);
|
||||
|
||||
#endif /* __PL080DMAC_AE2__ */
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include "pl080dmac_ae2.h"
|
||||
#include "ae2_i2s.h"
|
||||
#include "ae2_mca.h"
|
||||
#include <drivers/audio/audio.h>
|
||||
#include <debug.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef MAP_SRAM_CACHED
|
||||
#error DMA requires the SRAM to be uncached
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t mDMAChannel;
|
||||
int_handler mInterruptHandler;
|
||||
void * mInterruptHandlerData;
|
||||
int_handler mErrorHandler;
|
||||
void * mErrorHandlerData;
|
||||
} internal_dma_object_t;
|
||||
|
||||
#define validDMAChannel(dmaChannel) (dmaChannel!=0xffffffff)
|
||||
|
||||
//Per AE2 Local DMA Controller/Requests
|
||||
static const uint32_t sDMARequestInterfaceRx[kAudioDevice_Last] = {0, 2, 4, 6, 10, 6};
|
||||
static const uint32_t sDMARequestInterfaceTx[kAudioDevice_Last] = {1, 3, 5, 7, 11, 7};
|
||||
static void* sDMAAddressRX[kAudioDevice_Last] = { (void*)rI2S0_RXDB, (void*)rI2S1_RXDB, (void*)rI2S2_RXDB, (void*)rI2S3_RXDB, (void*)rMCA0_RXDATA, (void*)rMCA1_RXDATA };
|
||||
static void* sDMAAddressTX[kAudioDevice_Last] = { (void*)rI2S0_TXDB, (void*)rI2S1_TXDB, (void*)rI2S2_TXDB, (void*)rI2S3_TXDB, (void*)rMCA0_TXDATA, (void*)rMCA1_TXDATA };
|
||||
|
||||
static internal_dma_object_t* sDMAChannelObjects[kDMAChannelsPerController] = { NULL };
|
||||
static uint32_t sDMACC0InUse = 0;
|
||||
static uint32_t sDMACC0Running = 0;
|
||||
|
||||
uint32_t readReg(uint32_t address)
|
||||
{
|
||||
return *(volatile uint32_t *)address;
|
||||
}
|
||||
|
||||
void writeReg(uint32_t address, uint32_t value)
|
||||
{
|
||||
*(volatile uint32_t *)address = value;
|
||||
}
|
||||
|
||||
uint32_t readDMAReg(uint32_t offset)
|
||||
{
|
||||
return readReg(kDMACBase + offset);
|
||||
}
|
||||
|
||||
void writeDMAReg(uint32_t offset, uint32_t value)
|
||||
{
|
||||
writeReg(kDMACBase + offset, value);
|
||||
}
|
||||
|
||||
uint32_t readDMAChannelReg(uint32_t channel, uint32_t offset)
|
||||
{
|
||||
uint32_t dmaChannelOffset = kDMAC0Base + channel * kDMACRegisterSize;
|
||||
return readReg(dmaChannelOffset + offset);
|
||||
}
|
||||
|
||||
void writeDMAChannelReg(uint32_t channel, uint32_t offset, uint32_t value)
|
||||
{
|
||||
uint32_t dmaChannelOffset = kDMAC0Base + channel * kDMACRegisterSize;
|
||||
writeReg(dmaChannelOffset + offset, value);
|
||||
}
|
||||
|
||||
|
||||
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Creating a DMA object\n");
|
||||
if ((device >= kAudioDevice_Last) || ((direction != kDirectionIn) && (direction != kDirectionOut)))
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
|
||||
return NULL;
|
||||
}
|
||||
internal_dma_object_t *This = (internal_dma_object_t*)malloc(sizeof(internal_dma_object_t));
|
||||
if (This)
|
||||
{
|
||||
uint32_t dmaChannel = acquireDMAChannel();
|
||||
dprintf(DEBUG_CRITICAL, "channel is %d\n", dmaChannel);
|
||||
if (dmaChannel == 0xFFFFFFFF)
|
||||
{
|
||||
free(This);
|
||||
return NULL;
|
||||
}
|
||||
This->mDMAChannel = dmaChannel;
|
||||
This->mInterruptHandler = NULL;
|
||||
This->mInterruptHandlerData = NULL;
|
||||
This->mErrorHandler = NULL;
|
||||
This->mErrorHandlerData = NULL;
|
||||
configureDMA(This->mDMAChannel);
|
||||
if (direction == kDirectionIn)
|
||||
{
|
||||
setupDMA(This->mDMAChannel, sDMAAddressRX[device], buffer, chain, kDirectionIn, sDMARequestInterfaceRx[device], bytesToTransfer);
|
||||
}
|
||||
else
|
||||
{
|
||||
setupDMA(This->mDMAChannel, buffer, sDMAAddressTX[device], chain, kDirectionOut, sDMARequestInterfaceTx[device], bytesToTransfer);
|
||||
}
|
||||
sDMAChannelObjects[This->mDMAChannel] = This;
|
||||
// we can do this here repeatedly
|
||||
install_int_handler(AE2_INT_DMACINTTC, handleAudioDeviceDMAInterrupt, NULL);
|
||||
install_int_handler(AE2_INT_DMACINTERR, handleAudioDeviceDMAInterruptError, NULL);
|
||||
}
|
||||
return This;
|
||||
}
|
||||
|
||||
void destroy_dma_object(dma_object_t dma)
|
||||
{
|
||||
// preliminary stop
|
||||
stopDMAObject(dma, true);
|
||||
internal_dma_object_t *This = (internal_dma_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
sDMAChannelObjects[This->mDMAChannel] = NULL;
|
||||
freeDMAChannel(This->mDMAChannel);
|
||||
free(This);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getErrorCount(dma_object_t, Error_Index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg)
|
||||
{
|
||||
internal_dma_object_t *This = (internal_dma_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
This->mInterruptHandler = handler;
|
||||
This->mInterruptHandlerData = arg;
|
||||
}
|
||||
}
|
||||
|
||||
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg)
|
||||
{
|
||||
internal_dma_object_t *This = (internal_dma_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
This->mErrorHandler = handler;
|
||||
This->mErrorHandlerData = arg;
|
||||
}
|
||||
}
|
||||
|
||||
void startDMAObject(dma_object_t dma)
|
||||
{
|
||||
internal_dma_object_t *This = (internal_dma_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
startDMA(This->mDMAChannel);
|
||||
|
||||
sDMACC0Running |= (1 << This->mDMAChannel);
|
||||
if (sDMACC0Running)
|
||||
{
|
||||
unmask_int(AE2_INT_DMACINTTC);
|
||||
unmask_int(AE2_INT_DMACINTERR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stopDMAObject(dma_object_t dma, bool immediate)
|
||||
{
|
||||
internal_dma_object_t *This = (internal_dma_object_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
if (immediate)
|
||||
{
|
||||
disableDMAImmediate(This->mDMAChannel);
|
||||
}
|
||||
else
|
||||
{
|
||||
disableDMA(This->mDMAChannel);
|
||||
}
|
||||
sDMACC0Running &= ~(1 << This->mDMAChannel);
|
||||
if (!sDMACC0Running)
|
||||
{
|
||||
mask_int(AE2_INT_DMACINTTC);
|
||||
mask_int(AE2_INT_DMACINTERR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleAudioDeviceDMAInterrupt()
|
||||
{
|
||||
uint32_t status = readDMAReg(kDMACIntTCStatus);
|
||||
for (uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
|
||||
{
|
||||
uint32_t mask = 1 << channel;
|
||||
if (status & mask)
|
||||
{
|
||||
writeDMAReg(kDMACIntTCClear, mask); //clear int
|
||||
|
||||
if(sDMAChannelObjects[channel] != NULL && sDMAChannelObjects[channel]->mInterruptHandler)
|
||||
{
|
||||
sDMAChannelObjects[channel]->mInterruptHandler(sDMAChannelObjects[channel]->mInterruptHandlerData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleAudioDeviceDMAInterruptError()
|
||||
{
|
||||
uint32_t status = readDMAReg(kDMACIntErrStatus);
|
||||
for (uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
|
||||
{
|
||||
uint32_t mask = 1 << channel;
|
||||
if (status & mask)
|
||||
{
|
||||
writeDMAReg(kDMACIntErrClear, mask); //clear int
|
||||
if(sDMAChannelObjects[channel] != NULL && sDMAChannelObjects[channel]->mInterruptHandler)
|
||||
{
|
||||
sDMAChannelObjects[channel]->mInterruptHandler(sDMAChannelObjects[channel]->mInterruptHandlerData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t acquireDMAChannel(void)
|
||||
{
|
||||
uint32_t result = 0xFFFFFFFF;
|
||||
for(uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
|
||||
{
|
||||
if(!(sDMACC0InUse & (1 << channel)))
|
||||
{
|
||||
sDMACC0InUse |= (1 << channel);
|
||||
result = channel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sDMACC0InUse)
|
||||
{
|
||||
writeReg(kDMACClockGating, 0);
|
||||
writeDMAReg(kDMACConfiguration, DMACConfiguration_M2_Little | DMACConfiguration_M1_Little | DMACConfiguration_E_Enable);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void freeDMAChannel(uint32_t dmaChannel)
|
||||
{
|
||||
sDMACC0InUse &= ~(1 << dmaChannel);
|
||||
// if there are no channels in use, shut down DMA
|
||||
if (!sDMACC0InUse)
|
||||
{
|
||||
uint32_t config = readDMAReg(kDMACConfiguration);
|
||||
writeDMAReg(kDMACConfiguration, config & ~DMACConfiguration_E_Enable);
|
||||
}
|
||||
}
|
||||
|
||||
void configureDMA(uint32_t dmaChannel)
|
||||
{
|
||||
if(!validDMAChannel(dmaChannel)) return;
|
||||
|
||||
uint32_t control = DMACCxControl_I_Enable |
|
||||
DMACCxControl_D_Master1 |
|
||||
DMACCxControl_S_Master1 |
|
||||
DMACCxControl_SWidth_Halfword |
|
||||
DMACCxControl_DWidth_Halfword |
|
||||
DMACCxControl_SBSize_4 |
|
||||
DMACCxControl_DBSize_4;
|
||||
|
||||
writeDMAChannelReg(dmaChannel, kDMACCtrlOffset, control);
|
||||
}
|
||||
|
||||
void setupDMA(uint32_t dmaChannel, void *src, void *dst, DMALinkedListItem *chain, DMADirection direction, uint32_t peripheral, size_t bytesToTransfer)
|
||||
{
|
||||
if(!validDMAChannel(dmaChannel)) return;
|
||||
|
||||
uint32_t transferSize = bytesToTransfer >> 1; //assuming burst size of DMACCxControl_DWidth_Halfword
|
||||
uint32_t control = readDMAChannelReg(dmaChannel, kDMACCtrlOffset);
|
||||
|
||||
control &= ~(DMACCxControl_TransferSizeMask | // Clear transfer size
|
||||
DMACCxControl_SI_Mask | // Clear source increment
|
||||
DMACCxControl_DI_Mask); // Clear destination increment
|
||||
|
||||
control |= (transferSize & 0x0FFF);
|
||||
|
||||
uint32_t flowControl, sourcePeripheral, destinationPeripheral;
|
||||
if(direction == kDirectionIn) // -> peripheral-to-memory
|
||||
{
|
||||
control |= DMACCxControl_SI_No_Increment;
|
||||
control |= DMACCxControl_DI_Increment;
|
||||
|
||||
flowControl = DMACCxConfiguration_FlowCntrl_Peripheral_to_Memory_DMA;
|
||||
sourcePeripheral = peripheral;
|
||||
destinationPeripheral = 0; //mem
|
||||
}
|
||||
else if(direction == kDirectionOut) //kDirectionOut -> memory-to-peripheral
|
||||
{
|
||||
control |= DMACCxControl_SI_Increment;
|
||||
control |= DMACCxControl_DI_No_Increment;
|
||||
|
||||
flowControl = DMACCxConfiguration_FlowCntrl_Memory_to_Peripheral_DMA;
|
||||
sourcePeripheral = 0; //mem
|
||||
destinationPeripheral = peripheral;
|
||||
}
|
||||
else //kDirectionNone -> mem-to-mem here
|
||||
{
|
||||
control |= DMACCxControl_SI_Increment;
|
||||
control |= DMACCxControl_DI_Increment;
|
||||
|
||||
flowControl = DMACCxConfiguration_FlowCntrl_Memory_to_Memory_DMA;
|
||||
sourcePeripheral = 0; //mem
|
||||
destinationPeripheral = 0; //mem
|
||||
}
|
||||
|
||||
DMALinkedListItem *chainElement = chain;
|
||||
while(chainElement != NULL)
|
||||
{
|
||||
chainElement->control = (uint32_t)control;
|
||||
chainElement = chainElement->next;
|
||||
if(chainElement == chain)
|
||||
{
|
||||
//We've populated all of the control values in this circular linked list, we are done here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
writeDMAChannelReg(dmaChannel, kDMACSourceOffset, (uint32_t)src);
|
||||
writeDMAChannelReg(dmaChannel, kDMACDestOffset, (uint32_t)dst);
|
||||
writeDMAChannelReg(dmaChannel, kDMACLLIOffset, (uint32_t)chain);
|
||||
writeDMAChannelReg(dmaChannel, kDMACCtrlOffset, control);
|
||||
|
||||
flowControl |= DMACCxConfiguration_ITC_Enable;
|
||||
flowControl |= DMACCxConfiguration_IError_Enable;
|
||||
flowControl |= ((destinationPeripheral & 0xF) << 6);
|
||||
flowControl |= ((sourcePeripheral & 0xF) << 1);
|
||||
|
||||
// do this in the start
|
||||
//flowControl |= DMACCxConfiguration_E_Enable;
|
||||
|
||||
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, flowControl);
|
||||
}
|
||||
|
||||
void startDMA(uint32_t dmaChannel)
|
||||
{
|
||||
if(!validDMAChannel(dmaChannel)) return;
|
||||
|
||||
uint32_t flowControl = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
|
||||
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, flowControl |= DMACCxConfiguration_E_Enable);
|
||||
}
|
||||
|
||||
void disableDMA(uint32_t dmaChannel)
|
||||
{
|
||||
if(!validDMAChannel(dmaChannel)) return;
|
||||
|
||||
uint32_t config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
|
||||
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, config | DMACCxConfiguration_Halt);
|
||||
|
||||
config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
|
||||
// give a chance for the DMA to timeout. But don't wait forever...
|
||||
int timeToWait = 0x100;
|
||||
while(config & DMACCxConfiguration_Active && (timeToWait-- > 0))
|
||||
{
|
||||
config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
|
||||
}
|
||||
disableDMAImmediate(dmaChannel);
|
||||
}
|
||||
|
||||
void disableDMAImmediate(uint32_t dmaChannel)
|
||||
{
|
||||
if(!validDMAChannel(dmaChannel)) return;
|
||||
|
||||
uint32_t config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
|
||||
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, config & ~DMACCxConfiguration_E_Enable);
|
||||
|
||||
writeDMAReg(kDMACIntTCClear, 1 << dmaChannel); // clear any outstanding ints
|
||||
writeDMAReg(kDMACIntErrClear, 1 << dmaChannel); // clear any outstanding ints
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef __PL080DMAC_AE2__
|
||||
#define __PL080DMAC_AE2__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <platform/int.h>
|
||||
#include "ae2_dma.h"
|
||||
|
||||
#define kDMACClockGating 0x341A0004
|
||||
|
||||
#define kDMAChannelsPerController 8
|
||||
|
||||
#define kDMACBase 0x341A3000
|
||||
#define kDMAC0Base 0x341A3100
|
||||
|
||||
#define kDMACIntStatus 0x0
|
||||
#define kDMACIntTCStatus 0x4
|
||||
#define kDMACIntTCClear 0x8
|
||||
#define kDMACIntErrStatus 0xC
|
||||
#define kDMACIntErrClear 0x10
|
||||
|
||||
#define kDMACConfiguration 0x30
|
||||
|
||||
#define kDMACSourceOffset 0x0
|
||||
#define kDMACDestOffset 0x4
|
||||
#define kDMACLLIOffset 0x8
|
||||
#define kDMACCtrlOffset 0xC
|
||||
#define kDMACConfigOffset 0x10
|
||||
#define kDMACRegisterSize 0x20
|
||||
|
||||
#define DMACConfiguration_M2_Mask (1 << 2)
|
||||
#define DMACConfiguration_M2_Little (0 << 2)
|
||||
#define DMACConfiguration_M2_Big (1 << 2)
|
||||
#define DMACConfiguration_M1_Mask (1 << 1)
|
||||
#define DMACConfiguration_M1_Little (0 << 1)
|
||||
#define DMACConfiguration_M1_Big (1 << 1)
|
||||
#define DMACConfiguration_E_Mask (1 << 0)
|
||||
#define DMACConfiguration_E_Disable (0 << 0)
|
||||
#define DMACConfiguration_E_Enable (1 << 0)
|
||||
|
||||
#define DMACCxConfiguration_Halt (1 << 18)
|
||||
#define DMACCxConfiguration_Active (1 << 17)
|
||||
#define DMACCxConfiguration_ITC_Enable (1 << 15)
|
||||
#define DMACCxConfiguration_IError_Enable (1 << 14)
|
||||
#define DMACCxConfiguration_E_Enable (1 << 0)
|
||||
|
||||
#define DMACCxControl_I_Mask (1 << 31)
|
||||
#define DMACCxControl_I_Disable (0 << 31)
|
||||
#define DMACCxControl_I_Enable (1 << 31)
|
||||
#define DMACCxControl_SI_Mask (1 << 26)
|
||||
#define DMACCxControl_SI_No_Increment (0 << 26)
|
||||
#define DMACCxControl_SI_Increment (1 << 26)
|
||||
#define DMACCxControl_DI_Mask (1 << 27)
|
||||
#define DMACCxControl_DI_No_Increment (0 << 27)
|
||||
#define DMACCxControl_DI_Increment (1 << 27)
|
||||
#define DMACCxControl_D_Master1 (0 << 25)
|
||||
#define DMACCxControl_S_Master1 (0 << 24)
|
||||
#define DMACCxControl_SWidth_Halfword (1 << 18)
|
||||
#define DMACCxControl_DWidth_Halfword (1 << 21)
|
||||
#define DMACCxControl_DBSize_1 (0 << 15)
|
||||
#define DMACCxControl_DBSize_4 (1 << 15)
|
||||
#define DMACCxControl_SBSize_1 (0 << 12)
|
||||
#define DMACCxControl_SBSize_4 (1 << 12)
|
||||
#define DMACCxControl_TransferSizeMask (0x0FFF)
|
||||
|
||||
#define DMACCxConfiguration_FlowCntrl_Memory_to_Memory_DMA (0 << 11)
|
||||
#define DMACCxConfiguration_FlowCntrl_Memory_to_Peripheral_DMA (1 << 11)
|
||||
#define DMACCxConfiguration_FlowCntrl_Peripheral_to_Memory_DMA (2 << 11)
|
||||
|
||||
|
||||
void handleAudioDeviceDMAInterrupt();
|
||||
|
||||
void handleAudioDeviceDMAInterruptError();
|
||||
|
||||
/*
|
||||
Get a free DMA channel
|
||||
*/
|
||||
uint32_t acquireDMAChannel(void);
|
||||
|
||||
/*
|
||||
Free a DMA channel
|
||||
*/
|
||||
void freeDMAChannel(uint32_t dmaChannel);
|
||||
|
||||
/*
|
||||
Make sure the pl080 clock is enabled and initialize DMA channels 0 and 1
|
||||
*/
|
||||
void configureDMA(uint32_t dmaChannel);
|
||||
|
||||
/*
|
||||
Startup DMA from address src to destination dst
|
||||
If chain is not NULL, will setup that in the LLI to auto-restart DMA from the chain
|
||||
*/
|
||||
void setupDMA(uint32_t dmaChannel, void *src, void *dst, DMALinkedListItem *chain, DMADirection direction, uint32_t peripheral, size_t bytesToTransfer);
|
||||
|
||||
/*
|
||||
Startup DMA from address src to destination dst
|
||||
If chain is not NULL, will setup that in the LLI to auto-restart DMA from the chain
|
||||
*/
|
||||
void startDMA(uint32_t dmaChannel);
|
||||
|
||||
/*
|
||||
Halts the channel, then blocks until the DMA is complete and then fully disables it
|
||||
*/
|
||||
void disableDMA(uint32_t dmaChannel);
|
||||
|
||||
/*
|
||||
Blocks until the DMA is complete and then fully disables it
|
||||
*/
|
||||
void disableDMAImmediate(uint32_t dmaChannel);
|
||||
|
||||
#endif /* __PL080DMAC_AE2__ */
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef __AE2_I2S__
|
||||
#define __AE2_I2S__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <platform/int.h>
|
||||
|
||||
#define rI2S_BASE 0x34190000
|
||||
#define rI2S0_BASE (rI2S_BASE )
|
||||
#define rI2S1_BASE (rI2S_BASE + 0x1000)
|
||||
#define rI2S2_BASE (rI2S_BASE + 0x2000)
|
||||
#define rI2S3_BASE (rI2S_BASE + 0x3000)
|
||||
|
||||
#define rI2SCLKCON 0
|
||||
#define rI2STXCON 0x4
|
||||
#define rI2STXCOM 0x8
|
||||
#define rI2STXDB 0x10
|
||||
#define rI2SRXCON 0x30
|
||||
#define rI2SRXCOM 0x34
|
||||
#define rI2SRXDB 0x38
|
||||
#define rI2SSTATUS 0x3C
|
||||
#define rI2SBITCLK 0x40
|
||||
#define rI2SVERSION 0x44
|
||||
|
||||
#define rI2S0_CLKCON (rI2S0_BASE + rI2SCLKCON )
|
||||
#define rI2S0_TXCON (rI2S0_BASE + rI2STXCON )
|
||||
#define rI2S0_TXCOM (rI2S0_BASE + rI2STXCOM )
|
||||
#define rI2S0_TXDB (rI2S0_BASE + rI2STXDB )
|
||||
#define rI2S0_RXCON (rI2S0_BASE + rI2SRXCON )
|
||||
#define rI2S0_RXCOM (rI2S0_BASE + rI2SRXCOM )
|
||||
#define rI2S0_RXDB (rI2S0_BASE + rI2SRXDB )
|
||||
#define rI2S0_STATUS (rI2S0_BASE + rI2SSTATUS )
|
||||
#define rI2S0_BITCLK (rI2S0_BASE + rI2SBITCLK )
|
||||
#define rI2S0_VERSION (rI2S0_BASE + rI2SVERSION)
|
||||
|
||||
#define rI2S1_CLKCON (rI2S1_BASE + rI2SCLKCON )
|
||||
#define rI2S1_TXCON (rI2S1_BASE + rI2STXCON )
|
||||
#define rI2S1_TXCOM (rI2S1_BASE + rI2STXCOM )
|
||||
#define rI2S1_TXDB (rI2S1_BASE + rI2STXDB )
|
||||
#define rI2S1_RXCON (rI2S1_BASE + rI2SRXCON )
|
||||
#define rI2S1_RXCOM (rI2S1_BASE + rI2SRXCOM )
|
||||
#define rI2S1_RXDB (rI2S1_BASE + rI2SRXDB )
|
||||
#define rI2S1_STATUS (rI2S1_BASE + rI2SSTATUS )
|
||||
#define rI2S1_BITCLK (rI2S1_BASE + rI2SBITCLK )
|
||||
#define rI2S1_VERSION (rI2S1_BASE + rI2SVERSION)
|
||||
|
||||
#define rI2S2_CLKCON (rI2S2_BASE + rI2SCLKCON )
|
||||
#define rI2S2_TXCON (rI2S2_BASE + rI2STXCON )
|
||||
#define rI2S2_TXCOM (rI2S2_BASE + rI2STXCOM )
|
||||
#define rI2S2_TXDB (rI2S2_BASE + rI2STXDB )
|
||||
#define rI2S2_RXCON (rI2S2_BASE + rI2SRXCON )
|
||||
#define rI2S2_RXCOM (rI2S2_BASE + rI2SRXCOM )
|
||||
#define rI2S2_RXDB (rI2S2_BASE + rI2SRXDB )
|
||||
#define rI2S2_STATUS (rI2S2_BASE + rI2SSTATUS )
|
||||
#define rI2S2_BITCLK (rI2S2_BASE + rI2SBITCLK )
|
||||
#define rI2S2_VERSION (rI2S2_BASE + rI2SVERSION)
|
||||
|
||||
#define rI2S3_CLKCON (rI2S3_BASE + rI2SCLKCON )
|
||||
#define rI2S3_TXCON (rI2S3_BASE + rI2STXCON )
|
||||
#define rI2S3_TXCOM (rI2S3_BASE + rI2STXCOM )
|
||||
#define rI2S3_TXDB (rI2S3_BASE + rI2STXDB )
|
||||
#define rI2S3_RXCON (rI2S3_BASE + rI2SRXCON )
|
||||
#define rI2S3_RXCOM (rI2S3_BASE + rI2SRXCOM )
|
||||
#define rI2S3_RXDB (rI2S3_BASE + rI2SRXDB )
|
||||
#define rI2S3_STATUS (rI2S3_BASE + rI2SSTATUS )
|
||||
#define rI2S3_BITCLK (rI2S3_BASE + rI2SBITCLK )
|
||||
#define rI2S3_VERSION (rI2S3_BASE + rI2SVERSION)
|
||||
|
||||
#define rI2SSTATUS_RXOVERRUN 20
|
||||
#define rI2SSTATUS_TXOVERRUN 19
|
||||
#define rI2SSTATUS_RXFIFOLVL 13
|
||||
#define rI2SSTATUS_TXFIFOLVL 7
|
||||
#define rI2SSTATUS_RXOVERRUN_MASK ( 1 << 20)
|
||||
#define rI2SSTATUS_TXOVERRUN_MASK ( 1 << 19)
|
||||
#define rI2SSTATUS_RXFIFOLVL_MASK (0x3F << 13)
|
||||
#define rI2SSTATUS_TXFIFOLVL_MASK (0x3F << 7 )
|
||||
|
||||
#endif /* __AE2_I2S__ */
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef __AE2_MCA__
|
||||
#define __AE2_MCA__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <platform/int.h>
|
||||
|
||||
#define rMCA_BASE 0x34196000
|
||||
#define rMCA0_BASE (rMCA_BASE )
|
||||
#define rMCA1_BASE (rMCA_BASE + 0x1000)
|
||||
|
||||
#define rMCAVERSION 0
|
||||
#define rMCAFIFOSIZE 0x4
|
||||
#define rMCAFIFOSHIFT 0x8
|
||||
#define rMCACFG 0xC
|
||||
#define rMCAUNSCFG 0x10
|
||||
#define rMCAIDLE 0x14
|
||||
#define rMCATXCFG 0x18
|
||||
#define rMCAUNSTXCFG 0x1C
|
||||
#define rMCATXFIFOCFG 0x20
|
||||
#define rMCATXMASK 0x24
|
||||
#define rMCATXDATA 0x28
|
||||
#define rMCARXCFG 0x2C
|
||||
#define rMCAUNSRXCFG 0x30
|
||||
#define rMCARXFIFOCFG 0x34
|
||||
#define rMCARXMASK 0x38
|
||||
#define rMCARXDATA 0x3C
|
||||
#define rMCASTATUS 0x40
|
||||
#define rMCACTL 0x44
|
||||
|
||||
#define rMCA0_VERSION ( rMCA0_BASE + rMCAVERSION )
|
||||
#define rMCA0_FIFOSHIFT ( rMCA0_BASE + rMCAFIFOSHIFT)
|
||||
#define rMCA0_CFG ( rMCA0_BASE + rMCACFG )
|
||||
#define rMCA0_UNSCFG ( rMCA0_BASE + rMCAUNSCFG )
|
||||
#define rMCA0_IDLE ( rMCA0_BASE + rMCAIDLE )
|
||||
#define rMCA0_TXCFG ( rMCA0_BASE + rMCATXCFG )
|
||||
#define rMCA0_UNSTXCFG ( rMCA0_BASE + rMCAUNSTXCFG )
|
||||
#define rMCA0_TXFIFOCFG ( rMCA0_BASE + rMCATXFIFOCFG)
|
||||
#define rMCA0_TXMASK ( rMCA0_BASE + rMCATXMASK )
|
||||
#define rMCA0_TXDATA ( rMCA0_BASE + rMCATXDATA )
|
||||
#define rMCA0_RXCFG ( rMCA0_BASE + rMCARXCFG )
|
||||
#define rMCA0_UNSRXCFG ( rMCA0_BASE + rMCAUNSRXCFG )
|
||||
#define rMCA0_RXFIFOCFG ( rMCA0_BASE + rMCARXFIFOCFG)
|
||||
#define rMCA0_RXMASK ( rMCA0_BASE + rMCARXMASK )
|
||||
#define rMCA0_RXDATA ( rMCA0_BASE + rMCARXDATA )
|
||||
#define rMCA0_STATUS ( rMCA0_BASE + rMCASTATUS )
|
||||
#define rMCA0_CTL ( rMCA0_BASE + rMCACTL )
|
||||
|
||||
#define rMCA1_VERSION ( rMCA1_BASE + rMCAVERSION )
|
||||
#define rMCA1_FIFOSHIFT ( rMCA1_BASE + rMCAFIFOSHIFT)
|
||||
#define rMCA1_CFG ( rMCA1_BASE + rMCACFG )
|
||||
#define rMCA1_UNSCFG ( rMCA1_BASE + rMCAUNSCFG )
|
||||
#define rMCA1_IDLE ( rMCA1_BASE + rMCAIDLE )
|
||||
#define rMCA1_TXCFG ( rMCA1_BASE + rMCATXCFG )
|
||||
#define rMCA1_UNSTXCFG ( rMCA1_BASE + rMCAUNSTXCFG )
|
||||
#define rMCA1_TXFIFOCFG ( rMCA1_BASE + rMCATXFIFOCFG)
|
||||
#define rMCA1_TXMASK ( rMCA1_BASE + rMCATXMASK )
|
||||
#define rMCA1_TXDATA ( rMCA1_BASE + rMCATXDATA )
|
||||
#define rMCA1_RXCFG ( rMCA1_BASE + rMCARXCFG )
|
||||
#define rMCA1_UNSRXCFG ( rMCA1_BASE + rMCAUNSRXCFG )
|
||||
#define rMCA1_RXFIFOCFG ( rMCA1_BASE + rMCARXFIFOCFG)
|
||||
#define rMCA1_RXMASK ( rMCA1_BASE + rMCARXMASK )
|
||||
#define rMCA1_RXDATA ( rMCA1_BASE + rMCARXDATA )
|
||||
#define rMCA1_STATUS ( rMCA1_BASE + rMCASTATUS )
|
||||
#define rMCA1_CTL ( rMCA1_BASE + rMCACTL )
|
||||
|
||||
#define rMCASTATUS_FRAMEERROR 31
|
||||
#define rMCASTATUS_RXOVERRUN 29
|
||||
#define rMCASTATUS_RXUNDERRUN 28
|
||||
#define rMCASTATUS_RXHIGHWATER 27
|
||||
#define rMCASTATUS_RXLOWWATER 26
|
||||
#define rMCASTATUS_TXOVERRUN 13
|
||||
#define rMCASTATUS_TXUNDERRUN 12
|
||||
#define rMCASTATUS_TXHIGHWATER 11
|
||||
#define rMCASTATUS_TXLOWWATER 10
|
||||
#define rMCASTATUS_RXFIFOLVL 16
|
||||
#define rMCASTATUS_TXFIFOLVL 0
|
||||
#define rMCASTATUS_FRAMEEEROR_MASK ( 1 << rMCASTATUS_FRAMEERROR)
|
||||
#define rMCASTATUS_RXOVERRUN_MASK ( 1 << rMCASTATUS_RXOVERRUN)
|
||||
#define rMCASTATUS_RXUNDERRUN_MASK ( 1 << rMCASTATUS_RXUNDERRUN)
|
||||
#define rMCASTATUS_TXOVERRUN_MASK ( 1 << rMCASTATUS_TXOVERRUN)
|
||||
#define rMCASTATUS_TXUNDERRUN_MASK ( 1 << rMCASTATUS_TXUNDERRUN)
|
||||
#define rMCASTATUS_RXFIFOLVL_MASK (0x1FF << rMCASTATUS_RXFIFOLVL)
|
||||
#define rMCASTATUS_TXFIFOLVL_MASK (0x1FF << rMCASTATUS_TXFIFOLVL)
|
||||
|
||||
#define rMCAUNSRXCFG_IRQ_EN 1
|
||||
|
||||
#define rMCAUNSTXCFG_IRQ_EN 1
|
||||
|
||||
#endif /* __AE2_MCA__ */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* AudioUnit_AE2.h
|
||||
* CAServices-Aspen
|
||||
*
|
||||
* Created by richardp on 11/28/11.
|
||||
* Copyright 2011 Apple, Inc. All rights reserved.
|
||||
*
|
||||
* Serves as the base class for AudioUnits on AE2
|
||||
*/
|
||||
|
||||
#ifndef __AudioUnit_AE2_h__
|
||||
#define __AudioUnit_AE2_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "AudioUnitProperties_AE2.h"
|
||||
|
||||
/**
|
||||
* AUBase_AE2 is the wrapper to go from AE2 land to CoreAudio land.
|
||||
* For instance, we assume input channels are 16-bit, whereas CoreAudio
|
||||
* AudioUnit may require floats. These modules do the translation.
|
||||
*
|
||||
* This is not intended to be parity with AudioUnits, but a crippled
|
||||
* version.
|
||||
*/
|
||||
|
||||
class AUBase_AE2
|
||||
{
|
||||
public:
|
||||
AUBase_AE2() {}
|
||||
virtual ~AUBase_AE2() {}
|
||||
|
||||
virtual OSStatus Initialize() { return noErr; }
|
||||
|
||||
virtual void Process(const void *inSourceP,
|
||||
void *inDestP,
|
||||
uint32_t inFramesToProcess) {}
|
||||
|
||||
// for parameters
|
||||
virtual OSStatus SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value) { return kAudioUnitErr_InvalidParameter; }
|
||||
virtual AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID) { return 0; }
|
||||
|
||||
// for properties
|
||||
virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable) { return kAudioUnitErr_InvalidProperty; }
|
||||
virtual OSStatus GetProperty(AudioUnitPropertyID inID, void* outData) { return kAudioUnitErr_InvalidProperty; }
|
||||
virtual OSStatus SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize) { return kAudioUnitErr_InvalidProperty; }
|
||||
};
|
||||
|
||||
#endif // __AudioUnit_AE2_h__
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* AUComponent_AE2.h
|
||||
* Stripped down version of AUComponent just for what AE2 needs
|
||||
*
|
||||
* Copyright 2010 Apple Computer. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AUCOMPONENT_AE2__
|
||||
#define __AUCOMPONENT_AE2__
|
||||
|
||||
/*!
|
||||
@enum Audio unit errors
|
||||
@discussion These are the various errors that can be returned by AudioUnit... API calls
|
||||
|
||||
@constant kAudioUnitErr_InvalidProperty
|
||||
The property is not supported
|
||||
@constant kAudioUnitErr_InvalidParameter
|
||||
The parameter is not supported
|
||||
@constant kAudioUnitErr_InvalidElement
|
||||
The specified element is not valid
|
||||
@constant kAudioUnitErr_NoConnection
|
||||
There is no connection (generally an audio unit is asked to render but it has
|
||||
not input from which to gather data)
|
||||
@constant kAudioUnitErr_FailedInitialization
|
||||
The audio unit is unable to be initialised
|
||||
@constant kAudioUnitErr_TooManyFramesToProcess
|
||||
When an audio unit is initialised it has a value which specifies the max
|
||||
number of frames it will be asked to render at any given time. If an audio
|
||||
unit is asked to render more than this, this error is returned.
|
||||
@constant kAudioUnitErr_InvalidFile
|
||||
If an audio unit uses external files as a data source, this error is returned
|
||||
if a file is invalid (Apple's DLS synth returns this error)
|
||||
@constant kAudioUnitErr_FormatNotSupported
|
||||
Returned if an input or output format is not supported
|
||||
@constant kAudioUnitErr_Uninitialized
|
||||
Returned if an operation requires an audio unit to be initialised and it is
|
||||
not.
|
||||
@constant kAudioUnitErr_InvalidScope
|
||||
The specified scope is invalid
|
||||
@constant kAudioUnitErr_PropertyNotWritable
|
||||
The property cannot be written
|
||||
@constant kAudioUnitErr_CannotDoInCurrentContext
|
||||
Returned when an audio unit is in a state where it can't perform the requested
|
||||
action now - but it could later. Its usually used to guard a render operation
|
||||
when a reconfiguration of its internal state is being performed.
|
||||
@constant kAudioUnitErr_InvalidPropertyValue
|
||||
The property is valid, but the value of the property being provided is not
|
||||
@constant kAudioUnitErr_PropertyNotInUse
|
||||
Returned when a property is valid, but it hasn't been set to a valid value at
|
||||
this time.
|
||||
@constant kAudioUnitErr_Initialized
|
||||
Indicates the operation cannot be performed because the audio unit is
|
||||
initialized.
|
||||
@constant kAudioUnitErr_InvalidOfflineRender
|
||||
Used to indicate that the offline render operation is invalid. For instance,
|
||||
when the audio unit needs to be pre-flighted,
|
||||
but it hasn't been.
|
||||
@constant kAudioUnitErr_Unauthorized
|
||||
Returned by either Open or Initialise, this error is used to indicate that the
|
||||
audio unit is not authorised, that it cannot be used. A host can then present
|
||||
a UI to notify the user the audio unit is not able to be used in its current
|
||||
state.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kAudioUnitErr_InvalidProperty = -10879,
|
||||
kAudioUnitErr_InvalidParameter = -10878,
|
||||
kAudioUnitErr_InvalidElement = -10877,
|
||||
kAudioUnitErr_NoConnection = -10876,
|
||||
kAudioUnitErr_FailedInitialization = -10875,
|
||||
kAudioUnitErr_TooManyFramesToProcess = -10874,
|
||||
kAudioUnitErr_InvalidFile = -10871,
|
||||
kAudioUnitErr_FormatNotSupported = -10868,
|
||||
kAudioUnitErr_Uninitialized = -10867,
|
||||
kAudioUnitErr_InvalidScope = -10866,
|
||||
kAudioUnitErr_PropertyNotWritable = -10865,
|
||||
kAudioUnitErr_CannotDoInCurrentContext = -10863,
|
||||
kAudioUnitErr_InvalidPropertyValue = -10851,
|
||||
kAudioUnitErr_PropertyNotInUse = -10850,
|
||||
kAudioUnitErr_Initialized = -10849,
|
||||
kAudioUnitErr_InvalidOfflineRender = -10848,
|
||||
kAudioUnitErr_Unauthorized = -10847
|
||||
};
|
||||
|
||||
/*!
|
||||
@typedef AudioUnitPropertyID
|
||||
@discussion Type used for audio unit properties.
|
||||
Properties are used to describe the state of an audio unit (for instance,
|
||||
the input or output audio format)
|
||||
*/
|
||||
typedef UInt32 AudioUnitPropertyID;
|
||||
/*!
|
||||
@typedef AudioUnitScope
|
||||
@discussion Type used for audio unit scopes. Apple reserves the 0 < 1024 range for
|
||||
audio unit scope identifiers.
|
||||
Scopes are used to delineate a major attribute of an audio unit
|
||||
(for instance, global, input, output)
|
||||
*/
|
||||
typedef UInt32 AudioUnitScope;
|
||||
/*!
|
||||
@typedef AudioUnitElement
|
||||
@discussion Type used for audio unit elements.
|
||||
Scopes can have one or more member, and a member of a scope is
|
||||
addressed / described by its element
|
||||
For instance, input bus 1 is input scope, element 1
|
||||
*/
|
||||
typedef UInt32 AudioUnitElement;
|
||||
/*!
|
||||
@typedef AudioUnitParameterID
|
||||
@discussion Type used for audio unit parameters.
|
||||
Parameters are typically used to control and set render state
|
||||
(for instance, filter cut-off frequency)
|
||||
*/
|
||||
typedef UInt32 AudioUnitParameterID;
|
||||
/*!
|
||||
@typedef AudioUnitParameterValue
|
||||
@discussion Type used for audio unit parameter values.
|
||||
The value of a given parameter is specified using this type
|
||||
(typically a Float32)
|
||||
*/
|
||||
typedef Float32 AudioUnitParameterValue;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Render flags
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
/* these are obsolete, were never implemented: */
|
||||
/* kAudioUnitRenderAction_Accumulate = (1 << 0) */
|
||||
/* kAudioUnitRenderAction_UseProvidedBuffer = (1 << 1) */
|
||||
kAudioUnitRenderAction_PreRender = (1 << 2),
|
||||
kAudioUnitRenderAction_PostRender = (1 << 3),
|
||||
kAudioUnitRenderAction_OutputIsSilence = (1 << 4),
|
||||
/* Provides hint on return from Render(). if set, the buffer contains all zeroes */
|
||||
kAudioOfflineUnitRenderAction_Preflight = (1 << 5),
|
||||
kAudioOfflineUnitRenderAction_Render = (1 << 6),
|
||||
kAudioOfflineUnitRenderAction_Complete = (1 << 7),
|
||||
// this flag is set if on the post-render call an error was returned by the AUs render
|
||||
// in this case, the error can be retrieved through the lastRenderError property
|
||||
// and the ioData handed to the post-render notification will be invalid.
|
||||
kAudioUnitRenderAction_PostRenderError = (1 << 8)
|
||||
};
|
||||
|
||||
typedef UInt32 AudioUnitRenderActionFlags;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include "AUNull.h"
|
||||
#include "string.h"
|
||||
#include <debug.h>
|
||||
|
||||
#define GENERATE_SAWTOOTH 0
|
||||
|
||||
AUNull *
|
||||
AUNull::Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
|
||||
{
|
||||
AUNull *This = new AUNull;
|
||||
if (This)
|
||||
{
|
||||
if (This->InitWith(sampleRate, numChannels, sampleSize))
|
||||
{
|
||||
This->Initialize();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete This;
|
||||
This = NULL;
|
||||
}
|
||||
}
|
||||
return This;
|
||||
}
|
||||
|
||||
AUNull::AUNull()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
AUNull::InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
|
||||
{
|
||||
mSampleRate = sampleRate;
|
||||
mNumChannels = numChannels;
|
||||
mSampleSize = sampleSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
AUNull::~AUNull()
|
||||
{
|
||||
}
|
||||
|
||||
#if GENERATE_SAWTOOTH
|
||||
static int16_t currentValue = 0;
|
||||
static const uint32_t kFreq = 600;
|
||||
#endif
|
||||
|
||||
void
|
||||
AUNull::Process(const void *inSourceP,
|
||||
void *inDestP,
|
||||
UInt32 inFramesToProcess)
|
||||
{
|
||||
#if GENERATE_SAWTOOTH
|
||||
uint16_t amountToAdd = (0x10000L * kFreq) / mSampleRate;
|
||||
int16_t *dst = static_cast<int16_t*>(inDestP);
|
||||
size_t i = 0;
|
||||
for (i = 0; i < (inFramesToProcess * mNumChannels); ++i)
|
||||
{
|
||||
*dst++ = currentValue;
|
||||
currentValue += amountToAdd;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* AUNull.h
|
||||
*
|
||||
* Created by richardp on 11/28/11.
|
||||
* Copyright 2011 Apple, Inc. All rights reserved.
|
||||
*
|
||||
* Does necessary translation between AE2 and CA AU speaker protection
|
||||
*/
|
||||
|
||||
#ifndef __AUNull_h__
|
||||
#define __AUNull_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "AudioUnitProperties_AE2.h"
|
||||
#include "AUBase_AE2.h"
|
||||
|
||||
class IIRFilter;
|
||||
|
||||
/**
|
||||
* AUNull just passes input to output
|
||||
* Assumes audio data is 16-bit stereo
|
||||
*/
|
||||
class AUNull : public AUBase_AE2
|
||||
{
|
||||
public:
|
||||
static AUNull* Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
|
||||
virtual ~AUNull();
|
||||
|
||||
virtual void Process(const void *inSourceP,
|
||||
void *inDestP,
|
||||
UInt32 inFramesToProcess);
|
||||
|
||||
private:
|
||||
AUNull();
|
||||
bool InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
|
||||
|
||||
// some constants
|
||||
typedef AUBase_AE2 super;
|
||||
size_t mSampleRate;
|
||||
size_t mNumChannels;
|
||||
size_t mSampleSize;
|
||||
};
|
||||
|
||||
#endif // __AUNull_h__
|
||||
|
||||
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#if USE_SIDETONE
|
||||
#include "AUSidetone.h"
|
||||
#include "IIRFiltersA5.h"
|
||||
#include "VolumeA5.h"
|
||||
#include "string.h"
|
||||
#include <debug.h>
|
||||
|
||||
|
||||
// a basic fixed 16-bit mono 44.1 description
|
||||
static AudioStreamBasicDescription kDefaultMonoFormat = { 44100.0, 'lpcm', 0xC, 2, 1, 2, 1, 16, 0 };
|
||||
|
||||
static void SetDefaultSidetoneEQ(BiquadDescriptor *biquadValues, size_t numBands, uint32_t sampleRate)
|
||||
{
|
||||
size_t i = 0;
|
||||
if (i < numBands)
|
||||
{
|
||||
biquadValues[i].filterType = kAUNBandEQFilterType_2ndOrderButterworthLowPass;
|
||||
biquadValues[i].frequencyInHertz = (sampleRate == 8000) ? 3800.0f : 7400.0f;
|
||||
biquadValues[i].gain = 0.f;
|
||||
biquadValues[i].bw = 0.05;
|
||||
biquadValues[i].bypassBand = 0.f;
|
||||
++i;
|
||||
}
|
||||
if (i < numBands)
|
||||
{
|
||||
biquadValues[i].filterType = kAUNBandEQFilterType_2ndOrderButterworthHighPass;
|
||||
biquadValues[i].frequencyInHertz = (sampleRate == 8000) ? 200.0f : 100.0f;
|
||||
biquadValues[i].gain = 0.f;
|
||||
biquadValues[i].bw = 0.05;
|
||||
biquadValues[i].bypassBand = 0.f;
|
||||
++i;
|
||||
}
|
||||
|
||||
for (; i < numBands; ++i)
|
||||
{
|
||||
biquadValues[i].filterType = 0;
|
||||
biquadValues[i].frequencyInHertz = 100.0f;
|
||||
biquadValues[i].gain = 0.f;
|
||||
biquadValues[i].bw = 0.05;
|
||||
biquadValues[i].bypassBand = 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AUSidetone *
|
||||
AUSidetone::Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
|
||||
{
|
||||
AUSidetone *This = new AUSidetone;
|
||||
if (This)
|
||||
{
|
||||
if (This->InitWith(sampleRate, numChannels, sampleSize))
|
||||
{
|
||||
This->Initialize();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete This;
|
||||
This = NULL;
|
||||
}
|
||||
}
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
AUSidetone::AUSidetone() :
|
||||
mSampleRate(44100),
|
||||
mNumChannels(1),
|
||||
mSidetoneEQ(NULL),
|
||||
mVolume(NULL),
|
||||
mNumberBands(kMaxNumBands),
|
||||
mBiquadValues(NULL),
|
||||
mVolumeInDB(kDefaultSidetoneGain),
|
||||
mSidetoneEQBypass(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
AUSidetone::InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
|
||||
{
|
||||
if (sampleSize != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mSampleRate = sampleRate;
|
||||
mNumChannels = numChannels;
|
||||
|
||||
AudioStreamBasicDescription thisFormat = kDefaultMonoFormat;
|
||||
thisFormat.mSampleRate = mSampleRate;
|
||||
thisFormat.mBytesPerPacket *= mNumChannels;
|
||||
thisFormat.mBytesPerFrame *= mNumChannels;
|
||||
thisFormat.mChannelsPerFrame *= mNumChannels;
|
||||
thisFormat.mBitsPerChannel *= mNumChannels;
|
||||
|
||||
mSidetoneEQ = NewAE2IIRFilter(&thisFormat, &thisFormat, kMaxNumBands);
|
||||
dprintf(DEBUG_INFO, "sidetone eq %p\n", mSidetoneEQ);
|
||||
mBiquadValues = new BiquadDescriptor[mNumberBands];
|
||||
if (mBiquadValues)
|
||||
{
|
||||
SetDefaultSidetoneEQ(mBiquadValues, mNumberBands, mSampleRate);
|
||||
if (mSidetoneEQ)
|
||||
{
|
||||
mVolume = NewVolume(thisFormat);
|
||||
dprintf(DEBUG_INFO, "volume %p\n", mVolume);
|
||||
if (mVolume)
|
||||
{
|
||||
if ((SetAE2IIRFilter(mSidetoneEQ, &thisFormat, mNumberBands, mBiquadValues) == noErr) &&
|
||||
(SetGain(mVolume, mVolumeInDB) == noErr))
|
||||
{
|
||||
dprintf(DEBUG_INFO, "it is all good\n");
|
||||
return true;
|
||||
}
|
||||
DeleteVolume(mVolume);
|
||||
mVolume = NULL;
|
||||
}
|
||||
DeleteAE2IIRFilter(mSidetoneEQ);
|
||||
mSidetoneEQ = NULL;
|
||||
}
|
||||
delete [] mBiquadValues;
|
||||
mBiquadValues = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
AUSidetone::~AUSidetone()
|
||||
{
|
||||
DeleteAE2IIRFilter(mSidetoneEQ);
|
||||
mSidetoneEQ = NULL;
|
||||
DeleteVolume(mVolume);
|
||||
mVolume = NULL;
|
||||
delete [] mBiquadValues;
|
||||
mBiquadValues = NULL;
|
||||
}
|
||||
|
||||
|
||||
// These conversion routines are for translating from UInt32 of the AP/AE2 bridge
|
||||
// to the floating point of the internal representation. Scaling and offsets applied here.
|
||||
Float32 AudioUnitParameterValue_To_BypassBand(AudioUnitParameterValue value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value == 0) ? 0.f : 1.f));
|
||||
return (value == 0) ? 0.f : 1.f;
|
||||
}
|
||||
|
||||
|
||||
AudioUnitParameterValue BypassBand_To_AudioUnitParameterValue(Float32 value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value == 0.f) ? 0 : 1));
|
||||
return (value == 0.f) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
UInt32 AudioUnitParameterValue_To_FilterType(AudioUnitParameterValue value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
AudioUnitParameterValue FilterType_To_AudioUnitParameterValue(UInt32 value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
Float32 AudioUnitParameterValue_To_Frequency(AudioUnitParameterValue value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
AudioUnitParameterValue Frequency_To_AudioUnitParameterValue(Float32 value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
Float32 AudioUnitParameterValue_To_Gain(AudioUnitParameterValue value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(-96.0 + (value)));
|
||||
return -96.0 + (value);
|
||||
}
|
||||
|
||||
|
||||
AudioUnitParameterValue Gain_To_AudioUnitParameterValue(Float32 value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value) - (-96.0)));
|
||||
return (value) - (-96.0);
|
||||
}
|
||||
|
||||
|
||||
Float32 AudioUnitParameterValue_To_Bandwidth(AudioUnitParameterValue value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value / 100.0f));
|
||||
return value / 100.0f;
|
||||
}
|
||||
|
||||
|
||||
AudioUnitParameterValue Bandwidth_To_AudioUnitParameterValue(Float32 value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value * 100.0f));
|
||||
return value * 100.0f;
|
||||
}
|
||||
|
||||
|
||||
Float32 AudioUnitParameterValue_To_VolumeInDB(AudioUnitParameterValue value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(-96.0 + (value)));
|
||||
return -96.0 + (value);
|
||||
}
|
||||
|
||||
|
||||
AudioUnitParameterValue VolumeInDB_To_AudioUnitParameterValue(Float32 value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value) - (-96.0)));
|
||||
return (value) - (-96.0);
|
||||
}
|
||||
|
||||
|
||||
OSStatus
|
||||
AUSidetone::SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "SetParameter %d value %d\n", paramID, uint32_t(value));
|
||||
if (paramID == kAUVolume_InDB)
|
||||
{
|
||||
mVolumeInDB = AudioUnitParameterValue_To_VolumeInDB(value);
|
||||
return SetGain(mVolume, mVolumeInDB);
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription thisFormat = kDefaultMonoFormat;
|
||||
thisFormat.mSampleRate = mSampleRate;
|
||||
thisFormat.mBytesPerPacket *= mNumChannels;
|
||||
thisFormat.mBytesPerFrame *= mNumChannels;
|
||||
thisFormat.mChannelsPerFrame *= mNumChannels;
|
||||
thisFormat.mBitsPerChannel *= mNumChannels;
|
||||
|
||||
// get the index
|
||||
uint32_t which = paramID % 1000;
|
||||
uint32_t type = (paramID / 1000)*1000;
|
||||
bool success = false;
|
||||
if (which < mNumberBands)
|
||||
{
|
||||
success = true;
|
||||
switch (type)
|
||||
{
|
||||
case kAUNBandEQParam_BypassBand:
|
||||
mBiquadValues[which].bypassBand = AudioUnitParameterValue_To_BypassBand(value);
|
||||
break;
|
||||
case kAUNBandEQParam_FilterType:
|
||||
mBiquadValues[which].filterType = AudioUnitParameterValue_To_FilterType(value);
|
||||
break;
|
||||
case kAUNBandEQParam_Frequency:
|
||||
mBiquadValues[which].frequencyInHertz = AudioUnitParameterValue_To_Frequency(value);
|
||||
break;
|
||||
case kAUNBandEQParam_Gain:
|
||||
mBiquadValues[which].gain = AudioUnitParameterValue_To_Gain(value);
|
||||
break;
|
||||
case kAUNBandEQParam_Bandwidth:
|
||||
mBiquadValues[which].bw = AudioUnitParameterValue_To_Bandwidth(value);
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
OSStatus result = 0;
|
||||
if (success)
|
||||
{
|
||||
result = SetAE2IIRFilter(mSidetoneEQ, &thisFormat, mNumberBands, mBiquadValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = super::SetParameter(paramID, value);
|
||||
}
|
||||
dprintf(DEBUG_INFO, "result is %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AudioUnitParameterValue
|
||||
AUSidetone::GetParameter(AudioUnitParameterID paramID)
|
||||
{
|
||||
if (paramID == kAUVolume_InDB)
|
||||
{
|
||||
return VolumeInDB_To_AudioUnitParameterValue(mVolumeInDB);
|
||||
}
|
||||
// get the index
|
||||
uint32_t which = paramID % 1000;
|
||||
uint32_t type = (paramID / 1000)*1000;
|
||||
if (which < mNumberBands)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case kAUNBandEQParam_BypassBand:
|
||||
return BypassBand_To_AudioUnitParameterValue(mBiquadValues[which].bypassBand);
|
||||
case kAUNBandEQParam_FilterType:
|
||||
return FilterType_To_AudioUnitParameterValue(mBiquadValues[which].filterType);
|
||||
case kAUNBandEQParam_Frequency:
|
||||
return Frequency_To_AudioUnitParameterValue(mBiquadValues[which].frequencyInHertz);
|
||||
case kAUNBandEQParam_Gain:
|
||||
return Gain_To_AudioUnitParameterValue(mBiquadValues[which].gain);
|
||||
case kAUNBandEQParam_Bandwidth:
|
||||
return Bandwidth_To_AudioUnitParameterValue(mBiquadValues[which].bw);
|
||||
}
|
||||
}
|
||||
return super::GetParameter(paramID);
|
||||
}
|
||||
|
||||
|
||||
OSStatus
|
||||
AUSidetone::GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable)
|
||||
{
|
||||
OSStatus result = -1;
|
||||
switch (inID)
|
||||
{
|
||||
case kAUSongbird_SidetoneState:
|
||||
result = GetPropertyInfo(kAUSongbird_SidetoneEQBlockData, outDataSize, outWritable);
|
||||
if (noErr == result)
|
||||
{
|
||||
// leave room for the mVolume field of the AU_SidetoneState;
|
||||
outDataSize += offsetof(AU_SidetoneState, mFilterDescription);
|
||||
}
|
||||
break;
|
||||
case kAUSongbird_SidetoneEQBlockData:
|
||||
outDataSize = sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter) + mNumberBands * sizeof(AU_BiquadFilter);
|
||||
outWritable = true;
|
||||
result = noErr;
|
||||
break;
|
||||
default:
|
||||
result = super::GetPropertyInfo(inID, outDataSize, outWritable);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
OSStatus
|
||||
AUSidetone::GetProperty(AudioUnitPropertyID inID, void* outData)
|
||||
{
|
||||
OSStatus result = -1;
|
||||
switch (inID)
|
||||
{
|
||||
case kAUSongbird_SidetoneState:
|
||||
{
|
||||
AU_SidetoneState * state = static_cast<AU_SidetoneState*>(outData);
|
||||
state->mVolume = mVolumeInDB;
|
||||
state->mBypass = mSidetoneEQBypass;
|
||||
void * biquad_filter_description = &state->mFilterDescription;
|
||||
result = GetProperty(kAUSongbird_SidetoneEQBlockData, biquad_filter_description);
|
||||
}
|
||||
break;
|
||||
case kAUSongbird_SidetoneEQBlockData:
|
||||
{
|
||||
AU_BiquadFilterDescription * description = static_cast<AU_BiquadFilterDescription*>(outData);
|
||||
if (description)
|
||||
{
|
||||
description->mBypass = mSidetoneEQBypass;
|
||||
description->mNumberFilters = mNumberBands;
|
||||
BiquadCoefficientsDescriptor * filter = static_cast<BiquadCoefficientsDescriptor*>(static_cast<void*>(description->mFilters));
|
||||
if (filter)
|
||||
{
|
||||
result = GetAE2IIRFilterCoefficients(mSidetoneEQ, mNumberBands, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = super::GetProperty(inID, outData);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
OSStatus
|
||||
AUSidetone::SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize)
|
||||
{
|
||||
OSStatus result = -1;
|
||||
switch (inID)
|
||||
{
|
||||
case kAUSongbird_SidetoneState:
|
||||
{
|
||||
// determine if have a valid data structure
|
||||
if (inDataSize < (offsetof(AU_SidetoneState, mFilterDescription)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
const AU_SidetoneState * state = static_cast<const AU_SidetoneState*>(inData);
|
||||
const void * biquad_filter_description = &state->mFilterDescription;
|
||||
result = SetProperty(kAUSongbird_SidetoneEQBlockData, biquad_filter_description, inDataSize - offsetof(AU_SidetoneState, mFilterDescription));
|
||||
if (noErr == result)
|
||||
{
|
||||
mSidetoneEQBypass = state->mBypass;
|
||||
mVolumeInDB = state->mVolume;
|
||||
result = SetGain(mVolume, mVolumeInDB);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kAUSongbird_SidetoneEQBlockData:
|
||||
{
|
||||
// determine if we are talking to a valid structure
|
||||
if (inDataSize < sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter))
|
||||
{
|
||||
break;
|
||||
}
|
||||
const AU_BiquadFilterDescription * description = static_cast<const AU_BiquadFilterDescription*>(inData);
|
||||
if (inDataSize < ((sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter) + (sizeof(AU_BiquadFilter) * description->mNumberFilters))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (description->mNumberFilters > kMaxNumBands)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const BiquadCoefficientsDescriptor * filter = static_cast<const BiquadCoefficientsDescriptor*>(static_cast<const void*>(description->mFilters));
|
||||
result = SetAE2IIRFilterCoefficients(mSidetoneEQ, description->mNumberFilters, static_cast<const BiquadCoefficientsDescriptor*>(filter));
|
||||
if (!result)
|
||||
{
|
||||
mSidetoneEQBypass = description->mBypass;
|
||||
mNumberBands = description->mNumberFilters;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = super::SetProperty(inID, inData, inDataSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AUSidetone::Process(const void *inSourceP,
|
||||
void *inDestP,
|
||||
UInt32 inFramesToProcess)
|
||||
{
|
||||
if (!mSidetoneEQBypass)
|
||||
{
|
||||
ProcessAE2IIRFilter(mSidetoneEQ, inFramesToProcess, inSourceP, inDestP);
|
||||
}
|
||||
ProcessVolumeInplace(mVolume, inFramesToProcess, inDestP);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* AUSidetone.h
|
||||
*
|
||||
* Created by richardp on 11/28/11.
|
||||
* Copyright 2011 Apple, Inc. All rights reserved.
|
||||
*
|
||||
* Does necessary translation between AE2 and CA AU speaker protection
|
||||
*/
|
||||
|
||||
#ifndef __AUSidetone_h__
|
||||
#define __AUSidetone_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "AudioUnitProperties_AE2.h"
|
||||
#include "AUBase_AE2.h"
|
||||
|
||||
struct AE2_IIRFilter;
|
||||
struct AE2_Volume;
|
||||
struct BiquadDescriptor;
|
||||
|
||||
/**
|
||||
* AUSidetone implements the sidetone eq and gain.
|
||||
*
|
||||
* These values are hard-coded for now.
|
||||
*/
|
||||
class AUSidetone : public AUBase_AE2
|
||||
{
|
||||
public:
|
||||
static AUSidetone* Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
|
||||
virtual ~AUSidetone();
|
||||
|
||||
virtual void Process(const void *inSourceP,
|
||||
void *inDestP,
|
||||
UInt32 inFramesToProcess);
|
||||
|
||||
// for parameters
|
||||
virtual OSStatus SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value);
|
||||
virtual AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID);
|
||||
// for properties
|
||||
virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable);
|
||||
virtual OSStatus GetProperty(AudioUnitPropertyID inID, void* outData);
|
||||
virtual OSStatus SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize);
|
||||
|
||||
private:
|
||||
AUSidetone();
|
||||
bool InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
|
||||
|
||||
// some constants
|
||||
typedef AUBase_AE2 super;
|
||||
static const size_t kMaxNumBands = 5;
|
||||
static const float kDefaultSidetoneGain = -32.0;
|
||||
|
||||
// Parameters for the AUNBandEQ unit
|
||||
// Note that the parameter IDs listed correspond to band 0 (zero) of the unit. The parameter IDs for
|
||||
// higher bands can be obtained by adding the zero-indexed band number to the corresponding band 0
|
||||
// parameter ID up to the number of bands minus one, where the number of bands is described by the
|
||||
// AUNBandEQ property kAUNBandEQProperty_NumberOfBands. For example, the parameter ID corresponding
|
||||
// to the filter type of band 4 would be kAUNBandEQParam_FilterType + 3.
|
||||
enum {
|
||||
kAUVolume_InDB = 500,
|
||||
// Global, Boolean, 0 or 1, 1
|
||||
kAUNBandEQParam_BypassBand = 1000,
|
||||
// Global, Indexed, 0->kNumAUNBandEQFilterTypes-1, 0
|
||||
kAUNBandEQParam_FilterType = 2000,
|
||||
// Global, Hz, 20->(SampleRate/2), 1000
|
||||
kAUNBandEQParam_Frequency = 3000,
|
||||
// Global, dB, -96->24, 0
|
||||
kAUNBandEQParam_Gain = 4000,
|
||||
// Global, octaves, 0.05->5.0, 0.5
|
||||
kAUNBandEQParam_Bandwidth = 5000
|
||||
};
|
||||
|
||||
uint32_t mSampleRate;
|
||||
uint32_t mNumChannels;
|
||||
AE2_IIRFilter *mSidetoneEQ;
|
||||
AE2_Volume *mVolume;
|
||||
// to hold the coefficients
|
||||
uint32_t mNumberBands;
|
||||
|
||||
BiquadDescriptor *mBiquadValues;
|
||||
|
||||
float mVolumeInDB;
|
||||
bool mSidetoneEQBypass;
|
||||
};
|
||||
|
||||
#endif // __AUSidetone_h__
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
File: AudioUnitProperties_AE2.h
|
||||
|
||||
Contains: Property constants for AudioUnits, stripped down for AE2
|
||||
|
||||
Copyright: (c) 2010 by Apple, Inc., all rights reserved.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __AUDIOUNITPROPERTIES_AE2__
|
||||
#define __AUDIOUNITPROPERTIES_AE2__
|
||||
|
||||
#include "CoreAudioTypes_AE2.h"
|
||||
#include "AUComponent_AE2.h"
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Core Implementation
|
||||
#pragma mark -
|
||||
/*!
|
||||
@enum Audio Unit scope types
|
||||
@abstract The scope IDs for audio units define basic roles and contexts for an audio unit's state.
|
||||
@discussion Each scope is a discrete context. Apple reserves scope IDs from 0 through 1024.
|
||||
|
||||
@constant kAudioUnitScope_Global The context for audio unit characteristics that apply to the audio unit as a
|
||||
whole
|
||||
@constant kAudioUnitScope_Input The context for audio data coming into an audio unit
|
||||
@constant kAudioUnitScope_Output The context for audio data leaving an audio unit
|
||||
@constant kAudioUnitScope_Group A context specific to the control scope of parameters (for instance,
|
||||
MIDI Channels is an example of this scope)
|
||||
@constant kAudioUnitScope_Part A distinct rendering context. For instance a single timbre in a multi-timbral
|
||||
instrument, a single loop in a multi looping capable looper unit, etc.
|
||||
@constant kAudioUnitScope_Layer A context which functions as a layer within a part and allows
|
||||
grouped control of Cell-scope parameters.
|
||||
An example is the percussive attack layer for an electric organ instrument
|
||||
@constant kAudioUnitScope_Note A scope that can be used to apply changes to an individual note. The
|
||||
elementID used with this scope is the unique note ID returned from
|
||||
a started note (see MusicDeviceStartNote)
|
||||
@constant kAudioUnitScope_Cell A scope which represents the finest level of granularity within an AU.
|
||||
The individual sample zones and envelope generators within a synth are an
|
||||
example of this.
|
||||
*/
|
||||
enum {
|
||||
kAudioUnitScope_Global = 0,
|
||||
kAudioUnitScope_Input = 1,
|
||||
kAudioUnitScope_Output = 2
|
||||
};
|
||||
|
||||
//=====================================================================================================================
|
||||
#pragma mark - Parameter Definitions
|
||||
|
||||
// assume kAudioUnitParameterUnit_Generic if not found in this enum
|
||||
/*!
|
||||
@enum AudioUnitParameterUnit
|
||||
@constant kAudioUnitParameterUnit_Generic
|
||||
untyped value generally between 0.0 and 1.0
|
||||
@constant kAudioUnitParameterUnit_Indexed
|
||||
takes an integer value (good for menu selections)
|
||||
@constant kAudioUnitParameterUnit_Boolean
|
||||
0.0 means FALSE, non-zero means TRUE
|
||||
@constant kAudioUnitParameterUnit_Percent
|
||||
usually from 0 -> 100, sometimes -50 -> +50
|
||||
@constant kAudioUnitParameterUnit_Seconds
|
||||
absolute or relative time
|
||||
@constant kAudioUnitParameterUnit_SampleFrames
|
||||
one sample frame equals (1.0/sampleRate) seconds
|
||||
@constant kAudioUnitParameterUnit_Phase
|
||||
-180 to 180 degrees
|
||||
@constant kAudioUnitParameterUnit_Rate
|
||||
rate multiplier, for playback speed, etc. (e.g. 2.0 == twice as fast)
|
||||
@constant kAudioUnitParameterUnit_Hertz
|
||||
absolute frequency/pitch in cycles/second
|
||||
@constant kAudioUnitParameterUnit_Cents
|
||||
unit of relative pitch
|
||||
@constant kAudioUnitParameterUnit_RelativeSemiTones
|
||||
useful for coarse detuning
|
||||
@constant kAudioUnitParameterUnit_MIDINoteNumber
|
||||
absolute pitch as defined in the MIDI spec (exact freq may depend on tuning table)
|
||||
@constant kAudioUnitParameterUnit_MIDIController
|
||||
a generic MIDI controller value from 0 -> 127
|
||||
@constant kAudioUnitParameterUnit_Decibels
|
||||
logarithmic relative gain
|
||||
@constant kAudioUnitParameterUnit_LinearGain
|
||||
linear relative gain
|
||||
@constant kAudioUnitParameterUnit_Degrees
|
||||
-180 to 180 degrees, similar to phase but more general (good for 3D coord system)
|
||||
@constant kAudioUnitParameterUnit_EqualPowerCrossfade
|
||||
0 -> 100, crossfade mix two sources according to sqrt(x) and sqrt(1.0 - x)
|
||||
@constant kAudioUnitParameterUnit_MixerFaderCurve1
|
||||
0.0 -> 1.0, pow(x, 3.0) -> linear gain to simulate a reasonable mixer channel fader response
|
||||
@constant kAudioUnitParameterUnit_Pan
|
||||
standard left to right mixer pan
|
||||
@constant kAudioUnitParameterUnit_Meters
|
||||
distance measured in meters
|
||||
@constant kAudioUnitParameterUnit_AbsoluteCents
|
||||
absolute frequency measurement :
|
||||
if f is freq in hertz then absoluteCents = 1200 * log2(f / 440) + 6900
|
||||
@constant kAudioUnitParameterUnit_Octaves
|
||||
octaves in relative pitch where a value of 1 is equal to 1200 cents
|
||||
@constant kAudioUnitParameterUnit_BPM
|
||||
beats per minute, ie tempo
|
||||
@constant kAudioUnitParameterUnit_Beats
|
||||
time relative to tempo, i.e., 1.0 at 120 BPM would equal 1/2 a second
|
||||
@constant kAudioUnitParameterUnit_Milliseconds
|
||||
parameter is expressed in milliseconds
|
||||
@constant kAudioUnitParameterUnit_Ratio
|
||||
for compression, expansion ratio, etc.
|
||||
@constant kAudioUnitParameterUnit_CustomUnit
|
||||
this is the parameter unit type for parameters that present a custom unit name
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kAudioUnitParameterUnit_Generic = 0,
|
||||
kAudioUnitParameterUnit_Indexed = 1,
|
||||
kAudioUnitParameterUnit_Boolean = 2,
|
||||
kAudioUnitParameterUnit_Percent = 3,
|
||||
kAudioUnitParameterUnit_Seconds = 4,
|
||||
kAudioUnitParameterUnit_SampleFrames = 5,
|
||||
kAudioUnitParameterUnit_Phase = 6,
|
||||
kAudioUnitParameterUnit_Rate = 7,
|
||||
kAudioUnitParameterUnit_Hertz = 8,
|
||||
kAudioUnitParameterUnit_Cents = 9,
|
||||
kAudioUnitParameterUnit_RelativeSemiTones = 10,
|
||||
kAudioUnitParameterUnit_MIDINoteNumber = 11,
|
||||
kAudioUnitParameterUnit_MIDIController = 12,
|
||||
kAudioUnitParameterUnit_Decibels = 13,
|
||||
kAudioUnitParameterUnit_LinearGain = 14,
|
||||
kAudioUnitParameterUnit_Degrees = 15,
|
||||
kAudioUnitParameterUnit_EqualPowerCrossfade = 16,
|
||||
kAudioUnitParameterUnit_MixerFaderCurve1 = 17,
|
||||
kAudioUnitParameterUnit_Pan = 18,
|
||||
kAudioUnitParameterUnit_Meters = 19,
|
||||
kAudioUnitParameterUnit_AbsoluteCents = 20,
|
||||
kAudioUnitParameterUnit_Octaves = 21,
|
||||
kAudioUnitParameterUnit_BPM = 22,
|
||||
kAudioUnitParameterUnit_Beats = 23,
|
||||
kAudioUnitParameterUnit_Milliseconds = 24,
|
||||
kAudioUnitParameterUnit_Ratio = 25,
|
||||
kAudioUnitParameterUnit_CustomUnit = 26
|
||||
};
|
||||
/*!
|
||||
@typedef AudioUnitParameterUnit
|
||||
*/
|
||||
typedef UInt32 AudioUnitParameterUnit;
|
||||
|
||||
/*!
|
||||
@struct AudioUnitParameterInfo
|
||||
@field name
|
||||
UNUSED - set to zero - UTF8 encoded C string (originally).
|
||||
@field unitName
|
||||
only valid if kAudioUnitParameterUnit_CustomUnit is set. If kAudioUnitParameterUnit_CustomUnit
|
||||
is set, this field must contain a valid CFString.
|
||||
@field clumpID
|
||||
only valid if kAudioUnitParameterFlag_HasClump
|
||||
@field cfNameString
|
||||
only valid if kAudioUnitParameterFlag_HasCFNameString
|
||||
@field unit
|
||||
if the "unit" field contains a value not in the enum above, then assume
|
||||
kAudioUnitParameterUnit_Generic
|
||||
@field minValue
|
||||
@field maxValue
|
||||
@field defaultValue
|
||||
@field flags
|
||||
Due to some vagaries about the ways in which Parameter's CFNames have been described, it was
|
||||
necessary to add a flag: kAudioUnitParameterFlag_CFNameRelease
|
||||
In normal usage a parameter name is essentially a static object, but sometimes an audio unit will
|
||||
generate parameter names dynamically.. As these are expected to be CFStrings, in that case
|
||||
the host should release those names when it is finished with them, but there was no way
|
||||
to communicate this distinction in behavior.
|
||||
Thus, if an audio unit will (or could) generate a name dynamically, it should set this flag in
|
||||
the parameter's info. The host should check for this flag, and if present, release the parameter
|
||||
name when it is finished with it.
|
||||
*/
|
||||
typedef struct AudioUnitParameterInfo
|
||||
{
|
||||
//char name[52];
|
||||
//CFStringRef unitName;
|
||||
UInt32 clumpID;
|
||||
//CFStringRef cfNameString;
|
||||
AudioUnitParameterUnit unit;
|
||||
AudioUnitParameterValue minValue;
|
||||
AudioUnitParameterValue maxValue;
|
||||
AudioUnitParameterValue defaultValue;
|
||||
UInt32 flags;
|
||||
} AudioUnitParameterInfo;
|
||||
|
||||
/*!
|
||||
@enum Audio Unit Parameter Flags
|
||||
@discussion Bit positions 18, 17, and 16 are set aside for display scales. Bit 19 is reserved.
|
||||
@constant kAudioUnitParameterFlag_CFNameRelease
|
||||
@constant kAudioUnitParameterFlag_MeterReadOnly
|
||||
@constant kAudioUnitParameterFlag_DisplayMask
|
||||
@constant kAudioUnitParameterFlag_DisplaySquareRoot
|
||||
@constant kAudioUnitParameterFlag_DisplaySquared
|
||||
@constant kAudioUnitParameterFlag_DisplayCubed
|
||||
@constant kAudioUnitParameterFlag_DisplayCubeRoot
|
||||
@constant kAudioUnitParameterFlag_DisplayExponential
|
||||
@constant kAudioUnitParameterFlag_HasClump
|
||||
@constant kAudioUnitParameterFlag_ValuesHaveStrings
|
||||
@constant kAudioUnitParameterFlag_DisplayLogarithmic
|
||||
@constant kAudioUnitParameterFlag_IsHighResolution
|
||||
@constant kAudioUnitParameterFlag_NonRealTime
|
||||
@constant kAudioUnitParameterFlag_CanRamp
|
||||
@constant kAudioUnitParameterFlag_ExpertMode
|
||||
@constant kAudioUnitParameterFlag_HasCFNameString
|
||||
@constant kAudioUnitParameterFlag_IsGlobalMeta
|
||||
@constant kAudioUnitParameterFlag_IsElementMeta
|
||||
@constant kAudioUnitParameterFlag_IsReadable
|
||||
@constant kAudioUnitParameterFlag_IsWritable
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kAudioUnitParameterFlag_CFNameRelease = (1L << 4),
|
||||
kAudioUnitParameterFlag_MeterReadOnly = (1L << 15),
|
||||
|
||||
// bit positions 18,17,16 are set aside for display scales. bit 19 is reserved.
|
||||
kAudioUnitParameterFlag_DisplayMask = (7L << 16) | (1L << 22),
|
||||
kAudioUnitParameterFlag_DisplaySquareRoot = (1L << 16),
|
||||
kAudioUnitParameterFlag_DisplaySquared = (2L << 16),
|
||||
kAudioUnitParameterFlag_DisplayCubed = (3L << 16),
|
||||
kAudioUnitParameterFlag_DisplayCubeRoot = (4L << 16),
|
||||
kAudioUnitParameterFlag_DisplayExponential = (5L << 16),
|
||||
kAudioUnitParameterFlag_HasClump = (1L << 20),
|
||||
kAudioUnitParameterFlag_ValuesHaveStrings = (1L << 21),
|
||||
kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22),
|
||||
kAudioUnitParameterFlag_IsHighResolution = (1L << 23),
|
||||
kAudioUnitParameterFlag_NonRealTime = (1L << 24),
|
||||
kAudioUnitParameterFlag_CanRamp = (1L << 25),
|
||||
kAudioUnitParameterFlag_ExpertMode = (1L << 26),
|
||||
kAudioUnitParameterFlag_HasCFNameString = (1L << 27),
|
||||
kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28),
|
||||
kAudioUnitParameterFlag_IsElementMeta = (1L << 29),
|
||||
kAudioUnitParameterFlag_IsReadable = (1L << 30),
|
||||
kAudioUnitParameterFlag_IsWritable = (1L << 31)
|
||||
};
|
||||
|
||||
typedef struct AUChannelInfo {
|
||||
SInt16 inChannels;
|
||||
SInt16 outChannels;
|
||||
} AUChannelInfo;
|
||||
|
||||
//==================================================================================================
|
||||
/*
|
||||
@constant kAUSongbird_SidetoneEQBlockData
|
||||
@discussion Scope: Global
|
||||
Value Type: blob
|
||||
Access: read/write
|
||||
|
||||
A property to get/set the biquad coefficients of the AE2 SidetoneEQ block as an
|
||||
array of AU_BiquadFilterDescription, sized to be the number of stages.
|
||||
*/
|
||||
|
||||
enum {
|
||||
kAUSongbird_SidetoneEQBlockData = 10000,
|
||||
kAUSongbird_SidetoneState = 10001
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
@struct AU_BiquadFilter
|
||||
@field mBypass
|
||||
Boolean to indicate if biquad is bypassed;
|
||||
@field b0
|
||||
@field b1
|
||||
@field b2
|
||||
@field a1
|
||||
@field a2
|
||||
Fields for biquad coefficients values;
|
||||
*/
|
||||
|
||||
typedef struct AU_BiquadFilter {
|
||||
UInt32 mBypass;
|
||||
Float64 b0, b1, b2, a1, a2;
|
||||
} AU_BiquadFilter;
|
||||
|
||||
/*!
|
||||
@struct AU_BiquadFilterDescription
|
||||
kAUSongbird_SidetoneEQBlockData data structure
|
||||
@field mBypass
|
||||
Boolean to indicate if all biquads are bypassed;
|
||||
@field mNumberFilters
|
||||
Number of active filters in the sidetone eq;
|
||||
@field mFilters
|
||||
variable length sized as mNumberFilters AU_BiquadFilter structures;
|
||||
*/
|
||||
|
||||
typedef struct AU_BiquadFilterDescription {
|
||||
UInt32 mBypass;
|
||||
UInt32 mNumberFilters;
|
||||
AU_BiquadFilter mFilters[1]; // this is a variable length array of mNumberFilters elements
|
||||
} AU_BiquadFilterDescription;
|
||||
|
||||
/*!
|
||||
@struct AU_SidetoneState
|
||||
kAUSongbird_SidetoneState data structure
|
||||
@field mVolume
|
||||
Gain for Sidetone;
|
||||
@field mFilterDescription
|
||||
Filter description for sidetone;
|
||||
*/
|
||||
|
||||
typedef struct AU_SidetoneState {
|
||||
bool mBypass;
|
||||
Float32 mVolume;
|
||||
AU_BiquadFilterDescription mFilterDescription;
|
||||
} AU_SidetoneState;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* CoreAudioTypes_AE2.h
|
||||
* Stripped down version of CoreAudioTypes just for what AE2 needs
|
||||
*
|
||||
* Copyright 2010 Apple Computer. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __COREAUDIOTYPES_AE2__
|
||||
#define __COREAUDIOTYPES_AE2__
|
||||
|
||||
#define CA_PREFER_FIXED_POINT 1
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef double Float64;
|
||||
typedef float Float32;
|
||||
|
||||
typedef SInt32 OSStatus;
|
||||
typedef SInt32 ComponentResult;
|
||||
|
||||
typedef SInt16 AudioSampleType;
|
||||
typedef SInt32 AudioUnitSampleType;
|
||||
|
||||
//XXX find the place where this is really defined
|
||||
enum {
|
||||
noErr = 0
|
||||
};
|
||||
|
||||
/*!
|
||||
@struct AudioStreamBasicDescription
|
||||
@abstract This structure encapsulates all the information for describing the basic
|
||||
format properties of a stream of audio data.
|
||||
@discussion This structure is sufficient to describe any constant bit rate format that has
|
||||
channels that are the same size. Extensions are required for variable bit rate
|
||||
data and for constant bit rate data where the channels have unequal sizes.
|
||||
However, where applicable, the appropriate fields will be filled out correctly
|
||||
for these kinds of formats (the extra data is provided via separate properties).
|
||||
In all fields, a value of 0 indicates that the field is either unknown, not
|
||||
applicable or otherwise is inapproprate for the format and should be ignored.
|
||||
Note that 0 is still a valid value for most formats in the mFormatFlags field.
|
||||
|
||||
In audio data a frame is one sample across all channels. In non-interleaved
|
||||
audio, the per frame fields identify one channel. In interleaved audio, the per
|
||||
frame fields identify the set of n channels. In uncompressed audio, a Packet is
|
||||
one frame, (mFramesPerPacket == 1). In compressed audio, a Packet is an
|
||||
indivisible chunk of compressed data, for example an AAC packet will contain
|
||||
1024 sample frames.
|
||||
@field mSampleRate
|
||||
The number of sample frames per second of the data in the stream.
|
||||
@field mFormatID
|
||||
A four char code indicating the general kind of data in the stream.
|
||||
@field mFormatFlags
|
||||
Flags specific to each format.
|
||||
@field mBytesPerPacket
|
||||
The number of bytes in a packet of data.
|
||||
@field mFramesPerPacket
|
||||
The number of sample frames in each packet of data.
|
||||
@field mBytesPerFrame
|
||||
The number of bytes in a single sample frame of data.
|
||||
@field mChannelsPerFrame
|
||||
The number of channels in each frame of data.
|
||||
@field mBitsPerChannel
|
||||
The number of bits of sample data for each channel in a frame of data.
|
||||
@field mReserved
|
||||
Pads the structure out to force an even 8 byte alignment.
|
||||
*/
|
||||
struct AudioStreamBasicDescription
|
||||
{
|
||||
Float64 mSampleRate;
|
||||
UInt32 mFormatID;
|
||||
UInt32 mFormatFlags;
|
||||
UInt32 mBytesPerPacket;
|
||||
UInt32 mFramesPerPacket;
|
||||
UInt32 mBytesPerFrame;
|
||||
UInt32 mChannelsPerFrame;
|
||||
UInt32 mBitsPerChannel;
|
||||
UInt32 mReserved;
|
||||
};
|
||||
typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;
|
||||
|
||||
|
||||
// SMPTETime is also defined in the CoreVideo headers.
|
||||
#if !defined(__SMPTETime__)
|
||||
#define __SMPTETime__
|
||||
|
||||
/*!
|
||||
@struct SMPTETime
|
||||
@abstract A structure for holding a SMPTE time.
|
||||
@field mSubframes
|
||||
The number of subframes in the full message.
|
||||
@field mSubframeDivisor
|
||||
The number of subframes per frame (typically 80).
|
||||
@field mCounter
|
||||
The total number of messages received.
|
||||
@field mType
|
||||
The kind of SMPTE time using the SMPTE time type constants.
|
||||
@field mFlags
|
||||
A set of flags that indicate the SMPTE state.
|
||||
@field mHours
|
||||
The number of hours in the full message.
|
||||
@field mMinutes
|
||||
The number of minutes in the full message.
|
||||
@field mSeconds
|
||||
The number of seconds in the full message.
|
||||
@field mFrames
|
||||
The number of frames in the full message.
|
||||
*/
|
||||
struct SMPTETime
|
||||
{
|
||||
SInt16 mSubframes;
|
||||
SInt16 mSubframeDivisor;
|
||||
UInt32 mCounter;
|
||||
UInt32 mType;
|
||||
UInt32 mFlags;
|
||||
SInt16 mHours;
|
||||
SInt16 mMinutes;
|
||||
SInt16 mSeconds;
|
||||
SInt16 mFrames;
|
||||
};
|
||||
typedef struct SMPTETime SMPTETime;
|
||||
|
||||
/*!
|
||||
@enum SMPTE Time Types
|
||||
@abstract Constants that describe the type of SMPTE time.
|
||||
@constant kSMPTETimeType24
|
||||
24 Frame
|
||||
@constant kSMPTETimeType25
|
||||
25 Frame
|
||||
@constant kSMPTETimeType30Drop
|
||||
30 Drop Frame
|
||||
@constant kSMPTETimeType30
|
||||
30 Frame
|
||||
@constant kSMPTETimeType2997
|
||||
29.97 Frame
|
||||
@constant kSMPTETimeType2997Drop
|
||||
29.97 Drop Frame
|
||||
@constant kSMPTETimeType60
|
||||
60 Frame
|
||||
@constant kSMPTETimeType5994
|
||||
59.94 Frame
|
||||
@constant kSMPTETimeType60Drop
|
||||
60 Drop Frame
|
||||
@constant kSMPTETimeType5994Drop
|
||||
59.94 Drop Frame
|
||||
@constant kSMPTETimeType50
|
||||
50 Frame
|
||||
@constant kSMPTETimeType2398
|
||||
23.98 Frame
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kSMPTETimeType24 = 0,
|
||||
kSMPTETimeType25 = 1,
|
||||
kSMPTETimeType30Drop = 2,
|
||||
kSMPTETimeType30 = 3,
|
||||
kSMPTETimeType2997 = 4,
|
||||
kSMPTETimeType2997Drop = 5,
|
||||
kSMPTETimeType60 = 6,
|
||||
kSMPTETimeType5994 = 7,
|
||||
kSMPTETimeType60Drop = 8,
|
||||
kSMPTETimeType5994Drop = 9,
|
||||
kSMPTETimeType50 = 10,
|
||||
kSMPTETimeType2398 = 11
|
||||
};
|
||||
|
||||
/*!
|
||||
@enum SMPTE State Flags
|
||||
@abstract Flags that describe the SMPTE time state.
|
||||
@constant kSMPTETimeValid
|
||||
The full time is valid.
|
||||
@constant kSMPTETimeRunning
|
||||
Time is running.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kSMPTETimeValid = (1 << 0),
|
||||
kSMPTETimeRunning = (1 << 1)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@struct AudioTimeStamp
|
||||
@abstract A structure that holds different representations of the same point in time.
|
||||
@field mSampleTime
|
||||
The absolute sample frame time.
|
||||
@field mHostTime
|
||||
The host machine's time base, mach_absolute_time.
|
||||
@field mRateScalar
|
||||
The ratio of actual host ticks per sample frame to the nominal host ticks
|
||||
per sample frame.
|
||||
@field mWordClockTime
|
||||
The word clock time.
|
||||
@field mSMPTETime
|
||||
The SMPTE time.
|
||||
@field mFlags
|
||||
A set of flags indicating which representations of the time are valid.
|
||||
@field mReserved
|
||||
Pads the structure out to force an even 8 byte alignment.
|
||||
*/
|
||||
struct AudioTimeStamp
|
||||
{
|
||||
Float64 mSampleTime;
|
||||
UInt64 mHostTime;
|
||||
Float64 mRateScalar;
|
||||
UInt64 mWordClockTime;
|
||||
SMPTETime mSMPTETime;
|
||||
UInt32 mFlags;
|
||||
UInt32 mReserved;
|
||||
};
|
||||
typedef struct AudioTimeStamp AudioTimeStamp;
|
||||
|
||||
/*!
|
||||
@enum AudioTimeStamp Flags
|
||||
@abstract The flags that indicate which fields in an AudioTimeStamp structure are valid.
|
||||
@constant kAudioTimeStampSampleTimeValid
|
||||
The sample frame time is valid.
|
||||
@constant kAudioTimeStampHostTimeValid
|
||||
The host time is valid.
|
||||
@constant kAudioTimeStampRateScalarValid
|
||||
The rate scalar is valid.
|
||||
@constant kAudioTimeStampWordClockTimeValid
|
||||
The word clock time is valid.
|
||||
@constant kAudioTimeStampSMPTETimeValid
|
||||
The SMPTE time is valid.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kAudioTimeStampSampleTimeValid = (1 << 0),
|
||||
kAudioTimeStampHostTimeValid = (1 << 1),
|
||||
kAudioTimeStampRateScalarValid = (1 << 2),
|
||||
kAudioTimeStampWordClockTimeValid = (1 << 3),
|
||||
kAudioTimeStampSMPTETimeValid = (1 << 4)
|
||||
};
|
||||
|
||||
/*!
|
||||
@enum Commonly Used Combinations of AudioTimeStamp Flags
|
||||
@abstract Some commonly used combinations of AudioTimeStamp flags.
|
||||
@constant kAudioTimeStampSampleHostTimeValid
|
||||
The sample frame time and the host time are valid.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kAudioTimeStampSampleHostTimeValid = (kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include "iop_au_interface.h"
|
||||
#include "AUSidetone.h"
|
||||
#include "AUNull.h"
|
||||
#include <debug.h>
|
||||
|
||||
extern "C" audio_unit_t Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
|
||||
{
|
||||
#if USE_SIDETONE
|
||||
AUBase_AE2 *This = AUSidetone::Create_AUSidetone(sampleRate, numChannels, sampleSize);
|
||||
return (audio_unit_t)This;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" audio_unit_t Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
|
||||
{
|
||||
AUBase_AE2 *This = AUNull::Create_AUNull(sampleRate, numChannels, sampleSize);
|
||||
return (audio_unit_t)This;
|
||||
}
|
||||
|
||||
extern "C" void Destroy_AudioUnit(audio_unit_t au)
|
||||
{
|
||||
AUBase_AE2 *This = (AUBase_AE2*)au;
|
||||
delete This;
|
||||
}
|
||||
|
||||
extern "C" int32_t AudioUnit_GetParameter(audio_unit_t au, uint32_t parameter, float *parameterValue)
|
||||
{
|
||||
if (!au)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AUBase_AE2 *This = (AUBase_AE2*)au;
|
||||
*parameterValue = This->GetParameter((AudioUnitParameterID)parameter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int32_t AudioUnit_SetParameter(audio_unit_t au, uint32_t parameter, float parameterValue)
|
||||
{
|
||||
if (!au)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AUBase_AE2 *This = (AUBase_AE2*)au;
|
||||
return This->SetParameter((AudioUnitParameterID)parameter, (AudioUnitParameterValue)parameterValue);
|
||||
}
|
||||
|
||||
extern "C" int32_t AudioUnit_GetPropertyInfo(audio_unit_t au, uint32_t propID, uint32_t *outSize, bool *outWritable)
|
||||
{
|
||||
if (!au)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AUBase_AE2 *This = (AUBase_AE2*)au;
|
||||
return This->GetPropertyInfo(static_cast<AudioUnitPropertyID>(propID), *outSize, *outWritable);
|
||||
}
|
||||
|
||||
extern "C" int32_t AudioUnit_GetProperty(audio_unit_t au, uint32_t propID, void *outData, uint32_t *ioSize)
|
||||
{
|
||||
if (!au)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
uint32_t propSize;
|
||||
bool propWritable;
|
||||
int32_t result = AudioUnit_GetPropertyInfo(au, propID, &propSize, &propWritable);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (propSize > *ioSize)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*ioSize = propSize;
|
||||
AUBase_AE2 *This = (AUBase_AE2*)au;
|
||||
return This->GetProperty(static_cast<AudioUnitPropertyID>(propID), outData);
|
||||
}
|
||||
|
||||
extern "C" int32_t AudioUnit_SetProperty(audio_unit_t au, uint32_t propID, const void *inData, uint32_t inSize)
|
||||
{
|
||||
if (!au)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
uint32_t propSize;
|
||||
bool propWritable;
|
||||
int32_t result = AudioUnit_GetPropertyInfo(au, propID, &propSize, &propWritable);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((!propWritable))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AUBase_AE2 *This = (AUBase_AE2*)au;
|
||||
return This->SetProperty(static_cast<AudioUnitPropertyID>(propID), inData, inSize);
|
||||
}
|
||||
|
||||
extern "C" void AudioUnit_Process(audio_unit_t au, const void *inSourceP, void *inDestP, uint32_t inFramesToProcess)
|
||||
{
|
||||
if (!au)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
AUBase_AE2 *This = (AUBase_AE2*)au;
|
||||
This->Process(inSourceP, inDestP, inFramesToProcess);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef _IOP_AU_INTERFACE_H_
|
||||
#define _IOP_AU_INTERFACE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void * audio_unit_t;
|
||||
|
||||
audio_unit_t Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
|
||||
audio_unit_t Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
|
||||
|
||||
void Destroy_AudioUnit(audio_unit_t au);
|
||||
|
||||
// parameters are single values that can be adjusted
|
||||
int32_t AudioUnit_GetParameter(audio_unit_t au, uint32_t parameter, float *parameterValue);
|
||||
int32_t AudioUnit_SetParameter(audio_unit_t au, uint32_t parameter, float parameterValue);
|
||||
|
||||
// properties are blocks of data that can be adjusted, usually a private setting between user and unit
|
||||
int32_t AudioUnit_GetPropertyInfo(audio_unit_t au, uint32_t propID, uint32_t *outSize, bool *outWritable);
|
||||
int32_t AudioUnit_GetProperty(audio_unit_t au, uint32_t propID, void *outData, uint32_t *ioSize);
|
||||
int32_t AudioUnit_SetProperty(audio_unit_t au, uint32_t propID, const void *inData, uint32_t inSize);
|
||||
|
||||
void AudioUnit_Process(audio_unit_t au, const void *inSourceP, void *inDestP, uint32_t inFramesToProcess);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _IOP_AU_INTERFACE_H_
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include "debug_tap.h"
|
||||
#include "timestamper.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <AssertMacros.h>
|
||||
#include <platform.h>
|
||||
#include <platform/timer.h>
|
||||
#include <sys/task.h>
|
||||
|
||||
#include "iop_au_interface.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *IOBufferBegin;
|
||||
uint8_t *IOBufferEnd;
|
||||
uint8_t *IOBuffer;
|
||||
} internal_debug_tap_t;
|
||||
|
||||
debug_tap_t create_debug_tap(uint8_t *inIOBegin, uint8_t *inIOEnd, uint8_t *inIO)
|
||||
{
|
||||
internal_debug_tap_t *This = (internal_debug_tap_t*)malloc(sizeof(debug_tap_t));
|
||||
if (This)
|
||||
{
|
||||
This->IOBufferBegin = inIOBegin;
|
||||
This->IOBufferEnd = inIOEnd;
|
||||
This->IOBuffer = inIO;
|
||||
}
|
||||
return This;
|
||||
}
|
||||
|
||||
void destroy_debug_tap(debug_tap_t tap)
|
||||
{
|
||||
free(tap);
|
||||
}
|
||||
|
||||
static void clean_cache(void *addr, size_t size)
|
||||
{
|
||||
// the onus is on the caller to make sure the cache is aligned
|
||||
void* lineBase = (void*)((uint32_t)addr & ~(CPU_CACHELINE_SIZE-1));
|
||||
size_t sizeToDo = ((((uint32_t)addr & (CPU_CACHELINE_SIZE-1)) + size)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
|
||||
platform_cache_operation(CACHE_CLEAN, lineBase, sizeToDo);
|
||||
}
|
||||
|
||||
static void invalidate_cache(void *addr, size_t size)
|
||||
{
|
||||
void* lineBase = (void*)((uint32_t)addr & ~(CPU_CACHELINE_SIZE-1));
|
||||
size_t sizeToDo = ((((uint32_t)addr & (CPU_CACHELINE_SIZE-1)) + size)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
|
||||
platform_cache_operation(CACHE_INVALIDATE, lineBase, sizeToDo);
|
||||
}
|
||||
|
||||
static size_t min(size_t a, size_t b) { return (a < b) ? a : b; }
|
||||
|
||||
size_t send_to_tap(debug_tap_t tap, const uint8_t* src, uint32_t sizeInBytes)
|
||||
{
|
||||
internal_debug_tap_t *This = (internal_debug_tap_t*)tap;
|
||||
size_t result = 0;
|
||||
|
||||
// don't transfer anything to user if sizeInBytes is larger than IOBuffer:
|
||||
if (sizeInBytes > (uint32_t)(This->IOBufferEnd - This->IOBufferBegin))
|
||||
return result;
|
||||
|
||||
// make sure we don't overflow the IOBuffer
|
||||
size_t dataToSend = min(This->IOBufferEnd - This->IOBuffer, sizeInBytes);
|
||||
memcpy(This->IOBuffer, src, dataToSend);
|
||||
clean_cache(This->IOBuffer, dataToSend);
|
||||
This->IOBuffer += dataToSend;
|
||||
src += dataToSend;
|
||||
if (This->IOBuffer >= This->IOBufferEnd)
|
||||
{
|
||||
// if we've wrapped around, send the size of the IOBuffer to the caller
|
||||
This->IOBuffer = This->IOBufferBegin;
|
||||
result = This->IOBufferEnd - This->IOBufferBegin;
|
||||
}
|
||||
|
||||
// now that we've sent the data, how much is left
|
||||
dataToSend = sizeInBytes - dataToSend;
|
||||
// if there is anything left, send it
|
||||
if (dataToSend)
|
||||
{
|
||||
memcpy(This->IOBuffer, src, dataToSend);
|
||||
clean_cache(This->IOBuffer, sizeInBytes);
|
||||
This->IOBuffer += dataToSend;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
size_t get_from_tap(debug_tap_t tap, uint8_t* dst, uint32_t sizeInBytes)
|
||||
{
|
||||
internal_debug_tap_t *This = (internal_debug_tap_t*)tap;
|
||||
size_t result = 0;
|
||||
|
||||
// don't transfer anything to user if sizeInBytes is larger than IOBuffer:
|
||||
if (sizeInBytes > (uint32_t)(This->IOBufferEnd - This->IOBufferBegin))
|
||||
return result;
|
||||
|
||||
// make sure we don't overflow the IOBuffer
|
||||
size_t dataToGet = min(This->IOBufferEnd - This->IOBuffer, sizeInBytes);
|
||||
invalidate_cache(This->IOBuffer, dataToGet);
|
||||
memcpy(dst, This->IOBuffer, dataToGet);
|
||||
This->IOBuffer += dataToGet;
|
||||
dst += dataToGet;
|
||||
if (This->IOBuffer >= This->IOBufferEnd)
|
||||
{
|
||||
// if we've wrapped around, send the size of the IOBuffer to the caller
|
||||
This->IOBuffer = This->IOBufferBegin;
|
||||
result = This->IOBufferEnd - This->IOBufferBegin;
|
||||
}
|
||||
|
||||
// now that we've got the data, how much is left
|
||||
dataToGet = sizeInBytes - dataToGet;
|
||||
// if there is anything left, get it
|
||||
if (dataToGet)
|
||||
{
|
||||
invalidate_cache(This->IOBuffer, dataToGet);
|
||||
memcpy(dst, This->IOBuffer, dataToGet);
|
||||
This->IOBuffer += dataToGet;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUG_TAP_H_
|
||||
#define _DEBUG_TAP_H_
|
||||
|
||||
#include <platform.h>
|
||||
|
||||
/*
|
||||
* A debug_tap is a way of tapping off or tapping in an audio signal from
|
||||
* user space. Pass the pointer to the beginning, end, and start to create
|
||||
* a tap. You send or get data from a tap, which causes the write or read
|
||||
* head to progress around the buffer circularly. When the process wraps,
|
||||
* the send or get call returns the total number of bytes sent, otherwise
|
||||
* it returns 0.
|
||||
*/
|
||||
|
||||
typedef void * debug_tap_t;
|
||||
|
||||
debug_tap_t create_debug_tap(uint8_t *inIOBegin, uint8_t *inIOEnd, uint8_t *inIO);
|
||||
|
||||
void destroy_debug_tap(debug_tap_t tap);
|
||||
|
||||
// return value is nonzero on calls where we wrap IO buffer. Returned
|
||||
// value indicates number bytes to give to "timestamp" function
|
||||
size_t send_to_tap(debug_tap_t tap, const uint8_t* src, uint32_t sizeInBytes);
|
||||
size_t get_from_tap(debug_tap_t tap, uint8_t* dst, uint32_t sizeInBytes);
|
||||
|
||||
#endif // _DEBUG_TAP_H_
|
||||
|
||||
@@ -0,0 +1,893 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <AssertMacros.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <platform.h>
|
||||
#include <platform/timer.h>
|
||||
#include <sys/task.h>
|
||||
|
||||
#include <iop.h>
|
||||
#include <qwi.h>
|
||||
#include <qwi_protocol.h>
|
||||
#include <EmbeddedIOPProtocol.h>
|
||||
#include <arch.h>
|
||||
#include <arch/arm/arm.h>
|
||||
#include <drivers/audio/audio.h>
|
||||
#include "clock_stepping.h"
|
||||
|
||||
#define USE_DMA 0
|
||||
|
||||
#include "iop_audiodsp_protocol.h"
|
||||
#include "iop_au_interface.h"
|
||||
#include "loopback_process.h"
|
||||
#include "timestamper.h"
|
||||
#include "debug_tap.h"
|
||||
#include "ae2_mca.h"
|
||||
#if USE_DMA
|
||||
#include "ae2_i2s.h"
|
||||
#include "ae2_dma.h"
|
||||
#else
|
||||
#include "loopback_device.h"
|
||||
#endif
|
||||
|
||||
|
||||
static const uint32_t kDefaultSampleRate = 8000;
|
||||
static const uint32_t kMaxSampleRate = 48000;
|
||||
// We configure for 0.004 sec of latency.
|
||||
static const uint32_t kDefaultLatencyInUS = 4000;
|
||||
static const uint32_t kMaxLatencyInUS = 40000;
|
||||
// Describing the MCA data format.
|
||||
static const uint32_t kDefaultNumChannels = 1;
|
||||
static const uint32_t kMaxNumChannels = 2;
|
||||
#if USE_DMA
|
||||
static const uint32_t kValidChannelBitmask = 1;
|
||||
#endif
|
||||
static const uint32_t kDefaultSampleSize = 2;
|
||||
|
||||
static const size_t kDefaultDMABufferSize = 0x300;
|
||||
|
||||
static const AudioDevice_Index kDataDevice = kMCA_0;
|
||||
#if USE_DMA
|
||||
static const uint32_t kDataReceivePort = rMCA0_RXDATA;
|
||||
static const uint32_t kDataTransmitPort = rMCA0_TXDATA;
|
||||
#endif
|
||||
|
||||
static bool audiodsp_message_process(void);
|
||||
|
||||
static int iop_audiodsp_task(void *cfg);
|
||||
static void iop_audiodsp_sleep(int mode);
|
||||
|
||||
IOP_FUNCTION(audiodsp, iop_audiodsp_task, 8*1024, AUDIODSP_CONTROL_CHANNEL);
|
||||
IOP_SLEEP_HOOK(audiodsp, iop_audiodsp_sleep);
|
||||
|
||||
static int audiodsp_channel;
|
||||
|
||||
#if USE_DMA
|
||||
static dma_object_t s_input_dma = NULL;
|
||||
static dma_object_t s_output_dma = NULL;
|
||||
static void audiodevice_dma_int_err_handler(void *arg);
|
||||
static void audiodevice_dma_int_handler(void *arg);
|
||||
|
||||
static const size_t kNumDMABuffers = 4;
|
||||
static int16_t *sBuffer[kNumDMABuffers];
|
||||
static size_t sCurrentProcessBufferIndex;
|
||||
static DMALinkedListItem *sLLIDMAInput[kNumDMABuffers], *sLLIDMAOutput[kNumDMABuffers];
|
||||
#endif
|
||||
#if !USE_DMA
|
||||
static loopback_device_t s_loopback_device = NULL;
|
||||
#endif // USE_DMA
|
||||
|
||||
// Loopback processing "globals"
|
||||
static audio_unit_t s_loopback_au = NULL;
|
||||
|
||||
// Audio Unit biquad property.
|
||||
static void* s_loopback_au_state_property = NULL;
|
||||
static uint32_t s_loopback_au_state_property_size = 0;
|
||||
// forward declare here. Make sure to change this if id changes
|
||||
static const uint32_t k_loopback_au_state_property_id = 10001;
|
||||
|
||||
static loopback_process_t s_loopback_process = NULL;
|
||||
static debug_tap_t sInputTap = NULL;
|
||||
static debug_tap_t sOutputTap = NULL;
|
||||
|
||||
// forward declaration:
|
||||
audio_unit_t Create_Loopback_AU(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess);
|
||||
|
||||
/***
|
||||
* IMPORTANT!
|
||||
* We assume that all the SRAM allocations will total less than 0x1000.
|
||||
* Adjust the SRAM Heap in memmap.h if this assumption changes.
|
||||
*/
|
||||
static uint32_t sSRAMMallocOffset = 0;
|
||||
#ifdef AUDIO_SRAM_RESERVE
|
||||
static const uint32_t kSRAMMallocMax = AUDIO_SRAM_RESERVE;
|
||||
#else /* !AUDIO_SRAM_RESERVE */
|
||||
static const uint32_t kSRAMMallocMax = (uint32_t)-1;
|
||||
#endif /* AUDIO_SRAM_RESERVE */
|
||||
|
||||
void* mallocFromSRAM(uint32_t byteSize)
|
||||
{
|
||||
//Make sure its a 4-byte aligned allocation (for DMA xfers)
|
||||
byteSize = (byteSize + 3) / 4;
|
||||
byteSize *= 4;
|
||||
|
||||
//AUDIO_BASE_ADDR is the start of SRAM in AE2
|
||||
void *ptr = (void*)(AUDIO_BASE_ADDR + sSRAMMallocOffset);
|
||||
sSRAMMallocOffset += byteSize;
|
||||
|
||||
if (sSRAMMallocOffset > kSRAMMallocMax)
|
||||
panic("mallocFromSRAM beyond limit");
|
||||
|
||||
memset(ptr, 0, byteSize);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if USE_DMA
|
||||
static bool sDMABuffersInitialized = false;
|
||||
void
|
||||
initialize_dma_buffers(void)
|
||||
{
|
||||
if (!sDMABuffersInitialized)
|
||||
{
|
||||
for(uint32_t i = 0; i < kNumDMABuffers; i++)
|
||||
{
|
||||
sLLIDMAInput[i] = (DMALinkedListItem *)mallocFromSRAM(sizeof(DMALinkedListItem));
|
||||
sLLIDMAOutput[i] = (DMALinkedListItem *)mallocFromSRAM(sizeof(DMALinkedListItem));
|
||||
sBuffer[i] = (int16_t *)mallocFromSRAM(kDefaultDMABufferSize);
|
||||
}
|
||||
|
||||
//Setup input chain for infinite quad-buffer looping, control setup handled in startDMA
|
||||
for(uint32_t i = 0; i < kNumDMABuffers; i++)
|
||||
{
|
||||
// RX goes into buffer 2 on start, so we start the chain on 3
|
||||
sLLIDMAInput[i]->destination = (uint32_t)sBuffer[(i+3)%kNumDMABuffers];
|
||||
sLLIDMAInput[i]->next = sLLIDMAInput[(i+1)%kNumDMABuffers];
|
||||
sLLIDMAInput[i]->source = kDataReceivePort;
|
||||
|
||||
// TX goes from buffer 0 on start, so we start the chain on 1
|
||||
sLLIDMAOutput[i]->source = (uint32_t)sBuffer[(i+1)%kNumDMABuffers];
|
||||
sLLIDMAOutput[i]->next = sLLIDMAOutput[(i+1)%kNumDMABuffers];
|
||||
sLLIDMAOutput[i]->destination = kDataTransmitPort;
|
||||
}
|
||||
sDMABuffersInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
reset_dma_buffers(void)
|
||||
{
|
||||
for(uint32_t i = 0; i < kNumDMABuffers; i++)
|
||||
{
|
||||
memset(sBuffer[i], 0, kDefaultDMABufferSize);
|
||||
}
|
||||
sCurrentProcessBufferIndex = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
iop_audiodsp_task(void *cfg)
|
||||
{
|
||||
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
|
||||
struct task_event* audiodsp_message_event = (struct task_event*) malloc(sizeof(struct task_event));
|
||||
|
||||
dprintf(DEBUG_SPEW, "@@ AudioDSP task starting\n");
|
||||
|
||||
check(kIOPAUDIODSP_COMMAND_SIZE == sizeof(IOPAUDIODSP_Command));
|
||||
|
||||
/* establish the host communications channel */
|
||||
event_init(audiodsp_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
dprintf(DEBUG_SPEW, "** opening audiodsp channel\n");
|
||||
audiodsp_channel = qwi_instantiate_channel("audio command",
|
||||
QWI_ROLE_CONSUMER,
|
||||
channel->ring_size,
|
||||
(void *)mem_static_map_cached(channel->ring_base),
|
||||
(qwi_channel_hook)event_signal,
|
||||
audiodsp_message_event);
|
||||
|
||||
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
|
||||
/* Little doubt we'll need VFP/Neon */
|
||||
arch_task_fp_enable(true);
|
||||
#endif
|
||||
|
||||
// create a audio unit if one does not exist
|
||||
if (!s_loopback_au)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "creating loopback\n");
|
||||
s_loopback_au = Create_Loopback_AU(kDefaultSampleRate, kDefaultNumChannels, kDefaultSampleSize);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
dprintf(DEBUG_SPEW, "@@ waiting for message on audiodsp channel\n");
|
||||
while (audiodsp_message_process()) {
|
||||
// eat all available messages
|
||||
}
|
||||
event_wait(audiodsp_message_event);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
audio_unit_t Create_Loopback_AU(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
|
||||
{
|
||||
audio_unit_t loopback_au = NULL;
|
||||
dprintf(DEBUG_INFO, "creating loopback\n");
|
||||
#if USE_SIDETONE
|
||||
loopback_au = Create_AUSidetone(sampleRate, channels, kDefaultSampleSize);
|
||||
#else
|
||||
loopback_au = Create_AUNull(sampleRate, channels, kDefaultSampleSize);
|
||||
#endif
|
||||
if (loopback_au && s_loopback_au_state_property && s_loopback_au_state_property_size)
|
||||
{
|
||||
// if we had a previous property
|
||||
uint32_t propSize = s_loopback_au_state_property_size;
|
||||
bool propWritable = true;
|
||||
if (/*noErr*/0 == AudioUnit_GetPropertyInfo(loopback_au, k_loopback_au_state_property_id, &propSize, &propWritable))
|
||||
{
|
||||
if (propWritable && propSize == s_loopback_au_state_property_size)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Restoring previous SidetoneEQ values\n");
|
||||
if (/*noErr*/0 != AudioUnit_SetProperty(loopback_au, k_loopback_au_state_property_id, s_loopback_au_state_property, s_loopback_au_state_property_size))
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Warning, could not restore previous SidetoneEQ values\n");
|
||||
// now what? Destroy the AU and start over again
|
||||
Destroy_AudioUnit(loopback_au);
|
||||
loopback_au = NULL;
|
||||
#if USE_SIDETONE
|
||||
loopback_au = Create_AUSidetone(sampleRate, channels, kDefaultSampleSize);
|
||||
#else
|
||||
loopback_au = Create_AUNull(sampleRate, channels, kDefaultSampleSize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return loopback_au;
|
||||
}
|
||||
|
||||
|
||||
void Destroy_Loopback_AU(audio_unit_t loopback_au)
|
||||
{
|
||||
if (loopback_au)
|
||||
{
|
||||
uint32_t propSize = s_loopback_au_state_property_size;
|
||||
bool propWritable = true;
|
||||
if (/*noErr*/0 == AudioUnit_GetPropertyInfo(loopback_au, k_loopback_au_state_property_id, &propSize, &propWritable))
|
||||
{
|
||||
// clear the old property block if it won't fit
|
||||
if (s_loopback_au_state_property && s_loopback_au_state_property_size != propSize)
|
||||
{
|
||||
free(s_loopback_au_state_property);
|
||||
s_loopback_au_state_property = NULL;
|
||||
s_loopback_au_state_property_size = 0;
|
||||
}
|
||||
// if we don't have somewhere to write the properties, create it:
|
||||
if (!s_loopback_au_state_property && (0 != propSize))
|
||||
{
|
||||
s_loopback_au_state_property = malloc(propSize);
|
||||
s_loopback_au_state_property_size = propSize;
|
||||
}
|
||||
// if we do have somewhere to write it, save off what we have
|
||||
if (s_loopback_au_state_property)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Saving SidetoneEQ values\n");
|
||||
if (/*noErr*/0 != AudioUnit_GetProperty(loopback_au, k_loopback_au_state_property_id, s_loopback_au_state_property, &s_loopback_au_state_property_size))
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Warning, could not save SidetoneEQ values\n");
|
||||
// for whatever reason, we didn't save the property. free it so we don't try to restore it
|
||||
free(s_loopback_au_state_property);
|
||||
s_loopback_au_state_property = NULL;
|
||||
s_loopback_au_state_property_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Destroy_AudioUnit(loopback_au);
|
||||
loopback_au = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if USE_DMA
|
||||
// Destroy the loopback processing and AU. Assumes that loopback is not running
|
||||
bool Destroy_Loopback_Processing()
|
||||
{
|
||||
if (s_loopback_process)
|
||||
{
|
||||
destroy_loopback_process(s_loopback_process);
|
||||
s_loopback_process = NULL;
|
||||
}
|
||||
if (s_loopback_au)
|
||||
{
|
||||
Destroy_Loopback_AU(s_loopback_au);
|
||||
s_loopback_au = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates the loopback processing and AU. Assumes that loopback is not running
|
||||
// will always destroy any existing loopbacks
|
||||
bool Create_Loopback_Processing(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
|
||||
{
|
||||
Destroy_Loopback_Processing();
|
||||
// initialize the loopback processing. It will be enabled later
|
||||
// create a audio unit if one does not exist
|
||||
if (!s_loopback_au)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "creating loopback\n");
|
||||
s_loopback_au = Create_Loopback_AU(sampleRate, channels, kDefaultSampleSize);
|
||||
}
|
||||
dprintf(DEBUG_INFO, "loopback is %p\n", s_loopback_au);
|
||||
// if we have an audio unit, create a loopback proces
|
||||
if (s_loopback_au && !s_loopback_process)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "creating loopback process\n");
|
||||
s_loopback_process = create_loopback_process(s_loopback_au, framesToProcess, channels, kDefaultSampleSize, kValidChannelBitmask);
|
||||
}
|
||||
dprintf(DEBUG_INFO, "loopback process is %p\n", s_loopback_process);
|
||||
dprintf(DEBUG_INFO, "free mem after %d\n", heap_get_free_mem());
|
||||
|
||||
if (s_loopback_process)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool s_loopback_enabled = false;
|
||||
// return true if loop processing stopped
|
||||
bool Stop_Loopback_Processing()
|
||||
{
|
||||
// if loopback processing not running, done
|
||||
if(!s_loopback_enabled)
|
||||
{
|
||||
return !s_loopback_enabled;
|
||||
}
|
||||
#if USE_DMA
|
||||
// log any sort of errors encountered
|
||||
if (s_input_dma || s_output_dma)
|
||||
{
|
||||
for (uint32_t i = kFrameError; i < kError_last; ++i)
|
||||
{
|
||||
// from Alexei:
|
||||
// Ignore the frame errors. They're not causing
|
||||
// interrupts and they are expected (because L81
|
||||
// masters BCLK at 12MHz always, which is more than the
|
||||
// exact number of bits specified in the MCA config;
|
||||
// unfortunately, there isn't a way to configure MCA to
|
||||
// say "give me a frame error if there are fewer bits,
|
||||
// but not if there are more", which is what you'd
|
||||
// want.)
|
||||
if (kFrameError == i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t error_count = ((s_input_dma) ? getErrorCount(s_input_dma, i) : 0) + ((s_output_dma) ? getErrorCount(s_output_dma, i) : 0);
|
||||
if (error_count)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "DMA encountered %d %s error%s!\n", error_count, kErrorTypeStr[i], error_count > 1 ? "s" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
dprintf(DEBUG_INFO, "Going to stop the DMA\n");
|
||||
if (s_input_dma)
|
||||
{
|
||||
destroy_dma_object(s_input_dma);
|
||||
s_input_dma = NULL;
|
||||
}
|
||||
if (s_output_dma)
|
||||
{
|
||||
destroy_dma_object(s_output_dma);
|
||||
s_output_dma = NULL;
|
||||
}
|
||||
dprintf(DEBUG_CRITICAL, "Stopped the DMA\n");
|
||||
#else
|
||||
// log any sort of errors encountered
|
||||
if (s_loopback_device)
|
||||
{
|
||||
for (uint32_t i = kFrameError; i < kError_last; ++i)
|
||||
{
|
||||
// from Alexei:
|
||||
// Ignore the frame errors. They're not causing
|
||||
// interrupts and they are expected (because L81
|
||||
// masters BCLK at 12MHz always, which is more than the
|
||||
// exact number of bits specified in the MCA config;
|
||||
// unfortunately, there isn't a way to configure MCA to
|
||||
// say "give me a frame error if there are fewer bits,
|
||||
// but not if there are more", which is what you'd
|
||||
// want.)
|
||||
if (kFrameError == i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t error_count = getErrorCount(s_loopback_device, i);
|
||||
if (error_count)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "loopback encountered %d %s error%s!\n", error_count, kErrorTypeStr[i], error_count > 1 ? "s" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
dprintf(DEBUG_INFO, "Going to stop the loopback\n");
|
||||
if (s_loopback_device)
|
||||
{
|
||||
destroy_loopback_device(s_loopback_device);
|
||||
s_loopback_device = NULL;
|
||||
}
|
||||
dprintf(DEBUG_CRITICAL, "Stopped the loopback\n");
|
||||
#endif
|
||||
|
||||
SetClockState(kClockRequestLoopback, kClockValueLow);
|
||||
|
||||
s_loopback_enabled = false;
|
||||
return !s_loopback_enabled;
|
||||
}
|
||||
|
||||
|
||||
// return true if loopback processing started
|
||||
bool Start_Loopback_Processing(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Start_Loopback_Processing(sampleRate %d, channels %d, framesToProcess %d\n", sampleRate, channels, framesToProcess);
|
||||
if (!Stop_Loopback_Processing())
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Could not stop loopback\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if loopback processing already running, done
|
||||
if (s_loopback_enabled)
|
||||
{
|
||||
return s_loopback_enabled;
|
||||
}
|
||||
|
||||
if (sampleRate > 32000)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Increasing clock rate for loopback\n");
|
||||
SetClockState(kClockRequestLoopback, kClockValueHigh);
|
||||
}
|
||||
|
||||
#if USE_DMA
|
||||
// create the loopback proessing with this sample rate
|
||||
if (Create_Loopback_Processing(sampleRate, channels, framesToProcess) && s_loopback_process)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "starting up DMA\n");
|
||||
initialize_dma_buffers();
|
||||
|
||||
reset_dma_buffers();
|
||||
if (s_input_dma)
|
||||
{
|
||||
destroy_dma_object(s_input_dma);
|
||||
s_input_dma = NULL;
|
||||
}
|
||||
s_input_dma = create_dma_object(sBuffer[2], kDataDevice, sLLIDMAInput[0], kDirectionIn, framesToProcess*channels*kDefaultSampleSize);
|
||||
if (s_output_dma)
|
||||
{
|
||||
destroy_dma_object(s_output_dma);
|
||||
s_output_dma = NULL;
|
||||
}
|
||||
s_output_dma = create_dma_object(sBuffer[0], kDataDevice, sLLIDMAOutput[0], kDirectionOut, framesToProcess*channels*kDefaultSampleSize);
|
||||
if (s_input_dma && s_output_dma)
|
||||
{
|
||||
setupInterruptHandler(s_input_dma, audiodevice_dma_int_handler, s_loopback_process);
|
||||
setupErrorHandler(s_input_dma, audiodevice_dma_int_err_handler, NULL);
|
||||
setupErrorHandler(s_output_dma, audiodevice_dma_int_err_handler, NULL);
|
||||
startDMAObject(s_input_dma);
|
||||
startDMAObject(s_output_dma);
|
||||
s_loopback_enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "could not create DMA objects!\n");
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (s_loopback_au)
|
||||
{
|
||||
Destroy_Loopback_AU(s_loopback_au);
|
||||
s_loopback_au = NULL;
|
||||
}
|
||||
dprintf(DEBUG_INFO, "creating loopback\n");
|
||||
s_loopback_au = Create_Loopback_AU(sampleRate, channels, kDefaultSampleSize);
|
||||
// create the loopback proessing with this sample rate
|
||||
if (s_loopback_au)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "starting up loopback_device\n");
|
||||
if (s_loopback_device)
|
||||
{
|
||||
destroy_loopback_device(s_loopback_device);
|
||||
s_loopback_device = NULL;
|
||||
}
|
||||
s_loopback_device = create_loopback_device(kDataDevice, s_loopback_au, channels * kDefaultSampleSize);
|
||||
if (s_loopback_device)
|
||||
{
|
||||
s_loopback_enabled = start_loopback_device(s_loopback_device);
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "could not create loopback device!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return s_loopback_enabled;
|
||||
}
|
||||
|
||||
|
||||
bool Check_Parameter_Message_Valid(IOPAUDIODSP_MODULE_COMMAND *command)
|
||||
{
|
||||
if(command->mModule != kIOPAUDIODSP_MODULE_LOOPBACK_PROCESSING)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Invalid kIOPAUDIODSP_OPCODE_SET_PARAMETER received!\n");
|
||||
return false;
|
||||
}
|
||||
if(!s_loopback_au)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "No existing loopback processing audio unit!\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Check_Property_Message_Valid(IOPAUDIODSP_MODULE_COMMAND *command)
|
||||
{
|
||||
if (!Check_Parameter_Message_Valid(command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
size_t max_size_for_property = sizeof(IOPAUDIODSP_Command) - ((uint32_t)&command->mProperty.mPropertyData - (uint32_t)command);
|
||||
//make sure its formatted correctly ...
|
||||
if(command->mProperty.mPropertySizeBytes > max_size_for_property)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Invalid message size received!\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Handle_OPCODE_GET_PARAMETER(IOPAUDIODSP_MODULE_COMMAND *command)
|
||||
{
|
||||
if (!Check_Parameter_Message_Valid(command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
|
||||
if (AudioUnit_GetParameter(s_loopback_au, command->mParameter.mParameterID, &command->mParameter.mParameterValue))
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "FAILED getting parameter(%d) on loopback process!\n", command->mParameter.mParameterID);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Handle_OPCODE_SET_PARAMETER(IOPAUDIODSP_MODULE_COMMAND *command)
|
||||
{
|
||||
if (!Check_Parameter_Message_Valid(command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
|
||||
if (AudioUnit_SetParameter(s_loopback_au, command->mParameter.mParameterID, command->mParameter.mParameterValue))
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "FAILED setting parameter(%d) on loopback process!\n", command->mParameter.mParameterID);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Handle_OPCODE_GET_PROPERTY(IOPAUDIODSP_MODULE_COMMAND *command)
|
||||
{
|
||||
if (!Check_Property_Message_Valid(command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint32_t max_size_for_property = sizeof(IOPAUDIODSP_Command) - ((uint32_t)&command->mProperty.mPropertyData - (uint32_t)command);
|
||||
if (AudioUnit_GetProperty(s_loopback_au, command->mProperty.mPropertyID, command->mProperty.mPropertyData, &max_size_for_property) )
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "FAILED getting property(%d) on loopback process!\n", command->mProperty.mPropertyID);
|
||||
return false;
|
||||
}
|
||||
command->mProperty.mPropertySizeBytes = max_size_for_property;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Handle_OPCODE_SET_PROPERTY(IOPAUDIODSP_MODULE_COMMAND *command)
|
||||
{
|
||||
if (!Check_Property_Message_Valid(command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
|
||||
if (AudioUnit_SetProperty(s_loopback_au, command->mProperty.mPropertyID, command->mProperty.mPropertyData, command->mProperty.mPropertySizeBytes))
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "FAILED setting parameter(%d) on loopback process!\n", command->mProperty.mPropertyID);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
audiodsp_message_process(void)
|
||||
{
|
||||
uint32_t message;
|
||||
IOPAUDIODSP_Command* command;
|
||||
|
||||
dprintf(DEBUG_SPEW, "@@ handling host message\n");
|
||||
|
||||
/* look to see if there's an item waiting for us */
|
||||
if (qwi_receive_item(audiodsp_channel, &message) == -1)
|
||||
return(false);
|
||||
|
||||
dprintf(DEBUG_SPEW, "@@ received audio message\n");
|
||||
|
||||
/* find the command structure based on the message */
|
||||
command = (IOPAUDIODSP_Command*)mem_static_map_cached(message);
|
||||
|
||||
/*
|
||||
* Flush any cached item contents we might have lying around - we are guaranteed
|
||||
* that the command size is a multiple of our cacheline size.
|
||||
*/
|
||||
platform_cache_operation(CACHE_INVALIDATE,
|
||||
(void *)command,
|
||||
sizeof(*command));
|
||||
|
||||
// assume every command is a failure unless it gets set to success
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_FAILURE;
|
||||
switch (command->iopaudiodsp.mOpcode) {
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_START_LOOPBACK_PROCESSING:
|
||||
{
|
||||
uint32_t sampleRate = kDefaultSampleRate;
|
||||
uint32_t channels = kDefaultNumChannels;
|
||||
uint32_t latency = kDefaultLatencyInUS;
|
||||
|
||||
// read values from the paramter block
|
||||
uint32_t* parameterBlock = (uint32_t *)command->start.mAdditionalParameters;
|
||||
int32_t parameterBlockSize = command->start.mAdditionalParametersSizeBytes;
|
||||
if (parameterBlockSize > 0)
|
||||
{
|
||||
sampleRate = *parameterBlock;
|
||||
sampleRate = (sampleRate < kMaxSampleRate) ? sampleRate : kMaxSampleRate;
|
||||
++parameterBlock;
|
||||
parameterBlockSize -= sizeof(parameterBlock[0]);
|
||||
}
|
||||
if (parameterBlockSize > 0)
|
||||
{
|
||||
latency = *parameterBlock;
|
||||
latency = (latency < kMaxLatencyInUS) ? latency : kMaxLatencyInUS;
|
||||
++parameterBlock;
|
||||
parameterBlockSize -= sizeof(parameterBlock[0]);
|
||||
}
|
||||
if (parameterBlockSize > 0)
|
||||
{
|
||||
channels = *parameterBlock;
|
||||
channels = (channels < kMaxNumChannels) ? channels : kMaxNumChannels;
|
||||
++parameterBlock;
|
||||
parameterBlockSize -= sizeof(parameterBlock[0]);
|
||||
}
|
||||
if (!sampleRate || !latency || !channels)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Some parameter not correct (sampleRate = %d, latency = %d, channels = %d)\n", sampleRate, latency, channels);
|
||||
break;
|
||||
}
|
||||
dprintf(DEBUG_CRITICAL, "using values sample rate %d, latency %d, channels %d\n", sampleRate, latency, channels);
|
||||
uint32_t bytesToProcess = ((sampleRate * latency * channels) / 1000000) * kDefaultSampleSize;
|
||||
// because we have an input buffer and process buffer, need to cut the value in half
|
||||
bytesToProcess /= 2;
|
||||
bytesToProcess = (bytesToProcess < kDefaultDMABufferSize) ? bytesToProcess : kDefaultDMABufferSize;
|
||||
// because the DMA will do burst-4, make sure we have a valid bytes to process size:
|
||||
uint32_t burstSize = kDefaultSampleSize * 4;
|
||||
bytesToProcess = ((bytesToProcess + burstSize - 1)/burstSize) * burstSize;
|
||||
bytesToProcess = (bytesToProcess < kDefaultDMABufferSize) ? bytesToProcess : kDefaultDMABufferSize;
|
||||
uint32_t framesToProcess = bytesToProcess / (channels * kDefaultSampleSize);
|
||||
dprintf(DEBUG_CRITICAL, "Start command with sample rate %d, channels %d, frames %d\n", sampleRate, channels, framesToProcess);
|
||||
|
||||
// now what would the latency be?
|
||||
latency = (framesToProcess * 1000000) / sampleRate;
|
||||
// because we had to half the latency, we need to double it.
|
||||
latency *= 2;
|
||||
|
||||
if (Start_Loopback_Processing(sampleRate, channels, framesToProcess))
|
||||
{
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
dprintf(DEBUG_CRITICAL, "Started loopback processing!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Could not start loopback processing!\n");
|
||||
}
|
||||
|
||||
// write values back to the paramter block
|
||||
parameterBlock = (uint32_t *)command->start.mAdditionalParameters;
|
||||
parameterBlockSize = command->start.mAdditionalParametersSizeBytes;
|
||||
if (parameterBlockSize > 0)
|
||||
{
|
||||
*parameterBlock = sampleRate;
|
||||
++parameterBlock;
|
||||
parameterBlockSize -= sizeof(parameterBlock[0]);
|
||||
}
|
||||
if (parameterBlockSize > 0)
|
||||
{
|
||||
*parameterBlock = latency;
|
||||
++parameterBlock;
|
||||
parameterBlockSize -= sizeof(parameterBlock[0]);
|
||||
}
|
||||
if (parameterBlockSize > 0)
|
||||
{
|
||||
*parameterBlock = channels;
|
||||
++parameterBlock;
|
||||
parameterBlockSize -= sizeof(parameterBlock[0]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_STOP_LOOPBACK_PROCESSING:
|
||||
{
|
||||
if (Stop_Loopback_Processing())
|
||||
{
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
dprintf(DEBUG_CRITICAL, "Stopped loopback processing!\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_GET_PROPERTY:
|
||||
{
|
||||
if (Handle_OPCODE_GET_PROPERTY(&command->module_command))
|
||||
{
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_SET_PROPERTY:
|
||||
{
|
||||
if (Handle_OPCODE_SET_PROPERTY(&command->module_command))
|
||||
{
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_GET_PARAMETER:
|
||||
{
|
||||
if (Handle_OPCODE_GET_PARAMETER(&command->module_command))
|
||||
{
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_SET_PARAMETER:
|
||||
{
|
||||
if (Handle_OPCODE_SET_PARAMETER(&command->module_command))
|
||||
{
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_INITTIMESTAMP:
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Timestamp buffer is 0x%x\n", command->init_timestamp.mTimeStamperBufferAddr);
|
||||
set_timestamper_message_buffer((IOPAUDIODSP_Command *)mem_static_map_cached(command->init_timestamp.mTimeStamperBufferAddr));
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case kIOPAUDIODSP_OPCODE_DO_TRANSFER:
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Starting Transfer\n");
|
||||
dprintf(DEBUG_CRITICAL, "Index %d\n", command->do_transfer.mIndex);
|
||||
dprintf(DEBUG_CRITICAL, "Direction %d\n", command->do_transfer.mDirection);
|
||||
dprintf(DEBUG_CRITICAL, "Do transfer %d\n", command->do_transfer.mDoTransfer);
|
||||
dprintf(DEBUG_CRITICAL, "Buffer boundary [0x%x,0x%x), starting at offset 0x%x\n", command->do_transfer.mBufferBegin, command->do_transfer.mBufferEnd, command->do_transfer.mBufferStart);
|
||||
|
||||
dprintf(DEBUG_CRITICAL, "Sample Rate %d, bytes per frame %d\n", command->do_transfer.mSampleRate, command->do_transfer.mBytesPerFrame);
|
||||
|
||||
bool doTransfer = command->do_transfer.mDoTransfer;
|
||||
uint32_t index = command->do_transfer.mIndex;
|
||||
if (s_loopback_process)
|
||||
{
|
||||
set_tap_point(s_loopback_process, index);
|
||||
}
|
||||
|
||||
if (doTransfer)
|
||||
{
|
||||
uint8_t *IOBufferBegin = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferBegin);
|
||||
uint8_t *IOBufferEnd = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferEnd);
|
||||
uint8_t *IOBuffer = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferStart);
|
||||
// unload and delete any tap points
|
||||
set_debug_tap(s_loopback_process, NULL, NULL);
|
||||
if (sInputTap)
|
||||
{
|
||||
destroy_debug_tap(sInputTap);
|
||||
sInputTap = NULL;
|
||||
}
|
||||
if (sOutputTap)
|
||||
{
|
||||
destroy_debug_tap(sOutputTap);
|
||||
sOutputTap = NULL;
|
||||
}
|
||||
// create the tap points
|
||||
if (command->do_transfer.mDirection & 0x1)
|
||||
{
|
||||
sInputTap = create_debug_tap(IOBufferBegin, IOBufferEnd, IOBuffer);
|
||||
}
|
||||
if (command->do_transfer.mDirection & 0x2)
|
||||
{
|
||||
sOutputTap = create_debug_tap(IOBufferBegin, IOBufferEnd, IOBuffer);
|
||||
}
|
||||
// set the tap points
|
||||
if (s_loopback_process)
|
||||
{
|
||||
set_debug_tap(s_loopback_process, sInputTap, sOutputTap);
|
||||
}
|
||||
}
|
||||
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintf(DEBUG_CRITICAL, "@@ ERROR: unrecognised audiodsp opcode 0x%x\n",
|
||||
command->iopaudiodsp.mOpcode);
|
||||
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_PARAM_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
dprintf(DEBUG_SPEW, "@@ done processing audiodsp message with status 0x%08x\n", command->iopaudiodsp.mStatus);
|
||||
|
||||
platform_cache_operation(CACHE_CLEAN,
|
||||
(void *)command,
|
||||
sizeof(IOPAUDIODSP_Command));
|
||||
|
||||
qwi_send_item(audiodsp_channel, message);
|
||||
|
||||
dprintf(DEBUG_SPEW, "@@ signaled completion of audiodsp message to host\n");
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
static void
|
||||
iop_audiodsp_sleep(int mode)
|
||||
{
|
||||
}
|
||||
|
||||
#if USE_DMA
|
||||
static void
|
||||
audiodevice_dma_int_handler_wFloat(void *arg)
|
||||
{
|
||||
sCurrentProcessBufferIndex = (sCurrentProcessBufferIndex+1) % kNumDMABuffers;
|
||||
process_data((loopback_process_t)arg, sBuffer[sCurrentProcessBufferIndex]);
|
||||
}
|
||||
|
||||
static void
|
||||
audiodevice_dma_int_handler(void *arg)
|
||||
{
|
||||
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
|
||||
//Enables floating point ops from the ISR
|
||||
enter_critical_section();
|
||||
arm_call_fpsaved(arg, &audiodevice_dma_int_handler_wFloat);
|
||||
exit_critical_section();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
audiodevice_dma_int_err_handler(void *arg)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "audiodevice_dma_int_err_handler!\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef _IOP_AUDIODSP_PROTOCOL_H_
|
||||
#define _IOP_AUDIODSP_PROTOCOL_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
* Command size is (somewhat) tunable.
|
||||
*
|
||||
* The principal consideration here is the maximum scatter/gather list size
|
||||
* this permits.
|
||||
*/
|
||||
#define kIOPAUDIODSP_COMMAND_SIZE (512)
|
||||
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_opcode_t: identifies the command sent and from the AE2
|
||||
*/
|
||||
typedef uint32_t IOPAUDIODSP_opcode_t;
|
||||
|
||||
#define kIOPAUDIODSP_OPCODE_UNKNOWN ((IOPAUDIODSP_opcode_t) 0)
|
||||
|
||||
/*
|
||||
* Commands for communicating with DSP library(ies)
|
||||
*/
|
||||
#define kIOPAUDIODSP_OPCODE_START_LOOPBACK_PROCESSING ((IOPAUDIODSP_opcode_t) 3)
|
||||
#define kIOPAUDIODSP_OPCODE_STOP_LOOPBACK_PROCESSING ((IOPAUDIODSP_opcode_t) 4)
|
||||
#define kIOPAUDIODSP_OPCODE_INITTIMESTAMP ((IOPAUDIODSP_opcode_t) 5)
|
||||
#define kIOPAUDIODSP_OPCODE_TIMESTAMP ((IOPAUDIODSP_opcode_t) 6)
|
||||
#define kIOPAUDIODSP_OPCODE_SET_PARAMETER ((IOPAUDIODSP_opcode_t) 7)
|
||||
#define kIOPAUDIODSP_OPCODE_GET_PARAMETER ((IOPAUDIODSP_opcode_t) 8)
|
||||
#define kIOPAUDIODSP_OPCODE_DO_TRANSFER ((IOPAUDIODSP_opcode_t) 9)
|
||||
#define kIOPAUDIODSP_OPCODE_SET_PROPERTY ((IOPAUDIODSP_opcode_t) 10)
|
||||
#define kIOPAUDIODSP_OPCODE_GET_PROPERTY ((IOPAUDIODSP_opcode_t) 11)
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_status_t: result from commands
|
||||
*/
|
||||
typedef uint32_t IOPAUDIODSP_status_t;
|
||||
|
||||
#define kIOPAUDIODSP_STATUS_UNKNOWN ((IOPAUDIODSP_status_t) 0)
|
||||
#define kIOPAUDIODSP_STATUS_SUCCESS ((IOPAUDIODSP_status_t) 1)
|
||||
#define kIOPAUDIODSP_STATUS_FAILURE ((IOPAUDIODSP_status_t) 0x80000000)
|
||||
#define kIOPAUDIODSP_STATUS_DEVICE_ERROR ((IOPAUDIODSP_status_t) 0x80000001)
|
||||
#define kIOPAUDIODSP_STATUS_DEVICE_TIMEOUT ((IOPAUDIODSP_status_t) 0x80000002)
|
||||
#define kIOPAUDIODSP_STATUS_DMA_TIMEOUT ((IOPAUDIODSP_status_t) 0x80000003)
|
||||
#define kIOPAUDIODSP_STATUS_PARAM_INVALID ((IOPAUDIODSP_status_t) 0x80000004)
|
||||
#define kIOPAUDIODSP_STATUS_UNIMPLEMENTED ((IOPAUDIODSP_status_t) 0x80000005)
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_module_t: dsp modules
|
||||
*/
|
||||
typedef uint32_t IOPAUDIODSP_module_t;
|
||||
|
||||
#define kIOPAUDIODSP_MODULE_LOOPBACK_PROCESSING ((IOPAUDIODSP_module_t) 1)
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_token_t: The opaque token to use for audio processing
|
||||
*/
|
||||
typedef uint32_t IOPAUDIODSP_token_t;
|
||||
|
||||
// Token for sending messages to the iop_audiodsp system itself
|
||||
#define kIOPAUDIODSP_system_token_t ((IOPAUDIODSP_opcode_t) 0xFFFFFFFF)
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_status_t result from commands
|
||||
*/
|
||||
struct _IOPAUDIODSP
|
||||
{
|
||||
IOPAUDIODSP_opcode_t mOpcode;
|
||||
IOPAUDIODSP_status_t mStatus;
|
||||
};
|
||||
typedef struct _IOPAUDIODSP IOPAUDIODSP;
|
||||
|
||||
/*
|
||||
* Messages. Message layout is dependent on the opcode. For instance, if
|
||||
* the opcode is kIOPAUDIODSP_OPCODE_START, then the message will be interpreted
|
||||
* as IOPAUDIODSP_START.
|
||||
* It is the senders responsiblity to make sure a message is set correctly.
|
||||
*/
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_START message
|
||||
* Message used to start a new audio process.
|
||||
* If message succeeds, iopToken will contain the token used for processing.
|
||||
* Fill the input and output with necessary parameters. Additional parameters
|
||||
* passed through the additional_paramters pointer.
|
||||
*/
|
||||
struct _IOPAUDIODSP_START
|
||||
{
|
||||
IOPAUDIODSP mIOPHeader;
|
||||
uint32_t mAdditionalParametersSizeBytes;
|
||||
uint8_t mAdditionalParameters[];
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_START IOPAUDIODSP_START;
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_STOP message
|
||||
* Message used to destroy a token.
|
||||
* If message succeeds, iopToken is no longer valid.
|
||||
*/
|
||||
struct _IOPAUDIODSP_STOP
|
||||
{
|
||||
IOPAUDIODSP mIOPHeader;
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_STOP IOPAUDIODSP_STOP;
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_INITTIMESTAMP message
|
||||
*/
|
||||
struct _IOPAUDIODSP_INITTIMESTAMP
|
||||
{
|
||||
IOPAUDIODSP mIOPHeader;
|
||||
uint32_t mTimeStamperBufferAddr;
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_INITTIMESTAMP IOPAUDIODSP_INITTIMESTAMP;
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_TIMESTAMP message
|
||||
*/
|
||||
struct _IOPAUDIODSP_TIMESTAMP
|
||||
{
|
||||
IOPAUDIODSP mIOPHeader;
|
||||
uint64_t mSampleCount;
|
||||
uint64_t mTimeStamp;
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_TIMESTAMP IOPAUDIODSP_TIMESTAMP;
|
||||
|
||||
/*
|
||||
* IOPAUDIODSP_DO_TRANSFER message
|
||||
*/
|
||||
struct _IOPAUDIODSP_DO_TRANSFER
|
||||
{
|
||||
IOPAUDIODSP mIOPHeader;
|
||||
uint32_t mIndex;
|
||||
uint32_t mDirection;
|
||||
uint32_t mDoTransfer;
|
||||
// send the address range of the buffer to work with, as well as where processing should start
|
||||
uint32_t mBufferBegin;
|
||||
uint32_t mBufferEnd;
|
||||
uint32_t mBufferStart;
|
||||
|
||||
// this are necessary if we do psuedo transfer by timer
|
||||
uint32_t mSampleRate;
|
||||
// this is necessary to report samples transferred
|
||||
uint32_t mBytesPerFrame;
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_DO_TRANSFER IOPAUDIODSP_DO_TRANSFER;
|
||||
|
||||
/*
|
||||
* Format of Parameters is (IOPAUDIODSP_PARAMETER_SUBCOMMAND) ...
|
||||
* {
|
||||
* parameter (4-bytes)
|
||||
* parameter_value (float)
|
||||
* }
|
||||
*
|
||||
*/
|
||||
struct _IOPAUDIODSP_PARAMETER_SUBCOMMAND
|
||||
{
|
||||
uint32_t mParameterID;
|
||||
float mParameterValue;
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_PARAMETER_SUBCOMMAND IOPAUDIODSP_PARAMETER_SUBCOMMAND;
|
||||
|
||||
/*
|
||||
* Format of Properties is (IOPAUDIODSP_PROPERTY_SUBCOMMAND) ...
|
||||
* {
|
||||
* Property (4-bytes)
|
||||
* Property_size (4-byte)
|
||||
* Property (void*)
|
||||
* }
|
||||
*
|
||||
*/
|
||||
struct _IOPAUDIODSP_PROPERTY_SUBCOMMAND
|
||||
{
|
||||
uint32_t mPropertyID;
|
||||
uint32_t mPropertySizeBytes;
|
||||
uint8_t mPropertyData[];
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_PROPERTY_SUBCOMMAND IOPAUDIODSP_PROPERTY_SUBCOMMAND;
|
||||
|
||||
struct _IOPAUDIODSP_MODULE_COMMAND
|
||||
{
|
||||
IOPAUDIODSP mIOPHeader;
|
||||
uint32_t mModule;
|
||||
union
|
||||
{
|
||||
IOPAUDIODSP_PROPERTY_SUBCOMMAND mProperty;
|
||||
IOPAUDIODSP_PARAMETER_SUBCOMMAND mParameter;
|
||||
};
|
||||
};
|
||||
typedef struct _IOPAUDIODSP_MODULE_COMMAND IOPAUDIODSP_MODULE_COMMAND;
|
||||
|
||||
union _IOPAUDIODSP_Command
|
||||
{
|
||||
IOPAUDIODSP iopaudiodsp;
|
||||
|
||||
IOPAUDIODSP_START start;
|
||||
IOPAUDIODSP_STOP stop;
|
||||
IOPAUDIODSP_INITTIMESTAMP init_timestamp;
|
||||
IOPAUDIODSP_DO_TRANSFER do_transfer;
|
||||
IOPAUDIODSP_TIMESTAMP timestamp;
|
||||
IOPAUDIODSP_MODULE_COMMAND module_command;
|
||||
UInt8 _pad[kIOPAUDIODSP_COMMAND_SIZE];
|
||||
};
|
||||
typedef union _IOPAUDIODSP_Command IOPAUDIODSP_Command;
|
||||
|
||||
|
||||
#endif // _IOP_AUDIODSP_PROTOCOL_H_
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include "loopback_device.h"
|
||||
|
||||
#include "ae2_mca.h"
|
||||
|
||||
#include <arch/arm/arm.h>
|
||||
#include <drivers/audio/audio.h>
|
||||
#include <debug.h>
|
||||
#include <stdlib.h>
|
||||
#include <platform/timer.h>
|
||||
|
||||
enum { kBurstSize = 4 };
|
||||
|
||||
typedef struct
|
||||
{
|
||||
audio_unit_t mAudioUnit;
|
||||
size_t mBytesPerFrame;
|
||||
uint32_t mErrorCount[kAudioDevice_Last];
|
||||
} internal_loopback_device_t;
|
||||
|
||||
|
||||
uint32_t readReg(uint32_t address)
|
||||
{
|
||||
return *(volatile uint32_t *)address;
|
||||
}
|
||||
|
||||
void writeReg(uint32_t address, uint32_t value)
|
||||
{
|
||||
*(volatile uint32_t *)address = value;
|
||||
}
|
||||
|
||||
uint32_t readMCA0Reg(uint32_t offset)
|
||||
{
|
||||
return readReg(rMCA0_BASE + offset);
|
||||
}
|
||||
|
||||
void writeMCA0Reg(uint32_t offset, uint32_t value)
|
||||
{
|
||||
writeReg(rMCA0_BASE + offset, value);
|
||||
}
|
||||
|
||||
|
||||
// if this function takes too long, bail. We cannot hang the system here.
|
||||
static const utime_t kMaxTime = 1000000;
|
||||
bool startPIO()
|
||||
{
|
||||
bool result = true;
|
||||
const utime_t time_out = system_time() + kMaxTime;
|
||||
|
||||
// disable TX and RX
|
||||
uint32_t MCATXCFG = readMCA0Reg(rMCATXCFG);
|
||||
writeMCA0Reg(rMCATXCFG, MCATXCFG & ~(1));
|
||||
uint32_t MCARXCFG = readMCA0Reg(rMCARXCFG);
|
||||
writeMCA0Reg(rMCARXCFG, MCARXCFG & ~(1));
|
||||
|
||||
// Flush the RX and TX fifos
|
||||
writeMCA0Reg(rMCACTL, 0x300);
|
||||
uint32_t MCACTL = readMCA0Reg(rMCACTL);
|
||||
while (result && (MCACTL & 0x300))
|
||||
{
|
||||
MCACTL = readMCA0Reg(rMCACTL);
|
||||
result = system_time() < time_out;
|
||||
}
|
||||
|
||||
// "Prime" the tx with zeros.
|
||||
uint32_t tx_fifo_level = (readMCA0Reg(rMCASTATUS)) & 0x3FF;
|
||||
while (result && (tx_fifo_level < 0x8))
|
||||
{
|
||||
writeMCA0Reg(rMCATXDATA, 0);
|
||||
tx_fifo_level = (readMCA0Reg(rMCASTATUS)) & 0x3FF;
|
||||
result = system_time() < time_out;
|
||||
}
|
||||
|
||||
// enable RX IRQ (we don't need TX IRQ, we only respond when we have RX samples)
|
||||
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
|
||||
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG | (1 << rMCAUNSRXCFG_IRQ_EN));
|
||||
|
||||
// enable TX and RX
|
||||
writeMCA0Reg(rMCATXCFG, MCATXCFG);
|
||||
writeMCA0Reg(rMCARXCFG, MCARXCFG);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool stopPIO()
|
||||
{
|
||||
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
|
||||
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG & ~((1 << rMCAUNSRXCFG_IRQ_EN)));
|
||||
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
|
||||
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG & ~((1 << rMCAUNSTXCFG_IRQ_EN)));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
handleAudioDeviceErrors(internal_loopback_device_t * This, const uint32_t status)
|
||||
{
|
||||
// <rdar://problem/13467070> Panic when setting AppleSongbirdDSP sidetone EQ
|
||||
// clear any sort of sticky-bit errors.
|
||||
const uint32_t errors = status & ( rMCASTATUS_FRAMEEEROR_MASK | rMCASTATUS_RXOVERRUN_MASK | rMCASTATUS_RXUNDERRUN_MASK | rMCASTATUS_TXOVERRUN_MASK | rMCASTATUS_TXUNDERRUN_MASK );
|
||||
writeMCA0Reg(rMCASTATUS, errors);
|
||||
if (errors && This)
|
||||
{
|
||||
if (status & rMCASTATUS_FRAMEEEROR_MASK) ++This->mErrorCount[kFrameError];
|
||||
if (status & rMCASTATUS_RXOVERRUN_MASK) ++This->mErrorCount[kRXOverrun];
|
||||
if (status & rMCASTATUS_RXUNDERRUN_MASK) ++This->mErrorCount[kRXUnderrun];
|
||||
if (status & rMCASTATUS_TXOVERRUN_MASK) ++This->mErrorCount[kTXOverrun];
|
||||
if (status & rMCASTATUS_TXUNDERRUN_MASK) ++This->mErrorCount[kTXUnderrun];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handleAudioDeviceInterrupt_wFloat(void *arg)
|
||||
{
|
||||
internal_loopback_device_t * This = (internal_loopback_device_t *)arg;
|
||||
const uint32_t status = readMCA0Reg(rMCASTATUS);
|
||||
handleAudioDeviceErrors(This, status);
|
||||
|
||||
// where the magic occurs
|
||||
if ((status & (1 << rMCASTATUS_RXHIGHWATER)))
|
||||
{
|
||||
// we do burst sizes of 16-bit data
|
||||
int16_t data[kBurstSize];
|
||||
for (size_t i = 0; i < kBurstSize; ++i)
|
||||
{
|
||||
data[i] = readMCA0Reg(rMCARXDATA);
|
||||
}
|
||||
if (This && This->mAudioUnit)
|
||||
{
|
||||
AudioUnit_Process(This->mAudioUnit, data, data, sizeof(data)/This->mBytesPerFrame);
|
||||
}
|
||||
// <rdar://problem/14296993>, only write if there is room in the TX fifo
|
||||
const uint32_t tx_fifo_level = status & 0x3FF;
|
||||
const uint32_t k_tx_fifo_size = readMCA0Reg(rMCAFIFOSIZE) & 0x3FF;
|
||||
if ((tx_fifo_level + kBurstSize) < k_tx_fifo_size)
|
||||
{
|
||||
for (size_t i = 0; i < kBurstSize; ++i)
|
||||
{
|
||||
writeMCA0Reg(rMCATXDATA, data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enable floating point code
|
||||
void handleAudioDeviceInterrupt(void * object)
|
||||
{
|
||||
enter_critical_section();
|
||||
arm_call_fpsaved(object, &handleAudioDeviceInterrupt_wFloat);
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// create a loopback_device that will read from device rx, optional process, and send to device tx
|
||||
loopback_device_t create_loopback_device(AudioDevice_Index device, audio_unit_t optional_au, size_t bytes_per_frame)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "Creating a loopback_device object\n");
|
||||
// hardcoding to support only MCA right now.
|
||||
if ((device != kMCA_0))
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
|
||||
return NULL;
|
||||
}
|
||||
internal_loopback_device_t *This = (internal_loopback_device_t*)malloc(sizeof(internal_loopback_device_t));
|
||||
if (This)
|
||||
{
|
||||
This->mAudioUnit = optional_au;
|
||||
This->mBytesPerFrame = bytes_per_frame;
|
||||
for (uint32_t i = kFrameError; i < kError_last; ++i)
|
||||
{
|
||||
This->mErrorCount[i] = 0;
|
||||
}
|
||||
// we can do this here repeatedly
|
||||
install_int_handler(AE2_INT_MCA0, handleAudioDeviceInterrupt, This);
|
||||
}
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
void destroy_loopback_device(loopback_device_t device)
|
||||
{
|
||||
// preliminary stop
|
||||
stop_loopback_device(device);
|
||||
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
|
||||
if (This)
|
||||
{
|
||||
free(This);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t getErrorCount(loopback_device_t dma, Error_Index which)
|
||||
{
|
||||
// preliminary stop
|
||||
internal_loopback_device_t *This = (internal_loopback_device_t*)dma;
|
||||
if (This)
|
||||
{
|
||||
return This->mErrorCount[which];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool start_loopback_device(loopback_device_t device)
|
||||
{
|
||||
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
|
||||
if (This && startPIO())
|
||||
{
|
||||
unmask_int(AE2_INT_MCA0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stop_loopback_device(loopback_device_t device)
|
||||
{
|
||||
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
|
||||
if (This)
|
||||
{
|
||||
mask_int(AE2_INT_MCA0);
|
||||
return stopPIO();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef __LOOPBACK_DEVICE_H__
|
||||
#define __LOOPBACK_DEVICE_H__
|
||||
|
||||
#include "iop_au_interface.h"
|
||||
|
||||
typedef void * loopback_device_t;
|
||||
|
||||
/*
|
||||
* loopback_device_t is an object that handles looping device rx to device tx
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
kI2S_0 = 0,
|
||||
kI2S_1,
|
||||
kI2S_2,
|
||||
kI2S_3,
|
||||
kMCA_0,
|
||||
kMCA_1,
|
||||
kAudioDevice_Last
|
||||
} AudioDevice_Index;
|
||||
|
||||
// create a loopback_device that will read from device rx, optional process, and send to device tx
|
||||
loopback_device_t create_loopback_device(AudioDevice_Index device, audio_unit_t optional_au, size_t bytes_per_frame);
|
||||
void destroy_loopback_device(loopback_device_t device);
|
||||
|
||||
typedef enum {
|
||||
kFrameError = 0,
|
||||
kRXOverrun,
|
||||
kRXUnderrun,
|
||||
kTXOverrun,
|
||||
kTXUnderrun,
|
||||
kError_last
|
||||
} Error_Index;
|
||||
|
||||
static const char* const kErrorTypeStr[kError_last] = {
|
||||
"Frame_Error",
|
||||
"RX_Overrun",
|
||||
"RX_Underrun",
|
||||
"TX_Overrun",
|
||||
"TX_Underrun",
|
||||
};
|
||||
|
||||
uint32_t getErrorCount(loopback_device_t device, Error_Index which);
|
||||
|
||||
// return true on success
|
||||
bool start_loopback_device(loopback_device_t device);
|
||||
bool stop_loopback_device(loopback_device_t device);
|
||||
|
||||
#endif /* __LOOPBACK_DEVICE_H__ */
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include "loopback_process.h"
|
||||
#include "timestamper.h"
|
||||
#include "debug_tap.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <AssertMacros.h>
|
||||
#include <platform.h>
|
||||
#include <platform/timer.h>
|
||||
#include <sys/task.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t mNumFrames;
|
||||
uint32_t mNumChannels;
|
||||
uint32_t mSampleSize;
|
||||
uint32_t mChannelBitmask;
|
||||
// the scratch buffer is used by the AudioUnit. We assume it needs
|
||||
// 8.24 Mono (channel 0) data
|
||||
audio_unit_t mAudioUnit;
|
||||
|
||||
// for debug: which index to send to user
|
||||
uint32_t mIndex;
|
||||
debug_tap_t mInputTap;
|
||||
debug_tap_t mOutputTap;
|
||||
|
||||
} internal_loopback_process_t;
|
||||
|
||||
|
||||
// XXX channel bitmask currently not implemented. Assuming audio data is packed.
|
||||
|
||||
loopback_process_t create_loopback_process(audio_unit_t au, uint32_t frames_to_process, uint32_t channels_to_process, uint32_t sample_size, uint32_t channel_bitmask)
|
||||
{
|
||||
internal_loopback_process_t *This = (internal_loopback_process_t*)malloc(sizeof(internal_loopback_process_t));
|
||||
if (This)
|
||||
{
|
||||
This->mNumFrames = frames_to_process;
|
||||
This->mNumChannels = channels_to_process;
|
||||
This->mSampleSize = sample_size;
|
||||
This->mChannelBitmask = channel_bitmask;
|
||||
This->mAudioUnit = au;
|
||||
This->mIndex = 0;
|
||||
This->mInputTap = NULL;
|
||||
This->mOutputTap = NULL;
|
||||
}
|
||||
return This;
|
||||
}
|
||||
|
||||
void destroy_loopback_process(loopback_process_t loopback)
|
||||
{
|
||||
internal_loopback_process_t *This = (internal_loopback_process_t*)loopback;
|
||||
free(This);
|
||||
}
|
||||
|
||||
void set_debug_tap(loopback_process_t loopback, debug_tap_t inputTap, debug_tap_t outputTap)
|
||||
{
|
||||
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
|
||||
// should we turn ints off for this?
|
||||
This->mInputTap = inputTap;
|
||||
This->mOutputTap = outputTap;
|
||||
}
|
||||
|
||||
void set_tap_point(loopback_process_t loopback, uint32_t index)
|
||||
{
|
||||
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
|
||||
// should we turn ints off for this?
|
||||
This->mIndex = index;
|
||||
}
|
||||
|
||||
void process_data(loopback_process_t loopback, void *processBuffer)
|
||||
{
|
||||
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
|
||||
|
||||
uint32_t sizeOfInput = This->mNumFrames * This->mNumChannels * This->mSampleSize;
|
||||
uint32_t amountToSend = 0;
|
||||
|
||||
// DebugTap1
|
||||
if (This->mInputTap && (This->mIndex == 1))
|
||||
{
|
||||
size_t this_amountToSend = send_to_tap(This->mInputTap, (uint8_t*)processBuffer, sizeOfInput);
|
||||
if (this_amountToSend) amountToSend = this_amountToSend;
|
||||
}
|
||||
|
||||
// DebugInput1
|
||||
if (This->mOutputTap && (This->mIndex == 1))
|
||||
{
|
||||
size_t this_amountToSend = get_from_tap(This->mOutputTap, (uint8_t*)processBuffer, sizeOfInput);
|
||||
if (this_amountToSend) amountToSend = this_amountToSend;
|
||||
}
|
||||
|
||||
AudioUnit_Process(This->mAudioUnit, processBuffer, processBuffer, This->mNumFrames);
|
||||
|
||||
// DebugTap3
|
||||
if (This->mInputTap && (This->mIndex == 3))
|
||||
{
|
||||
size_t this_amountToSend = send_to_tap(This->mInputTap, (uint8_t*)processBuffer, sizeOfInput);
|
||||
if (this_amountToSend) amountToSend = this_amountToSend;
|
||||
}
|
||||
|
||||
// DebugInput1
|
||||
if (This->mOutputTap && (This->mIndex == 3))
|
||||
{
|
||||
size_t this_amountToSend = get_from_tap(This->mOutputTap, (uint8_t*)processBuffer, sizeOfInput);
|
||||
if (this_amountToSend) amountToSend = this_amountToSend;
|
||||
}
|
||||
|
||||
if (amountToSend)
|
||||
{
|
||||
// Should this be done at the beginning?
|
||||
uint64_t timestamp = timer_get_ticks();
|
||||
send_timestamp(This->mNumFrames, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef _SPEAKER_PROT_H_
|
||||
#define _SPEAKER_PROT_H_
|
||||
|
||||
#include "debug_tap.h"
|
||||
#include "iop_au_interface.h"
|
||||
#include <platform.h>
|
||||
|
||||
typedef void * loopback_process_t;
|
||||
|
||||
/*
|
||||
* loopback process is an object that will process an input buffer in-
|
||||
* place, applying the passed Audio Unit to it.
|
||||
*
|
||||
* debug_taps can be added at specific tap points.
|
||||
*
|
||||
* It is assumed that the input number of samples to process matches
|
||||
* with the call to process.
|
||||
*
|
||||
*/
|
||||
|
||||
// channel bitmask is to allow cases when audio is not packed.
|
||||
loopback_process_t create_loopback_process(audio_unit_t au, uint32_t frames_to_process, uint32_t channels_to_process, uint32_t sample_size, uint32_t channel_bitmask);
|
||||
|
||||
void destroy_loopback_process(loopback_process_t loopback);
|
||||
|
||||
void set_debug_tap(loopback_process_t loopback, debug_tap_t inputTap, debug_tap_t outputTap);
|
||||
void set_tap_point(loopback_process_t loopback, uint32_t index);
|
||||
|
||||
// Assumes that input is as described when loopback created
|
||||
void process_data(loopback_process_t loopback, void *processBuffer);
|
||||
|
||||
#endif // _SPEAKER_PROT_H_
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Computer, Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Computer, Inc.
|
||||
#
|
||||
|
||||
#
|
||||
# Audio driver
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
OPTIONS += WITH_FUNCTION_AUDIODSP=1 \
|
||||
USE_SIDETONE=1
|
||||
|
||||
IOP_FUNCTIONS += AUDIODSP
|
||||
|
||||
ALL_OBJS += $(LOCAL_DIR)/iop_audiodsp.o \
|
||||
$(LOCAL_DIR)/loopback_process.o \
|
||||
$(LOCAL_DIR)/loopback_device.o \
|
||||
$(LOCAL_DIR)/timestamper.o \
|
||||
$(LOCAL_DIR)/debug_tap.o \
|
||||
$(LOCAL_DIR)/AudioUnits/iop_au_interface.o \
|
||||
$(LOCAL_DIR)/AudioUnits/AUSidetone.o \
|
||||
$(LOCAL_DIR)/AudioUnits/AUNull.o \
|
||||
|
||||
INSTALL_HEADERS += $(LOCAL_DIR)/iop_audiodsp_protocol.h
|
||||
|
||||
# Assuming 8 k for each task, with 2 task.
|
||||
# Assuming 16 k heap for au process
|
||||
# 32 k heap total
|
||||
export IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),32768)
|
||||
|
||||
GLOBAL_INCLUDES += $(SDKROOT)/usr/local/standalone/firmware \
|
||||
$(SDKROOT)/usr/local/standalone/firmware/include \
|
||||
$(LOCAL_DIR)/AudioUnits \
|
||||
$(LOCAL_DIR)/AE2_I2S \
|
||||
$(LOCAL_DIR)/AE2_MCA \
|
||||
$(LOCAL_DIR)/AE2_DMA \
|
||||
$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework/Headers \
|
||||
$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework/PrivateHeaders
|
||||
|
||||
PREBUILT_STATICLIBS += $(SDKROOT)/usr/local/standalone/firmware/libm.a \
|
||||
$(SDKROOT)/usr/local/standalone/firmware/libvDSP.a
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include "timestamper.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <AssertMacros.h>
|
||||
#include <platform.h>
|
||||
#include <platform/timer.h>
|
||||
#include <sys/task.h>
|
||||
|
||||
#include <iop.h>
|
||||
#include <qwi.h>
|
||||
#include <qwi_protocol.h>
|
||||
#include <EmbeddedIOPProtocol.h>
|
||||
#include <arch.h>
|
||||
#include <arch/arm/arm.h>
|
||||
#include <drivers/audio/audio.h>
|
||||
|
||||
#define kLogTimestamps 0
|
||||
|
||||
static IOPAUDIODSP_Command *timestamper_message_buffer;
|
||||
|
||||
void set_timestamper_message_buffer(IOPAUDIODSP_Command *buffer)
|
||||
{
|
||||
timestamper_message_buffer = buffer;
|
||||
}
|
||||
|
||||
static int timestamper_message_channel;
|
||||
static struct task_event timestamper_message_event;
|
||||
static struct task_event timestamper_runtask_event;
|
||||
|
||||
static int timestamper_task(void *cfg);
|
||||
static void timestamper_sleep(int mode);
|
||||
|
||||
IOP_FUNCTION(timestamper, timestamper_task, 8*1024, AUDIODSP_TIMER_CHANNEL);
|
||||
IOP_SLEEP_HOOK(timestamper, timestamper_sleep);
|
||||
|
||||
/*
|
||||
* Get a message to send to the Host
|
||||
*/
|
||||
static IOPAUDIODSP_Command *
|
||||
get_timestamper_message(utime_t allowed_delay)
|
||||
{
|
||||
utime_t deadline, t;
|
||||
int idx;
|
||||
|
||||
/* nothing we can do if we don't have a buffer */
|
||||
if (NULL == timestamper_message_buffer)
|
||||
return(NULL);
|
||||
|
||||
/* try to get a slot right away */
|
||||
if (-1 != (idx = qwi_next_send_index(timestamper_message_channel)))
|
||||
return(timestamper_message_buffer + idx);
|
||||
if (IOP_MESSAGE_NO_WAIT == allowed_delay)
|
||||
return(NULL);
|
||||
|
||||
/* spin waiting for a free slot */
|
||||
deadline = system_time() + allowed_delay;
|
||||
for (;;) {
|
||||
/* get the current time and see if we've timed out */
|
||||
t = system_time();
|
||||
if (t >= deadline)
|
||||
return(NULL);
|
||||
|
||||
/* sleep waiting for notification or timeout */
|
||||
event_wait_timeout(×tamper_message_event, deadline - t);
|
||||
|
||||
/* try again to get a message slot */
|
||||
if (-1 != (idx = qwi_next_send_index(timestamper_message_channel)))
|
||||
return(timestamper_message_buffer + idx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reclaim a message the host has accepted.
|
||||
*/
|
||||
static void
|
||||
timestamper_message_wakeup(void *arg __unused)
|
||||
{
|
||||
bool replies;
|
||||
uint32_t message;
|
||||
|
||||
/* reap message replies */
|
||||
replies = false;
|
||||
while (qwi_receive_item(timestamper_message_channel, &message) != -1)
|
||||
replies = true;
|
||||
|
||||
/* if we got at least one reply, wake anyone trying to send */
|
||||
if (replies)
|
||||
{
|
||||
event_signal(×tamper_message_event);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to send a trace message to the host.
|
||||
*/
|
||||
void
|
||||
send_timestamp(uint32_t samplesTransferred, uint64_t timestamp)
|
||||
{
|
||||
IOPAUDIODSP_Command *msg;
|
||||
|
||||
/* now get a message - wait a little while for it but not forever as we might deadlock the host */
|
||||
if (NULL != (msg = get_timestamper_message(1000))) {
|
||||
|
||||
/* populate the message */
|
||||
msg->timestamp.mIOPHeader.mOpcode = kIOPAUDIODSP_OPCODE_TIMESTAMP;
|
||||
msg->timestamp.mIOPHeader.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
|
||||
msg->timestamp.mSampleCount = samplesTransferred;
|
||||
msg->timestamp.mTimeStamp = timestamp;
|
||||
|
||||
#if kLogTimestamps
|
||||
dprintf(DEBUG_CRITICAL, "Timestamp %ull\n", timestamp);
|
||||
#endif
|
||||
|
||||
/* push it to memory */
|
||||
platform_cache_operation(CACHE_CLEAN, msg, sizeof(*msg));
|
||||
|
||||
/* and give it to the host */
|
||||
qwi_send_item(timestamper_message_channel, QWI_ENCODE_ORDINAL(msg - timestamper_message_buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "couldn't get a timestamp message\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
timestamper_task(void *cfg)
|
||||
{
|
||||
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
|
||||
|
||||
dprintf(DEBUG_SPEW, "@@ Timestamper task starting\n");
|
||||
|
||||
/* establish the host communications channel */
|
||||
event_init(×tamper_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
event_init(×tamper_runtask_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
dprintf(DEBUG_SPEW, "** opening audiodsp channel\n");
|
||||
timestamper_message_channel = qwi_instantiate_channel("audio timestamp",
|
||||
QWI_ROLE_PRODUCER,
|
||||
channel->ring_size,
|
||||
(void *)mem_static_map_cached(channel->ring_base),
|
||||
timestamper_message_wakeup,
|
||||
NULL);
|
||||
|
||||
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
|
||||
/* Little doubt we'll need VFP/Neon */
|
||||
arch_task_fp_enable(true);
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
dprintf(DEBUG_SPEW, "Waiting on run event\n");
|
||||
event_wait(×tamper_runtask_event);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void
|
||||
timestamper_sleep(int mode)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef _TIMESTAMPER_H_
|
||||
#define _TIMESTAMPER_H_
|
||||
|
||||
#include <platform.h>
|
||||
|
||||
#include "iop_audiodsp_protocol.h"
|
||||
|
||||
void set_timestamper_message_buffer(IOPAUDIODSP_Command *buffer);
|
||||
|
||||
void send_timestamp(uint32_t samplesTransferred, uint64_t timestamp);
|
||||
|
||||
#endif // _TIMESTAMPER_H_
|
||||
|
||||
Reference in new issue
Block a user