2010-07-22 17 views
15

ठीक है तो मैं एक आवृत्ति जनरेटर जो हार्डवेयर के लिए पीसीएम डेटा भेजने के लिए audiotrack का उपयोग करता है। यहाँ कोड मैं उस के लिए उपयोग कर रहा हूँ है:एंड्रॉयड audiotrack बफरिंग समस्याओं

private class playSoundTask extends AsyncTask<Void, Void, Void> { 
    float frequency; 
    float increment; 
    float angle = 0; 
    short samples[] = new short[1024]; 

    @Override 
    protected void onPreExecute() { 
    int minSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);   
    track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, 
    AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
    minSize, AudioTrack.MODE_STREAM); 
    track.play(); 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 
    while(Main.this.isPlaying) 
    { 
    for(int i = 0; i < samples.length; i++) 
    { 
    frequency = (float)Main.this.slider.getProgress(); 
    increment = (float)(2*Math.PI) * frequency/44100; 
    samples[i] = (short)((float)Math.sin(angle)*Short.MAX_VALUE); 
    angle += increment; 
    } 

    track.write(samples, 0, samples.length); 
    } 
    return null; 
    } 
} 

आवृत्ति एक स्लाइड बार से जुड़ा हुआ है, और सही मान नमूना पीढ़ी पाश में सूचना दी जा रही है। जब मैं ऐप शुरू करता हूं तो सबकुछ ठीक और बेवकूफ होता है। जब आप स्लाइड बार के साथ अपनी अंगुली खींचते हैं तो आपको एक अच्छी व्यापक ध्वनि मिलती है। लेकिन इसके साथ खेलने के लगभग 10 सेकंड के बाद, ऑडियो अजीब हो जाना शुरू होता है। एक चिकनी स्वीप के बजाय, यह घबरा गया है, और केवल हर 1000 हर्ट्ज या उसके आसपास टोन बदलता है। इस पर क्या विचार हो सकता है पर कोई विचार?

यहाँ मामले में सभी कोड है समस्या कहीं और झूठ:

public class Main extends Activity implements OnClickListener, OnSeekBarChangeListener { 
AudioTrack track; 
SeekBar slider; 
ImageButton playButton; 
TextView display; 

boolean isPlaying=false; 

/** Called when the activity is first created. */ 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    display = (TextView) findViewById(R.id.display); 
    display.setText("5000 Hz"); 

    slider = (SeekBar) findViewById(R.id.slider); 
    slider.setMax(20000); 
    slider.setProgress(5000); 
    slider.setOnSeekBarChangeListener(this); 


    playButton = (ImageButton) findViewById(R.id.play); 
    playButton.setOnClickListener(this); 

} 

private class playSoundTask extends AsyncTask<Void, Void, Void> { 
    float frequency; 
    float increment; 
    float angle = 0; 
    short samples[] = new short[1024]; 

    @Override 
    protected void onPreExecute() { 
    int minSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);   
    track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, 
    AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
    minSize, AudioTrack.MODE_STREAM); 
    track.play(); 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 
    while(Main.this.isPlaying) 
    { 
    for(int i = 0; i < samples.length; i++) 
    { 
    frequency = (float)Main.this.slider.getProgress(); 
    increment = (float)(2*Math.PI) * frequency/44100; 
    samples[i] = (short)((float)Math.sin(angle)*Short.MAX_VALUE); 
    angle += increment; 
    } 

    track.write(samples, 0, samples.length); 
    } 
    return null; 
    } 
} 



@Override 
public void onProgressChanged(SeekBar seekBar, int progress, 
    boolean fromUser) { 
    display.setText(""+progress+" Hz"); 
} 

public void onClick(View v) { 
    if (isPlaying) { 
    stop(); 
    } else { 
    start(); 
    } 
} 

public void stop() { 
    isPlaying=false; 
    playButton.setImageResource(R.drawable.play); 
} 

public void start() { 
    isPlaying=true; 
    playButton.setImageResource(R.drawable.stop); 
    new playSoundTask().execute(); 
} 

@Override 
protected void onResume() { 
    super.onResume(); 

} 

@Override 
protected void onStop() { 
    super.onStop(); 
    //Store state 
    stop(); 

} 


@Override 
public void onStartTrackingTouch(SeekBar seekBar) { 
    // TODO Auto-generated method stub 

} 


@Override 
public void onStopTrackingTouch(SeekBar seekBar) { 
    // TODO Auto-generated method stub 

} 
} 
+0

आपके सवाल का सीधे जवाब दे नहीं लेकिन एक पक्ष नोट के रूप में, ध्यान दें प्रगति बार और स्ट्रीमिंग के साथ एक ज्ञात मुद्दा यह है कि: http://code.google.com/p/android/issues/detail?id=4124 हालांकि आपकी समस्या प्रगति पट्टी के साथ ही नहीं बल्कि ऑडियो स्ट्रीम के साथ है। इसलिए, आपकी समस्या का समाधान नहीं है, लेकिन जब से आप ऑडियो स्ट्रीम के साथ काम करते हैं, तो आपको अवगत होना चाहिए कि इसमें एक या अधिक खुली बग हैं। –

+0

