2012-04-15 47 views
8

ilk kez poster burada. Genelde cevabı kendim bulmak isterim (araştırma ya da deneme-yanılma yoluyla olsun), ama ben burada güldüm.Android Ses - Sinüs sesi üreteci garip davranışı

Yapmaya çalıştığım: Basit bir android ses sentezleyicisi yapıyorum. Şu anda, kullanıcı tarafından ayarlanan tonun frekansını değiştiren kullanıcı arabirimindeki bir sürgü ile gerçek zamanlı bir sinüs tonu oynatıyorum.

Nasıl oluşturdum: Temel olarak, iki iş parçacığım var - bir iş parçacığı ve bir çıktı iş parçacığı. Çalışan iş parçacığı, her defasında tick() yöntemi çağrıldığında sinüs dalgası verileriyle bir tamponu doldurur. Tampon doldurulduktan sonra, çıktı parçasını, verilerin ses parçasına yazılmaya hazır olduğunu bildirir. İki iş parçacığı kullanmamın nedeni, audiotrack.write() kodlarının engellenmesi ve çalışan iş parçacığının verilerini en kısa sürede işlemeye başlayabilmesini (ses kaydının yazmayı bitirmesini beklemek yerine) istemesidir. Kullanıcı Arabirimi üzerindeki kaydırıcı yalnızca çalışan iş parçacığındaki bir değişkeni değiştirir, böylece sıklıktaki herhangi bir değişiklik (kaydırıcı aracılığıyla) çalışan iş parçacığının tick() yöntemiyle okunur.

Ne işe yarıyor: Neredeyse her şey; İş parçacıkları iyi iletişim kurar, oynatımda herhangi bir boşluk veya tıklama görünmez. Büyük arabellek boyutuna rağmen (teşekkürler android), yanıt vermek iyidir. Frekans değişkeni, tick() yöntemindeki (Log.i() tarafından doğrulanmış) arabellek hesaplamaları sırasında kullanılan ara değerler gibi değişmektedir.

Ne işe yaramadı: Herhangi bir nedenle, duyulabilir frekansta sürekli bir değişiklik göremiyorum. Kaydırıcıyı ayarladığım zaman, frekans, genellikle dördüncü veya beşinci genişliğe kadar olan adımlarda değişir. Teorik olarak, 1Hz gibi bir dakikadaki değişiklikleri duymalıyım, ama ben değilim. Garip bir şekilde, kaydırıcıya yapılan değişiklikler sinüs dalgasının harmonik serilerde aralıklarla oynamaya neden oluyor gibi görünüyor; Bununla birlikte, frekans değişkeninin varsayılan frekansın integral katlarına uymadığını doğrulayabilirim.

My Ses parçası gibi kurulur:

_buffSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT); 
_audioTrackOut = new AudioTrack(AudioManager.STREAM_MUSIC, _sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, _buffSize, AudioTrack.MODE_STREAM); 

işçi parçacığının tampon doldurulur ediliyor (kene yoluyla()) gibi:

public short[] tick() 
{ 
    short[] outBuff = new short[_outBuffSize/2]; // (buffer size in Bytes)/2 
    for (int i = 0; i < _outBuffSize/2; i++) 
    { 
     outBuff[i] = (short) (Short.MAX_VALUE * ((float) Math.sin(_currentAngle))); 

     //Update angleIncrement, as the frequency may have changed by now 
     _angleIncrement = (float) (2.0f * Math.PI) * _freq/_sampleRate; 
     _currentAngle = _currentAngle + _angleIncrement;  
    } 
    return outBuff;  
} 

ses verileri gibi yazılmış ediliyor Bu:

Herhangi bir yardım büyük memnuniyetle karşılanacaktır. Frekansta nasıl daha kademeli değişiklikler yapabilirim? Log.i() işlevi, angleIncrement ve currentAngle değişkenlerinin düzgün bir şekilde güncellendiğini doğruladığından, tick() işlevindeki mantığımın oldukça emin olduğu konusunda oldukça eminim.

Teşekkür ederiz!

Güncelleme:

