2015-09-18 17 views
10

Daha büyük bir uygulamanın parçası olarak bir metronom oluşturuyorum ve tek sesler olarak kullanacağım birkaç çok kısa wav dosyasına sahibim. Ben NSTimer önemli gecikme sorunları vardır ve Core Audio Swift uygulamak için oldukça zor olduğu için AVAudioEngine kullanmak istiyorum. Aşağıdakileri deniyorum, ancak şu anda ilk 3 adımı uygulayamıyorum ve daha iyi bir yol olup olmadığını merak ediyorum.Düşük gecikmeli metronom için ses zamanlamak üzere AVAudioEngine kullanma

Kod anahat:

  1. metronom mevcut ayarları (vuruşa barda başına atım ve alt bölümlerin sayısı; yener için dosya A, alt bölümleri için dosya B) 'ye göre dosya URL'lerin dizisi yaratmak Programlı
  2. tek AudioBuffer içine bu dosyaları okuyun veya tempo ve dosyaların uzunluğuna dayalı sessizlik çerçevelerin uygun numarası ile bir wav dosyası, oluşturma ve seslerin her biri arasında diziye eklemek AudioBufferList
  3. ,

Şimdiye kadar tek bir ses dosyasının bir döngü tamponu (adım 4) oynamak mümkün olmuştur ama dosyaların bir diziden bir tampon oluşturmak veya programlı sessizlik oluşturmak için edemedik ne de Bunu ele alan StackOverflow hakkında herhangi bir cevap buldum. Bu yüzden, bunun en iyi yaklaşım olmadığını düşünüyorum.

Soruma soru: AVAudioEngine kullanarak düşük gecikme süresine sahip bir dizi ses zamanlamak ve sonra bu diziyi döngülemek mümkün mü? Değilse, Swift kodlarken sesleri zamanlamak için hangi çerçeve/yaklaşım en uygun?

+0

Bunun yardımcı olup olmadığından emin değilsiniz, ancak [TheAmazingAudioEngine] 'ı (https://github.com/TheAmazingAudioEngine/TheAmazingAudioEngine) deneyin. Bu objektif c'de yazılmıştır, ancak kısa bir süre içinde TAAE'ye baktığım hızlı bir şekilde –

+0

10'da bir çerçeve olarak kullanılabilir ve en iyi seçenek olabilir, ancak daha doğal bir yaklaşım olduğunu umuyorum. – blwinters

cevap

2

Dosyadan ses içeren bir arabellek ve gerekli uzunlukta sessizlik yapabildim.

// audioFile here – an instance of AVAudioFile initialized with wav-file 
func tickBuffer(forBpm bpm: Int) -> AVAudioPCMBuffer { 
    audioFile.framePosition = 0 // position in file from where to read, required if you're read several times from one AVAudioFile 
    let periodLength = AVAudioFrameCount(audioFile.processingFormat.sampleRate * 60/Double(bpm)) // tick's length for given bpm (sound length + silence length) 
    let buffer = AVAudioPCMBuffer(PCMFormat: audioFile.processingFormat, frameCapacity: periodLength) 
    try! audioFile.readIntoBuffer(buffer) // sorry for forcing try 
    buffer.frameLength = periodLength // key to success. This will append silcence to sound 
    return buffer 
} 

// player – instance of AVAudioPlayerNode within your AVAudioEngine 
func startLoop() { 
    player.stop() 
    let buffer = tickBuffer(forBpm: bpm) 
    player.scheduleBuffer(buffer, atTime: nil, options: .Loops, completionHandler: nil) 
    player.play() 
} 
+0

Bu, özellikle sessizlik için 'buffer.frameLength' kullanıldığında yardımcı olur, ancak yine de her bir atım türü için farklı bir ses dosyasına izin vermez (örneğin, alt sesler için A sesi, atımlar için B sesi ve alt bölümler için ses C). Gerçek hüner, bir dizi vuruş ve altbölüm içeren bir arabellek oluşturmak ve daha sonra tüm arabelleği/çubuğu döngülemek olacaktır. Yukarıdaki cevap, basit bir metronom veya tıklama izi için mükemmeldir, ancak en az iki farklı sese ihtiyaç duyan "gerçek" bir metronomu desteklemez. – blwinters

+0

İki örnek AVAudioPlayerNode ile grafik oluşturmayı deneyebilirsiniz (her bir "tick" türü için bir tane). Birincisi "temel" metronom ve diğeri ise sadece güçlü vuruşlarda oynar. – 5hrp

+0

Sonunda, bu benim gittiğim yaklaşım ve tek tıklamayla çalışmam var. Gelecekte birden fazla düğüm deneyebilir ve gerekli ofseti oluşturmak için atTime parametresini kullanabilirim. – blwinters

3

Mümkün olan en düşük zaman hatasıyla ses çalmanın olası yollarından birinin, ses örneklerini doğrudan geri arama yoluyla sağladığını düşünüyorum. IOS'ta bunu AudioUnit ile yapabilirsiniz.

Bu geri aramada örnek sayımını takip edebilir ve hangi örnekte olduğunuzu bilebilirsiniz. Örnek sayacından, zaman değerine (örnek oranını kullanarak) gidebilir ve metronom gibi üst düzey görevleriniz için kullanabilirsiniz. Metronom sesini çalmanın zamanı geldiğini görürseniz, o zaman o ses dosyasından ses örneklerini arabelleğe kopyalamaya başlıyorsunuz.

Bu, herhangi bir kod içermeyen teorik bir bölümdür, ancak AudioUnit ve geri çağrı tekniğinin birçok örneğini bulabilirsiniz.

+0

Teşekkür ederim, buna daha çok bakmalıyım. – blwinters

2

5hrp cevabı üzerine genişletmek için: Bu yardımcı olacaktır Umut

iki atım, iyimser (Tonu1) ve bir vuruşu (Tone2) sahip basit bir durumda alın ve gitmelerini istiyorum birbiri ile fazlayın, böylece ses belirli bir bpm'ye kadar (yukarı, aşağı, yukarı) olacaktır.

Sen Onları diyelim, (her dövmek için bir tane), normal olarak audioNode1 ve audioNode2

Eğer fazda olmak isteyecektir ilk yendi, böylece kurulum AVAudioPlayerNode iki örneğini gerekecektir

:

let buffer = tickBuffer(forBpm: bpm) 
audioNode1player.scheduleBuffer(buffer, atTime: nil, options: .loops, completionHandler: nil) 

daha sonra ikinci vuruş için tam olarak faz dışı olmasını veya t = bpm/2'den başlamasını istersiniz. üzerinden

audioNode2player.scheduleBuffer(buffer, atTime: audioTime2, options: .loops, completionHandler: nil) 

Bu döngü üzerinde iki atım oynayacak, bpm/2: şöyle tamponu içinde bu değişkeni kullanabilirsiniz

audioTime2 = AVAudioTime(sampleTime: AVAudioFramePosition(AVAudioFrameCount(audioFile2.processingFormat.sampleRate * 60/Double(bpm) * 0.5)), atRate: Double(1)) 

: bunun için bir AVAudioTime değişkeni kullanabilirsiniz Birbirinden faz!

Tüm çubuk oluşturmak için bunu daha fazla vuruşa nasıl genelleştireceğinizi görmek kolaydır. Yine de en zarif çözüm değil, çünkü 16 nota yazmak isterseniz 16 düğüm oluşturmanız gerekiyor.

İlgili konular