let’s start with core audio
DESCRIPTION
Core Audio is a technology that allows you to work with digital audio and is necessary for Apple applications that deal with audio capture, real-time effects, MP3 playback , virtual instruments , web radio , voice over IP and more.Core Audio is difficult, so much so that the words "easy" and "Core Audio" cannot be used in the same sentence, but it is equally true that several of the best Apps for iPhone have to do with sound .In this introductory session I will illustrate the techniques and tools to make it less difficult to deal with this set of frameworks in iOS. In reviewing the various frameworks I will explain when to use Core Audio and when not to use it, I will discuss in particular two of the three Core Audio engines : Audio Queue and Audio Unit . Finally, I will discuss some code for recording a sound and apply effects to it . Your journey towards Core Audio starts here .TRANSCRIPT
Francesco SinopoliProject Manager & Software Developer
http://it.linkedin.com/in/[email protected]
Let’s start with Core Audio
Outline
•Audio Technologies su iOS
• Il Suono
•Audio Queue Services
•Audio Unit
Parleremo di:
Media su iOSAudio Overview
Audio Overview
•Media Player framework: iPod library search and playback
•AV Foundation: Obj-C wrapper for audio file playback and recording
•Core Audio: low level audio streaming
Quali sono le alternative a Core Audio?
iOS offre il seguente set di tools organizzati in frameworks in base alla features che forniscono:
Audio OverviewUsiamo Media Player per eseguire song, audio book,
podcast dalla user’s iPod library
//Import the umbrella header file for the Media Player framework#import <MediaPlayer/MediaPlayer.h>
Audio OverviewUsiamo AV Foundation per eseguire e registrare
audio utilizzando un semplice interfaccia Objective-C
#import <AVFoundation/AVFoundation.h>
Audio OverviewAVAsset is the core class in the AV Foundation
framework
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
What is Core Audio?Core Audio è il motore che c’è dietro ogni suono
riprodotto su Mac OS X e iOS
What is Core Audio?Punti di vista
• Da un punto di vista audio Core Audio è “ad alto livello” perchè astrae sia l’hardware che il formato audio
• Da un punto di vista del developer appare essere a “basso livello” perché si utilizzano API C-based
Core Audio
•Non usarlo per ... eseguire Video
•Per accedere alla user iPod Library
•Per eseguire un file audio (AVAudioPlayer)
•Per registare un suono (AVAudioRecorder)
Quando non utilizzare Core Audio
“You should typically use the highest-level abstraction available that allows you to perform the tasks you want”
Core Audio
•Quando vogliamo fare qualcosa direttamente con i dati audio. Cioè, quando abbiamo la necessità di processare il suono in tempo reale; vogliamo mixare, misurare (i decibel e.g.), eseguire effetti sull’audio, etc.
•Quando come requisiti vengono richieste high performance e low latency
...allora quando?
Core Audio
•Engines
•Helpers
Core Audio è un set di framework per lavorare con audio digitale
È possibile suddividere questo set di frameworks in due gruppi:
Core Audio
•Audio Units: sono un API per la cattura e l’esecuzione di dati audio, sono caratterizzate da una bassa latenza, ci consentono di produrre effetti ed effettuare mixing. Sono utilizzabili più audio unit, ciascuno con il suo compito, connessi tra di loro come in un grafo. E altro ancora
•Audio Queues: anche loro sono un API per registrare ed eseguire audio, costruite sopra le audio unit. Sono utilizzate mediante un meccanismo di callback che fornisce o riceve audio, buffers di audio della dimensione desiderata. Hanno una latenza maggiore delle audio unit ma supportono formati compressi
•OpenAL: è un API per l’audio 3D, implementata sulle audio unit. Sono utilizzate soprattutto per il gaming
Le engine API le utilizziamo per processare lo stream audio
Core Audio
• Audio File: ci consentono di leggere da e scrivere su diversi tipi di file audio astraendo dal loro contenuto. Ci consentono anche di recuperare metadati come durata, iTunes info, etc.
• Audio File Stream: ci consentono di leggere da un stream network e di scoprire al volo l’encoding di un file
• Audio Converter: ci consentono di convertire buffer di audio tra differenti encoding
• ExtAudioFile: combinano le features di Audio File e Audio Converter; sono un’interfaccia unificata per leggere e scrivere file audio compressi e linear PCM
• Audio Session: giocano un ruolo decisivo nell’ambito del comportamento audio della nostra app (sostituite con le AVAudioSession)
Le helper API le utilizziamo per lavorare i dati o veicolarli attraverso gli engine
Core Audio
• iOS gestisce il comportamento audio a livello di app, inter-app e device usando il concetto di Audio Session
• Tramite le Audio Session rispondiamo a diverse questioni sul comportamento della nostra app
• L’audio environment su un device iOS è abbastanza complesso
•Un Audio Session incapsula un set di comportamenti; ogni set di comportamenti è identificato da una chiave detta categoria
•Configurando questa chiave stabiliamo come l’audio deve essere gestito nella nostra app
Audio Session: take notes
Core Audio Learning Curve
Il suonoAudio digitale 101
About the sound
•Segnale analogico continuo vs natura digitale del computer
•Campionamento del segnale: come i campioni sono rappresentati e organizzati in forma digitale
•Problemi quando trattiamo audio digitale: buffer (e relativa latenza), formati (vari tipi di formato audio digitale), etc.
Suono: questioni principali
About the sound
About the sound
About the sound
About the sound
Nyquist-Shannon
“per non alterare il contenuto in frequenza di un segnale la frequenza di campionamento deve essere maggiore del doppio della frequenza massima contenuta nel segnale”
About the soundIl problema fondamentale della fedeltà digitale consiste nel fare la migliore approssimazione
considerati i limiti hardware
Core Audio glossary•Sample: rappresenta l’ampiezza dell’onda
•Sample rate: è il numero di sample catturati per ogni secondo di audio. Si misura in sample per secondo
•Bit depth: È il numero di bit di informazione per ogni sample. Si può parlare di risoluzione del sample. Si misura in bit per sample
•Bit rate: è il numero di bit richiesti per descrivere un secondo di audio. Si misura in bit per secondo, corrisponde al prodotto tra bit depth e sample rate
•Frame: è un bundle che combina un sample per canale. Un frame rappresenta tutti i canali audio in un dato momento di tempo. Per il suono mono: un frame ha un sample; per il suono stereo: un frame ha due sample
Audio Queue ServicesCore Audio Engines
Audio Queue
•AudioToolbox framework
•Higest level playback and recording API (pure C interface) in Core Audio
Sono la tecnologia raccomandata per aggiungere semplici feature di registrazione e playback alla
nostra app
Audio QueueLe Audio Queue service hanno un semplice modello per catturare o eseguire un audio. Semplice perché ci consente di non preoccuparci della complessità dei dati audio o dell’hardware sottostante
Agli estremi abbiamo un trasduttore (microfono o speaker)
Lo stream continuo di dati è rappresentato da una coda (di buffer)
I buffer sono riempiti con i dati che provengono dall’hardware e che poi vengono passati alla funzione di callback
A recorder audio queue A playback audio queue
Example
•Catturiamo e registriamo l’audio
•Catturiamo l’audio e applichiamo un effetto
Example
DEMO
Audio Queue - RecorderAudio Queue non è un API per registrare ed eseguire un suono ma è a un livello più basso
• Configurare il formato audio che vogliamo utilizzare, AudioStreamBasicDescription
• Creare un audio queue, AudioQueueNewInput()
• Fornire una funzione di callback che processerà l’audio in entrata
• Avviare l’audio queue, AudioQueueStart()
• Terminare l’audio queue, AudioQueueStop()
Audio Queue - Recorder
Quando inizia a registrare l’audio queue riempie un buffer con i primi dati acquisiti
L’audio queue invia questi buffer alla funzione di callback nello stesso ordine in cui sono stati acquisiti
La funzione di callback dopo avere utilizzato il buffer lo rimette a disposizione dell’audio queue per il suo riuso
Example: Capture + Effect
CAStreamBasicDescription recordFormat; recordFormat.mFormatID = kAudioFormatLinearPCM; recordFormat.mChannelsPerFrame = 2; recordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; recordFormat.mBitsPerChannel = 16; recordFormat.mBytesPerPacket = (recordFormat.mBitsPerChannel / 8) * recordFormat.mChannelsPerFrame; recordFormat.mBytesPerFrame = (recordFormat.mBitsPerChannel / 8) * recordFormat.mChannelsPerFrame; recordFormat.mFramesPerPacket = 1; recordFormat.mSampleRate = 8000;
Formato Crea ControllaInizializza e avvia
AudioQueueRef queue = {0}; AudioQueueNewInput(&_mRecordFormat, audioQueueBufferHandler, (__bridge void*) self /* userData */, NULL /* run loop */, NULL /* run loop mode */, 0 /* flags */, &queue);
Example: Capture + EffectFormato Crea ControllaInizializza e avvia
#define kNumbRecordBuffers!3#define kBufferDurationSecond .5
// enough bytes for half a secondbufferByteSize = 32000;!for (i = 0; i < kNumbRecordBuffers; ++i) { AudioQueueAllocateBuffer(_audioQueue, bufferByteSize, &_mBuffers[i]); AudioQueueEnqueueBuffer(_audioQueue, _mBuffers[i], 0, NULL);}
AudioQueueStart(_audioQueue, NULL)
Example: Capture + EffectFormato Crea ControllaInizializza e avvia
// AudioQueue callback function, called when an input buffers has been filled.void audioQueueBufferHandler(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc){! PMMAQCapture *capture = (__bridge PMMAQCapture *)inUserData;! try {! ! if (inNumPackets > 0) {! ! ! // TO DO Something AudioQueueEnqueueBuffer(capture.audioQueue, inBuffer, 0, NULL);! ! }! ! } catch (CAXException e) {! ! char buf[256];! ! fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));! }}
Example: Capture + EffectFormato Crea ControllaInizializza e avvia
Set up size of the buffers
How big the buffers should be?
• Format, ASBD (bit rate, bit depth,...)• (Buffer) duration in seconds• Audio Queue, kAudioConverterPropertyMaximumOutputPacketSize (compressed data)
8000 x 1 x 2 x 1 = 16000 bytes
samples/seconds channels
bytes/channel seconds
Example: Capture + Effect
Write to a file
@property (nonatomic, assign) AudioFileID recordFile;@property (nonatomic, assign) SInt64 recordPacket;
...
NSString *recordFile = [NSTemporaryDirectory() stringByAppendingPathComponent:(NSString*)CFSTR("capture.caf")]; CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)recordFile, NULL); AudioFileCreateWithURL(url, kAudioFileCAFType, &recordFormat, kAudioFileFlags_EraseFile, &_recordFile);...AudioFileWritePackets(capture.recordFile, FALSE, inBuffer->mAudioDataByteSize, inPacketDesc, capture.recordPacket, &inNumPackets, inBuffer->mAudioData);capture.recordPacket += inNumPackets;
Declaration
Set Up
Callback
Example: Capture + Effect
Property-driven nature of the Audio Queue
•kAudioQueueProperty_EnableLevelMetering
•kAudioQueueProperty_CurrentLevelMeter(DB)
Example: Capture + Effect
UInt32 val = 1;XThrowIfError(AudioQueueSetProperty(_audioQueue, kAudioQueueProperty_EnableLevelMetering, &val, sizeof(UInt32)), "couldn't enable metering");
Example: Capture + Effect
UInt32 data_sz = sizeof(AudioQueueLevelMeterState) * 1; //[_channelNumbers count];OSErr status = AudioQueueGetProperty(self.audioQueue, kAudioQueueProperty_CurrentLevelMeterDB, self.chan_lvls, &data_sz);
@property (nonatomic, assign) AudioQueueLevelMeterState! *chan_lvls;...
...
...
float value = (float)(self.chan_lvls[0].mAveragePower);
[self.delegate recorderEngine:self levelMeterChanged:value];
Level meteringDeclaration
Set Up
Callback | Timer
Audio QueuePlayback
Audio UnitCore Audio Engines
Audio UnitNelle puntate precedenti
• iOS offre più tecnologie per gestire l’audio digitale: Media Player, AVFoundation, Core Audio. Ciascuna di queste tecnologie deve essere utilizzata in presenza di specifiche necessità
• Core Audio contiene tre engine per processare uno stream di dati audio (Audio Unit, Audio Queue e OpenAL). Audio Unit è l’engine principale, Audio Queue e OpenAL sono costruiti su di esso
Audio Unit“One engine to rule them all, one engine to find them, one engine to bring them all and in the darkness bind
them”
Audio UnitAll audio technologies in iOS are build on top of
audio units
Audio UnitBenvenuti nella funzionalità di più basso livello di
Core Audio
• Non è possibile, per un dev iOS, andare più vicini all’hardware
• È possibile lavorare con i dati audio raw come non è possibile fare a più alti livelli di astrazione
• È possibile sintetizzare l’audio, eseguire effetti su stream audio, catturare il suono dal microfono, combinare tutte queste cose tra di loro e farne altre ancora (ogni audio unit offre delle funzionalità particolari)
Audio Unit
• Eccellente reattività
• Riconfigurazione dinamica
Quando le utilizziamo
Audio Unit
• Audio Unit consente di lavorare con un meccanismo di plug-in; ogni singolo Audio Unit può inserirsi in una catena di processamento audio (grafo)
• Ci viene in aiuto un helper API: AUGraph
AUGraph
Audio UnitAudio Units life cycle
• Instantiate
• Configure: qui configureremo l’Audio Unit come richiesto dal tipo utilizzato al fine di raggiungere lo scopo della nostra app;
• Initialize: qui prepareremo l’Audio Unit a gestire l’audio che transiterà da esso
• Start
• Control: consideriamo che, ad un alto livello siamo abituati a passare un URL ad un player o ad un recorder; ad un Audio Unit level lavoriamo con funzioni di callback che sono chiamate centinaia di volte al secondo
• Clean
Audio UnitCreare un Audio Unit
• Type
• Subtype
• Manufacturer
L’audio unit è creato tramite tre codici:
questa tripletta identifica univocamente un audio unit
Audio Unit
Nel caso di più audio unit è necessario creare le connessioni tra di loro prima di avviare il processo audio
Creare un Audio Unit
Audio UnitAudio Unit è un oggetto software che esegue un
certo tipo di lavoro su uno stream audio.
• Generator unit: creano uno stream di audio da qualche sorgente, come file, network o memoria
• Music unit: sono simili ai generator ma producono uno stream di audio sintetizzato da un MIDI data
• Mixer unit (Multichannel mixer, 3D mixer): combinano multipli stream in uno o più stream
• Effect unit (iPodEQ, Delay, ...): eseguono qualche tipo di processamento del segnale audio su uno stream
• Converter unit (Format Converter): esegue trasformazioni che non sono rivolte all’utente finale ma piuttosto a conversioni tra diversi varietà di PCM (ad esempio, cambia il sample rate o il bit depth)
• Output unit (Remote I/O, Voice Processing I/O, Generic Output): fanno anche da input. Sono delle interfacce con l’audio input e output hardware, ci consentono di catturare l’audio dal microfono o eseguirlo attraverso le casse
Audio UnitScope: è un contesto all’interno di audio unit
Element: chiamato anche bus, è un contesto all’interno di un audio unit scope
Audio Unit
I/O Unit: è un’astrazione sull’audio hardware (From hardware To hardware). Ogni elemento ha un input scope e un output scope
Input element =
Element 1
Output element =
Element 0
Come avviene il flusso del segnale audio?
Audio Unit
Riceviamo audio dall’output scope dell’element di input e inviamo audio all’input scope dell’elemento di output
Come avviene il flusso del segnale audio?
Audio Unit
Audio Unit
Example
• Creiamo un audio unit per la cattura dell’audio dal microfono e mostriamo come applicare effetti anche senza utilizzare un effect unit di sistema fornito da iOS
• Come facciamo ad eseguire effetti “manualmente”? Collezioniamo i sample nell’output scope del bus 1 di un I/O unit e, dopo averli processati, forniamo questi sample, per essere eseguiti, all’input scope del bus 0 dello stesso I/O unit
Catturiamo l’audio e applichiamo un effetto
Example
DEMO
• Direct connection su bus 0/input scope e bus 1/output scope.
• AUGraph con un solo nodo
• Render callback function sul bus 0/input scope all’interno della quale effettuare il pull sul bus 1/output scope
Example: Capture + Effect
AudioUnit rioUnit; ... AudioComponentDescription audioComponentDesc; audioComponentDesc.componentType = kAudioUnitType_Output; audioComponentDesc.componentSubType = kAudioUnitSubType_RemoteIO; audioComponentDesc.componentManufacturer = kAudioUnitManufacturer_Apple; audioComponentDesc.componentFlags = 0; audioComponentDesc.componentFlagsMask = 0; AudioComponent rioComponent = AudioComponentFindNext(NULL, &audioComponentDesc); AudioComponentInstanceNew(rioComponent, &rioUnit);
Example: Capture + Effect
Crea Configura ControllaInizializza e avvia
UInt32 onFlag = 1;
AudioUnitElement bus1 = 1; AudioUnitSetProperty(rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, bus1, &onFlag, sizeof(onFlag));
AudioUnitElement bus0 = 0; AudioUnitSetProperty(rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, bus0, &onFlag, sizeof(onFlag));
Example: Capture + Effect
Crea Configura ControllaInizializza e avvia
AudioStreamBasicDescription customASBD; memset(&customASBD, 0, sizeof(customASBD)); customASBD.mSampleRate = hardwareSampleRate; customASBD.mFormatID = kAudioFormatLinearPCM; customASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; //kAudioFormatFlagsCanonical; //which means that you'll be working with signed integer samples customASBD.mBytesPerPacket = 2; //4; customASBD.mFramesPerPacket = 1; customASBD.mBytesPerFrame = 2; //4; customASBD.mChannelsPerFrame = 1; //mono 2 for stereo! customASBD.mBitsPerChannel = 16;
Example: Capture + Effect
Crea Configura ControllaInizializza e avvia
//Set ASBD for output (bus 0) on the RIO's input scope AudioUnitSetProperty(rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, bus0, &customASBD, sizeof(customASBD)); //Set ASBD for mic input (bus 1) on the Rio's output scope AudioUnitSetProperty(rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, bus1, &customASBD, sizeof(customASBD));
Example: Capture + Effect
Crea Configura ControllaInizializza e avvia
Example: Capture + EffectConfigure: set stream formats
// Set render proc to supply samples from input unit AURenderCallbackStruct callbackStruct; callbackStruct.inputProc = InputRenderCallback; callbackStruct.inputProcRefCon = (__bridge void*)self; AudioUnitSetProperty(rioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, bus0, &callbackStruct, sizeof(callbackStruct));
Crea Configura ControllaInizializza e avvia
Example: Capture + Effect
//initialize and start Rio unitAudioUnitInitialize(_effectState.rioUnit);AudioOutputUnitStart(_effectState.rioUnit); //now, RemoteIO Unit starts making callback to a //function called inputRenderCallback
Example: Capture + Effect
Crea Configura ControllaInizializza e avvia
static OSStatus InputRenderCallback (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){ EffectClass *effectClass = (EffectClass*) inRefCon; // Just copy samples UInt32 bus1 = 1; AudioUnitRender(effectClass->rioUnit, ioActionFlags, inTimeStamp, bus1, inNumberFrames, ioData); roboticVoice(effectClass, ioData, inNumberFrames); return noErr;
}
Example: Capture + Effect
Crea Configura ControllaInizializza e avvia
Ehy, you can find other DSP stuff on that site: http://musicdsp.org/
Finito?
Example: Capture + Effect
Crea Configura ControllaInizializza e avvia
Audio AppCose da fare per un app sounds
• Configure Your Audio Session
• Categorize your application
• Respond to interruptions
• Handle routing changes
Audio App
AVAudioSession *appSession = [AVAudioSession sharedInstance];
//we want to avoid sample rate conversion (CPU intensive); 44.100 Hertz [appSession setPreferredSampleRate:44100.0 error:nil]; [appSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[appSession setActive:YES error:nil];
//set up AudioQueue, AVAudioPlayer or Audio Unit, etc.
Set Up the session
Audio AppHandle interruptions
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleInterruption:) name: AVAudioSessionInterruptionNotification object: [AVAudioSession sharedInstance] ];
- (void) handleInterruption:(NSNotification*)notification{ NSDictionary *interruptionDict = notification.userInfo; NSUInteger interruptionType = (NSUInteger)[interruptionDict valueForKey:AVAudioSessionInterruptionTypeKey]; if (interruptionType == AVAudioSessionInterruptionTypeBegan) ... else if (interruptionType == AVAudioSessionInterruptionTypeEnded){ ... } }
Audio AppHandle route changes
//Posted on the main thread when the system’s audio route changes. [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleRouteChanging:) name: AVAudioSessionRouteChangeNotification object: [AVAudioSession sharedInstance]];
- (void)handleRouteChanging:(NSNotification*)notification{ UInt8 reasonValue = [[notification.userInfo valueForKey: AVAudioSessionRouteChangeReasonKey] intValue]; switch (reasonValue) { case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: break; case AVAudioSessionRouteChangeReasonWakeFromSleep: break; case AVAudioSessionRouteChangeReasonOverride: break; case AVAudioSessionRouteChangeReasonCategoryChange: break; case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: break; case AVAudioSessionRouteChangeReasonNewDeviceAvailable: break; case AVAudioSessionRouteChangeReasonUnknown: default: break; }}
Resources
•Apple documentation
•Learning Core Audio - (Adamson-Avila)
•Coreaudio-api mailing list
Thank you
The End