Burada benzer bir sorun bulundu: Android AudioTrack buffering problems çözüm tek mantıklı AudioTrack için yeterince hızlı örneklerini üretmek mümkün olması gerektiğini önerdi. Örnekleme oranımı 22050Hz'ye düşürdüm ve bazı ampirik testler yaptım - tamponumu (enlem() yoluyla) en kötü durumda yaklaşık 6 ms'de doldurabilirim. Bu yeterli değil. 22050Hz'de, audioTrack bana 2048 örnek (veya 4096 Bayt) tampon boyutunu verir. Yani, doldurulmuş her tampon ~ 0 sürer.Verileri oluşturmak için gerekenden çok daha uzun olan 0928 saniyelik ses (1 ~ 6 ms). Çabuk, numuneleri yeterince hızlı üreten bir problemim olmadığını biliyorum.

Ayrıca, uygulama yaşam döngüsünün ilk 3 saniyesinde, iyi çalışıyor - kaydırıcının düzgün bir şekilde süpürmesi ses çıkışında düzgün bir tarama yapıyor. Bundan sonra, gerçekten dalgalı (sadece her 100Mhz ile ilgili değişiklikler) elde etmeye başlar ve bundan sonra, hiç kaydırıcı girdisine yanıt vermeyi durdurur.

Ayrıca bir hata düzeltdim, ancak bunun bir etkisi olduğunu düşünmüyorum. AudioTrack.getMinBufferSize(), BYTES içinde izin verilen en küçük arabellek boyutunu döndürür ve bu sayıyı, tick() 'deki arabellek uzunluğu olarak kullanıyorum - Şimdi bu sayıyı yarıya (örnek başına 2 Bayt) kullanıyorum.

+0

'_freq' için hangi etkinlikleri kullanıyorsunuz? Her seferinde 1Hz'de gerçekten değişiyor mu? Maalesef, neyin çalışmadığını ** anlamıyorum. Sorununuzu dinleyebilmeniz için tam bir çalışma kodu örneği veya sentezlenmiş bir ses parçası sunabilir misiniz? Ses parçası için: ham ses veri formatı tamam - sadece arabelleğe ses kartına değil, dosyaya yaz. Örneğin, 16 bit/işaretli/mono/44100 Hz. –

+0

_freq dosyasını değiştirmek için onProgressChanged() ile _freq'a yeni bir int değeri yazan bir OnSeekBarChangeListener kullanıyorum. Bununla birlikte, frekansı 1 Hz artıran düğmeler (yukarı/aşağı) kullanmayı da denedim, ve çok sayıda artımdan sonra ses seviyesinde sadece sesli bir fark var (sesin yaklaşık 3. veya 4. adımda atlayacağı şekilde) 1Hz yerine). İşte neler olduğuna dair bir örnek; Yavaşça kaydırıp yavaşça 1Hz'lik artışlarla frekansı arttırdığımda duyduğum şey budur. (~ 200 - ~ 1000Hz): http://www.sendspace.com/file/tpxuwr – Tsherr

+0

Benzer bir sorunu online olarak buldum, ancak önerilen çözümü yardımcı olmadı - yukarıdaki güncellemeye bakın. – Tsherr

cevap

4

Ben onu buldum!

Sorun, arabelleklerle veya parçacıklarla ilgisi yok.

İlk birkaç saniyede kulağa hoş geliyor, çünkü hesaplama açısı nispeten küçük. Program çalışırken ve açı büyüdükçe Math.sin (_currentAngle) güvenilmez değerler üretmeye başlar.

FloatMath.sin() ile Math.sin()'u değiştirdim.

Ben de

_currentAngle = ((_currentAngle + _angleIncrement) % (2.0f * (float) Math.PI)); ile _currentAngle = _currentAngle + _angleIncrement;

yerini, bu nedenle açı daima < 2 * PI olduğunu.

Bir çekicilik gibi çalışır! Yardımınız için çok teşekkürler, Praetorian droid!

+0

Lanet olsun, 2 * Pi normalizasyonunu unuttum! Onu kendi eski kodumda gördüm ama senin yokluğunu kaçırdın. Ama açı değişkeninin taşmasının tıklamaları etkilemesi gerektiğini düşündüm ve örnek sesler farklıydı. Her neyse, problemini çözdüğüne sevindim. –

İlgili konular