मुझे बिल्कुल वही समस्या है। बड़ा बफर है, बाद में कूदना शुरू होता है। लॉग में मैं बहुत लंबे जीसी देखता हूं (जीसी ने 4200 एमएमएस में 180 वस्तुओं को साफ किया)। मैं कोई आवंटन नहीं कर रहा हूं। डीडीएमएस आवंटन ट्रैकर में ऐसा लगता है कि ऑडियोट्रैक ही आवंटन कर रहा है। यकीन नहीं है कि जीसी को कूदने के साथ क्या करना है। क्या आपने इसे हल किया है? –

+0

क्या आपने कभी अपना "ऑडियोटैक" साफ़ किया है? यह एक स्मृति संबंधित मुद्दा हो सकता है –

उत्तर

11

मैं कारण मिल गया!

समस्या audiotrack के बफर का आकार है। यदि आप नमूनों को पर्याप्त तेज़ी से उत्पन्न नहीं कर सकते हैं, तो नमूने खत्म हो जाते हैं, प्लेबैक रुक जाता है, और तब पर्याप्त रहता है जब पर्याप्त नमूने होते हैं।

एकमात्र समाधान सुनिश्चित करें कि आप नमूने काफी तेजी से (44100/s) उत्पन्न कर सकते हैं बनाने के लिए है। एक तेज़ फिक्स के रूप में, नमूना दर को 22000 (या बफर के आकार में वृद्धि) को कम करने का प्रयास करें।

कम से कम इस लिए कि यह कैसे मेरे लिए बर्ताव करता है - जब मैं अपने नमूना पीढ़ी दिनचर्या अनुकूलित किया है, कूद चला गया।

(बफर के आकार को बढ़ाने से बाद में ध्वनि शुरू हो जाती है, क्योंकि बफर भरने तक कुछ रिजर्व इंतजार कर रहे हैं। लेकिन फिर भी, यदि आप नमूनों को पर्याप्त तेज़ नहीं बना रहे हैं, तो आखिरकार नमूने खत्म हो जाते हैं)।


ओह, और आपको लूप के बाहर परिवर्तनीय चर डाल देना चाहिए! और शायद नमूने सरणी को बड़ा कर दें, ताकि लूप अधिक समय तक चल सके।

short samples[] = new short[4*1024]; 
... 

frequency = (float)Main.this.slider.getProgress(); 
increment = (float)(2 * Math.PI) * frequency/44100; 
for(int i = 0; i < samples.length; i++) 
{ 
    samples[i] = (short)((float)Math.sin(angle) * Short.MAX_VALUE); 
    angle += increment; 
} 

तुम भी सभी जीवाओं की मूल्यों आवृत्तियों 200Hz-8000Hz के लिए, 10 हर्ट्ज की एक कदम के साथ precompute सकता है। या मांग पर ऐसा करें: जब उपयोगकर्ता आवृत्ति का चयन करता है, तो थोड़ी देर के लिए अपनी विधि का उपयोग करके नमूने उत्पन्न करें और उन्हें एक सरणी में भी सहेजें। जब आप एक पूर्ण साइन लहर रखने के लिए पर्याप्त नमूने उत्पन्न करते हैं, तो आप केवल सरणी पर लूपिंग रख सकते हैं (क्योंकि पाप एक आवधिक कार्य है)। असल में वहाँ के रूप में आप एक अवधि वास्तव में कभी नहीं प्रवेश ध्वनि में कुछ छोटे विसंगतियों हो सकता है (क्योंकि आप 1 से कोण वृद्धि हो रही है और एक पूर्ण साइन वेव की लंबाई sampleRate/(double)frequency नमूने है)। लेकिन इस अवधि का सही बहुमत लेना असंगतता को अनजान बनाता है।

इसके अलावा, http://developer.android.com/guide/practices/design/performance.html

4

मामले में भी यही समस्या के साथ इस भर में ठोकर देख (मैंने किया था के रूप में), समाधान वास्तव में बफर के साथ कोई संबंध नहीं है, यह ज्या समारोह से कोई लेना देना नहीं है। angle += (increment % (2.0f * (float) Math.PI)); साथ angle += increment की जगह की कोशिश करो। इसके अलावा, दक्षता के लिए, Math.sin() के बजाय FloatMath.sin() का उपयोग करने का प्रयास करें।

1

मुझे लगता है कि मैंने समस्या को हल करने में कामयाब रहा है।

... 
samples[i] = (short)((float)Math.sin(angle)*Short.MAX_VALUE); 
angle += increment; 
angle = angle % (2.0f * (float) Math.PI); //added statement 
... 

जैसा कि पहले बताया गया है, यह बफर के बारे में नहीं है। यह कोण चर के बारे में है, यह लगातार बढ़ता है। थोड़ी देर के बाद, चर बहुत बड़ा हो जाता है और छोटे चरणों का समर्थन नहीं करता है।

जैसे साइन 2 * पीआई के बाद दोहराता है, हमें कोण के मॉड्यूलस को लेने की आवश्यकता होती है।

उम्मीद है कि इससे मदद मिलती है।

संपादित: कोण + = वृद्धि नौकरी के लिए पर्याप्त है।

संबंधित मुद्दे