2014-11-29 29 views
8

Sono davvero entusiasta del nuovo AVAudioEngine. Sembra un buon wrapper API intorno all'unità audio. Sfortunatamente la documentazione è finora inesistente e ho problemi a far funzionare un semplice grafico.Toccare Ingresso microfono utilizzando AVAudioEngine in Swift

Utilizzando il seguente codice semplice per impostare un grafico del motore audio, il blocco di tocco non viene mai chiamato. Imita alcuni dei codici di esempio che circolano sul web, sebbene anche quelli non funzionino.

let inputNode = audioEngine.inputNode 
var error: NSError? 
let bus = 0 

inputNode.installTapOnBus(bus, bufferSize: 2048, format: inputNode.inputFormatForBus(bus)) { 
    (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in 
    println("sfdljk") 
} 

audioEngine.prepare() 
if audioEngine.startAndReturnError(&error) { 
    println("started audio") 
} else { 
    if let engineStartError = error { 
     println("error starting audio: \(engineStartError.localizedDescription)") 
    } 
} 

Tutto quello che sto cercando è il buffer PCM prima per l'analisi. Non ho bisogno di effetti o output. Secondo il discorso del WWDC "502 Audio Engine in Practice", questa configurazione dovrebbe funzionare.

Ora se si desidera acquisire dati dal nodo di input, è possibile installare un nodo toccare e ne abbiamo parlato.

Ma ciò che è interessante di questo particolare esempio è, se volessi lavorare solo con il nodo di input, diciamo semplicemente catturare dati dal microfono e magari esaminarlo, analizzarlo in tempo reale o magari scriverlo in un file, io puoi installare direttamente un tocco sul nodo di input.

E il rubinetto eseguirà il lavoro di estrazione del nodo di input per i dati, inserendolo nei buffer e quindi restituendolo all'applicazione.

Una volta ottenuti questi dati, puoi fare tutto ciò che devi fare.

ecco alcuni link che ho provato:

  1. http://hondrouthoughts.blogspot.com/2014/09/avfoundation-audio-monitoring.html
  2. http://jamiebullock.com/post/89243252529/live-coding-audio-with-swift-playgrounds (SIGABRT nel parco giochi sulla startAndReturnError)

Edit: Questa è l'implementazione basata su suggerimento di Thorsten Karrer. Purtroppo non funziona.

class AudioProcessor { 
    let audioEngine = AVAudioEngine() 

    init(){ 
     let inputNode = audioEngine.inputNode 
     let bus = 0 
     var error: NSError? 

     inputNode.installTapOnBus(bus, bufferSize: 2048, format:inputNode.inputFormatForBus(bus)) { 
      (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in 
       println("sfdljk") 
     } 

     audioEngine.prepare() 
     audioEngine.startAndReturnError(nil) 
     println("started audio") 
    } 
} 

risposta

16

Potrebbe essere il caso che il vostro AVAudioEngine sta andando fuori portata e viene rilasciato da ARC ("Se ti è piaciuto, allora si dovrebbe avere messo a mantenere su di esso ...").

Il codice seguente (motore viene spostato in un ivar e attacca quindi intorno) spara rubinetto:

class AppDelegate: NSObject, NSApplicationDelegate { 

    let audioEngine = AVAudioEngine() 

    func applicationDidFinishLaunching(aNotification: NSNotification) { 
     let inputNode = audioEngine.inputNode 
     let bus = 0 
     inputNode.installTapOnBus(bus, bufferSize: 2048, format: inputNode.inputFormatForBus(bus)) { 
      (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in 
      println("sfdljk") 
     } 

     audioEngine.prepare() 
     audioEngine.startAndReturnError(nil) 
    } 
} 

(ho rimosso il trattamento per brevità errore)

+0

ho tentato questo e modificato la mia domanda con il codice utilizzato. Il codice nella tua risposta funziona per te? – brodney

+0

Jup, lavora per me. Non l'ho provato nel parco giochi ma funziona quando ho compilato una corsa da Xcode. Il blocco tap è chiamato continuamente. –

+0

Purtroppo devo ammettere che dopotutto era un oggetto non trattenuto. La mia classe AudioProcessor veniva istanziata in un metodo, non come una proprietà di classe. – brodney

2

La risposta di cui sopra non funzionava per me, ma il seguente ha fatto. Sto installando un tap su un nodo mixer.

 mMixerNode?.installTapOnBus(0, bufferSize: 4096, format: mMixerNode?.outputFormatForBus(0), 
    { 
     (buffer: AVAudioPCMBuffer!, time:AVAudioTime!) -> Void in 
      NSLog("tapped") 

    } 
    ) 
9

AGGIORNAMENTO: ho implementato un esempio completo di registrazione ingresso microfonico di lavoro, applicando alcuni effetti (riverberi, delay, distorsione) in fase di esecuzione, e salvare tutti questi effetti in un file di output.

var engine = AVAudioEngine() 
var distortion = AVAudioUnitDistortion() 
var reverb = AVAudioUnitReverb() 
var audioBuffer = AVAudioPCMBuffer() 
var outputFile = AVAudioFile() 
var delay = AVAudioUnitDelay() 

// inizializza il motore audio

func initializeAudioEngine() { 

    engine.stop() 
    engine.reset() 
    engine = AVAudioEngine() 

    isRealTime = true 
    do { 
     try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord) 

     let ioBufferDuration = 128.0/44100.0 

     try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(ioBufferDuration) 

    } catch { 

     assertionFailure("AVAudioSession setup error: \(error)") 
    } 

    let fileUrl = URLFor("/NewRecording.caf") 
    print(fileUrl) 
    do { 

     try outputFile = AVAudioFile(forWriting: fileUrl!, settings: engine.mainMixerNode.outputFormatForBus(0).settings) 
    } 
    catch { 

    } 

    let input = engine.inputNode! 
    let format = input.inputFormatForBus(0) 

    //settings for reverb 
    reverb.loadFactoryPreset(.MediumChamber) 
    reverb.wetDryMix = 40 //0-100 range 
    engine.attachNode(reverb) 

    delay.delayTime = 0.2 // 0-2 range 
    engine.attachNode(delay) 

    //settings for distortion 
    distortion.loadFactoryPreset(.DrumsBitBrush) 
    distortion.wetDryMix = 20 //0-100 range 
    engine.attachNode(distortion) 


    engine.connect(input, to: reverb, format: format) 
    engine.connect(reverb, to: distortion, format: format) 
    engine.connect(distortion, to: delay, format: format) 
    engine.connect(delay, to: engine.mainMixerNode, format: format) 

    assert(engine.inputNode != nil) 

    isReverbOn = false 

    try! engine.start() 
} 

// Ora la funzione di registrazione:

func startRecording() { 

    let mixer = engine.mainMixerNode 
    let format = mixer.outputFormatForBus(0) 

    mixer.installTapOnBus(0, bufferSize: 1024, format: format, block: 
     { (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in 

      print(NSString(string: "writing")) 
      do{ 
       try self.outputFile.writeFromBuffer(buffer) 
      } 
      catch { 
       print(NSString(string: "Write failed")); 
      } 
    }) 
} 

func stopRecording() { 

    engine.mainMixerNode.removeTapOnBus(0) 
    engine.stop() 
} 

Spero che questo potrebbe aiutare. Grazie!

+0

Funziona ma c'è un modo per farlo funzionare senza gli effetti che escono dall'altoparlante in tempo reale? – user3344977

+0

@ user3344977 Attualmente sto lavorando su un modulo simile che non ho avuto successo nell'implementazione. cioè il rendering offline di un file audio con effetti. Ecco un post che segue: [Rendering offline] (http://stackoverflow.com/a/15361251/2429443). Spero che questo aiuti –

+3

Da dove provengono questi valori ?: let ioBufferDuration = 128.0/44100.0 – Josh

0

bello argomento

hi Brodney

al tuo argomento trovo la mia soluzione. qui è soggetti simili Generate AVAudioPCMBuffer with AVAudioRecorder

vedono conferenza WWDC 2014 502 - AVAudioEngine nella cattura Practice microfono => in 20 min creare il buffer con il codice di tap => in 21 .50

qui è rapido 3 Codice

per fermare l'audio

func stopAudio() 
    { 

     let inputNode = audioEngine.inputNode 
     let bus = 0 
     inputNode?.removeTap(onBus: bus) 
     self.audioEngine.stop() 

    }