2015-11-25 20 views
6

Mam dwie klasy, MicrophoneHandler i AudioPlayer. Udało mi się wykorzystać AVCaptureSession wykorzystać dane mikrofon za pomocą zatwierdzonego odpowiedź here, ai konwertowane CMSampleBuffer do NSData użyciem tej funkcji:Odtwarzanie dźwięku z AVAudioPCMBuffer z AVAudioEngine

func sendDataToDelegate(buffer: CMSampleBuffer!) 
{ 
    let block = CMSampleBufferGetDataBuffer(buffer) 
    var length = 0 
    var data: UnsafeMutablePointer<Int8> = nil 

    var status = CMBlockBufferGetDataPointer(block!, 0, nil, &length, &data) // TODO: check for errors 

    let result = NSData(bytesNoCopy: data, length: length, freeWhenDone: false) 

    self.delegate.handleBuffer(result) 
} 

bym teraz chciał odtworzyć dźwięk przez głośnik przekształcając NSData produkowanego powyżej do AVAudioPCMBuffer i graj za pomocą AVAudioEngine. Moja AudioPlayer klasy wygląda następująco:

var engine: AVAudioEngine! 
var playerNode: AVAudioPlayerNode! 
var mixer: AVAudioMixerNode! 

override init() 
{ 
    super.init() 

    self.setup() 
    self.start() 
} 

func handleBuffer(data: NSData) 
{ 
    let newBuffer = self.toPCMBuffer(data) 
    print(newBuffer) 

    self.playerNode.scheduleBuffer(newBuffer, completionHandler: nil) 
} 

func setup() 
{ 
    self.engine = AVAudioEngine() 
    self.playerNode = AVAudioPlayerNode() 

    self.engine.attachNode(self.playerNode) 
    self.mixer = engine.mainMixerNode 

    engine.connect(self.playerNode, to: self.mixer, format: self.mixer.outputFormatForBus(0)) 
} 

func start() 
{ 
    do { 
     try self.engine.start() 
    } 
    catch { 
     print("error couldn't start engine") 
    } 

    self.playerNode.play() 
} 

func toPCMBuffer(data: NSData) -> AVAudioPCMBuffer 
{ 
    let audioFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.PCMFormatFloat32, sampleRate: 8000, channels: 2, interleaved: false) // given NSData audio format 
    let PCMBuffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity: UInt32(data.length)/audioFormat.streamDescription.memory.mBytesPerFrame) 

    PCMBuffer.frameLength = PCMBuffer.frameCapacity 

    let channels = UnsafeBufferPointer(start: PCMBuffer.floatChannelData, count: Int(PCMBuffer.format.channelCount)) 

    data.getBytes(UnsafeMutablePointer<Void>(channels[0]) , length: data.length) 

    return PCMBuffer 
} 

Bufor osiąga funkcję handleBuffer:buffer gdy self.delegate.handleBuffer(result) nazywa w pierwszym fragmencie powyżej.

Jestem w stanie uzyskać print(newBuffer) i zobaczyć lokalizacje w pamięci skonwertowanych buforów, ale nic nie wychodzi z głośników. Mogę sobie tylko wyobrazić, że coś nie jest spójne między konwersjami do i od NSData. Jakieś pomysły? Z góry dziękuję.

Odpowiedz

1

pominąć surowy NSData Format

Dlaczego nie skorzystać AVAudioPlayer całą drogę? Jeśli potrzebujesz pozytywnie NSData, zawsze możesz załadować takie dane z poniższej listy soundURL. W tym przykładzie bufor dysku jest podobny do:

let soundURL = documentDirectory.URLByAppendingPathComponent("sound.m4a") 

W każdym razie rozsądne jest nagrywanie bezpośrednio do pliku w celu optymalnego zarządzania pamięcią i zasobami. Otrzymasz NSData z nagrania w ten sposób:

let data = NSFileManager.defaultManager().contentsAtPath(soundURL.path()) 

Poniższy kod jest wszystko, czego potrzebujesz:

Rekord

if !audioRecorder.recording { 
    let audioSession = AVAudioSession.sharedInstance() 
    do { 
     try audioSession.setActive(true) 
     audioRecorder.record() 
    } catch {} 
} 

Odtwórz

if (!audioRecorder.recording){ 
    do { 
     try audioPlayer = AVAudioPlayer(contentsOfURL: audioRecorder.url) 
     audioPlayer.play() 
    } catch {} 
} 

konfiguracji

let audioSession = AVAudioSession.sharedInstance() 
do { 
    try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord) 
    try audioRecorder = AVAudioRecorder(URL: self.directoryURL()!, 
     settings: recordSettings) 
    audioRecorder.prepareToRecord() 
} catch {} 

Ustawienia

let recordSettings = [AVSampleRateKey : NSNumber(float: Float(44100.0)), 
    AVFormatIDKey : NSNumber(int: Int32(kAudioFormatMPEG4AAC)), 
    AVNumberOfChannelsKey : NSNumber(int: 1), 
    AVEncoderAudioQualityKey : NSNumber(int: Int32(AVAudioQuality.Medium.rawValue))] 

Pobierz Xcode projektu:

Można znaleźć tę samą przykład here. Pobierz pełny projekt, który nagrywa i gra na symulatorze i urządzeniu, od Swift Recipes.

+0

Potrzebujemy surowego formatu NSData do przesłania go przez gniazdo. Przesyłamy strumieniowo dźwięk. –

+0

plik 'NSData' pojawia się po wyjęciu z pudełka. Czy uważasz, że plik jest tylko buforem? Zdarza się, że jest to bufor dysku, ale wciąż jest to tylko bufor? – SwiftArchitect

+0

Jak usuwać bufor za każdym razem, gdy wysyłamy pakiet NSData przez gniazdo? @SwiftArchitect –

Powiązane problemy