มีวิธีใดบ้างที่จะทำให้ Android ส่งเสียงความถี่โดยพลการ (หมายความว่าฉันไม่ต้องการให้มีไฟล์เสียงที่บันทึกไว้ล่วงหน้า)
ฉันมองไปรอบ ๆ และToneGeneratorเป็นสิ่งเดียวที่ฉันสามารถพบได้ว่ามันอยู่ใกล้ แต่ดูเหมือนว่าจะสามารถส่งออกโทน DTMF มาตรฐาน
ความคิดใด ๆ ?
มีวิธีใดบ้างที่จะทำให้ Android ส่งเสียงความถี่โดยพลการ (หมายความว่าฉันไม่ต้องการให้มีไฟล์เสียงที่บันทึกไว้ล่วงหน้า)
ฉันมองไปรอบ ๆ และToneGeneratorเป็นสิ่งเดียวที่ฉันสามารถพบได้ว่ามันอยู่ใกล้ แต่ดูเหมือนว่าจะสามารถส่งออกโทน DTMF มาตรฐาน
ความคิดใด ๆ ?
คำตอบ:
เดิมทีฉันพบโค้ดตัวอย่างนี้ในบล็อก แต่มีข้อบกพร่องบางอย่างที่ทำให้เกิดเสียงที่น่ากลัว ฉันได้แก้ไขข้อบกพร่องและโพสต์รหัสผลลัพธ์ที่นี่ ดูเหมือนจะทำงานได้ดีสำหรับฉัน!
public class PlaySound extends Activity {
// originally from http://marblemice.blogspot.com/2010/04/generate-and-play-tone-in-android.html
// and modified by Steve Pomeroy <steve@staticfree.info>
private final int duration = 3; // seconds
private final int sampleRate = 8000;
private final int numSamples = duration * sampleRate;
private final double sample[] = new double[numSamples];
private final double freqOfTone = 440; // hz
private final byte generatedSnd[] = new byte[2 * numSamples];
Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onResume() {
super.onResume();
// Use a new tread as this can take a while
final Thread thread = new Thread(new Runnable() {
public void run() {
genTone();
handler.post(new Runnable() {
public void run() {
playSound();
}
});
}
});
thread.start();
}
void genTone(){
// fill out the array
for (int i = 0; i < numSamples; ++i) {
sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
for (final double dVal : sample) {
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
}
void playSound(){
final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
audioTrack.play();
}
}
0.0 1.0การคูณด้วย32767จะแปลงเป็นช่วงจุดคงที่ 16 บิต audiotrackคาดว่าบัฟเฟอร์ที่จะน้อยendianรูปแบบ ดังนั้นสองบรรทัดถัดไปจะแปลงลำดับไบต์จาก endian ใหญ่เป็น endian น้อย
การปรับปรุงโค้ดด้านบน:
เพิ่มทางลาดขึ้นและทางลาดลงเพื่อหลีกเลี่ยงการคลิก
เพิ่มรหัสเพื่อตรวจสอบว่าเมื่อแทคเล่นเสร็จแล้ว
double duration = 1; // seconds
double freqOfTone = 1000; // hz
int sampleRate = 8000; // a number
double dnumSamples = duration * sampleRate;
dnumSamples = Math.ceil(dnumSamples);
int numSamples = (int) dnumSamples;
double sample[] = new double[numSamples];
byte generatedSnd[] = new byte[2 * numSamples];
for (int i = 0; i < numSamples; ++i) { // Fill the sample array
sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalized.
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
int i = 0 ;
int ramp = numSamples / 20 ; // Amplitude ramp as a percent of sample count
for (i = 0; i< ramp; ++i) { // Ramp amplitude up (to avoid clicks)
double dVal = sample[i];
// Ramp up to maximum
final short val = (short) ((dVal * 32767 * i/ramp));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
for (i = i; i< numSamples - ramp; ++i) { // Max amplitude for most of the samples
double dVal = sample[i];
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
for (i = i; i< numSamples; ++i) { // Ramp amplitude down
double dVal = sample[i];
// Ramp down to zero
final short val = (short) ((dVal * 32767 * (numSamples-i)/ramp ));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
AudioTrack audioTrack = null; // Get audio track
try {
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, (int)numSamples*2,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length); // Load the track
audioTrack.play(); // Play the track
}
catch (Exception e){
RunTimeError("Error: " + e);
return false;
}
int x =0;
do{ // Monitor playback to find when done
if (audioTrack != null)
x = audioTrack.getPlaybackHeadPosition();
else
x = numSamples;
} while (x<numSamples);
if (audioTrack != null) audioTrack.release(); // Track play done. Release track.
ฉันรวมโซลูชันที่ยอดเยี่ยมข้างต้นไว้ในแพ็คเกจเล็ก ๆ ที่เป็นระเบียบซึ่งสามารถใช้งานได้มากขึ้นนอกกรอบเป็นเสียงกริ่งที่กำหนดค่าได้ง่าย มันทำงานในเธรดพื้นหลังและมีวิธีหยุดและเล่นและตัวเลือกจำนวนหนึ่งที่คุณสามารถตั้งค่าได้
มันขึ้นอยู่กับ JCenter เพื่อให้คุณสามารถเพิ่มลงในรายการอ้างอิงของคุณเช่นนี้
compile 'net.mabboud:android-tone-player:0.2'
และคุณใช้แบบนี้สำหรับเสียงกริ่งต่อเนื่อง
ContinuousBuzzer tonePlayer = new ContinuousBuzzer();
tonePlayer.play();
// just an example don't actually use Thread.sleep in your app
Thread.sleep(1000);
tonePlayer.stop();
หรือเสียงกริ่งเล่นเพียงครั้งเดียวและคุณสามารถตั้งค่าความถี่และระดับเสียงได้เช่นนี้
OneTimeBuzzer buzzer = new OneTimeBuzzer();
buzzer.setDuration(5);
// volume values are from 0-100
buzzer.setVolume(50);
buzzer.setToneFreqInHz(110);
เนื่องจากมีข้อบกพร่องใน Android เวอร์ชันเก่าบางรุ่นที่ทำให้หน่วยความจำรั่วเมื่อใช้ MODE_STATIC ฉันจึงแก้ไขคำตอบของ Xarph ด้านบนเพื่อใช้ MODE_STREAM หวังว่าจะช่วยได้บ้าง
public void playTone(double freqOfTone, double duration) {
//double duration = 1000; // seconds
// double freqOfTone = 1000; // hz
int sampleRate = 8000; // a number
double dnumSamples = duration * sampleRate;
dnumSamples = Math.ceil(dnumSamples);
int numSamples = (int) dnumSamples;
double sample[] = new double[numSamples];
byte generatedSnd[] = new byte[2 * numSamples];
for (int i = 0; i < numSamples; ++i) { // Fill the sample array
sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalized.
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
int i = 0 ;
int ramp = numSamples / 20 ; // Amplitude ramp as a percent of sample count
for (i = 0; i< ramp; ++i) { // Ramp amplitude up (to avoid clicks)
double dVal = sample[i];
// Ramp up to maximum
final short val = (short) ((dVal * 32767 * i/ramp));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
for (i = i; i< numSamples - ramp; ++i) { // Max amplitude for most of the samples
double dVal = sample[i];
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
for (i = i; i< numSamples; ++i) { // Ramp amplitude down
double dVal = sample[i];
// Ramp down to zero
final short val = (short) ((dVal * 32767 * (numSamples-i)/ramp ));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
AudioTrack audioTrack = null; // Get audio track
try {
int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize,
AudioTrack.MODE_STREAM);
audioTrack.play(); // Play the track
audioTrack.write(generatedSnd, 0, generatedSnd.length); // Load the track
}
catch (Exception e){
}
if (audioTrack != null) audioTrack.release(); // Track play done. Release track.
}
นี่คืออีกหนึ่งบล็อกที่สาธิตการใช้งาน Synth แบบง่ายพร้อม UI บางส่วน
คุณอาจสนใจ csound หรือ pdlib (pure data lib) สำหรับ Android
Modified Code ตามคำตอบของ Singhaks
public class MainActivity extends Activity {
private final int duration = 30; // seconds
private final int sampleRate = 8000;
private final int numSamples = duration * sampleRate;
private final double sample[] = new double[numSamples];
private final double freqOfTone = 440; // hz
private final byte generatedSnd[] = new byte[2 * numSamples];
Handler handler = new Handler();
private AudioTrack audioTrack;
private boolean play = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
8000, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, numSamples,
AudioTrack.MODE_STREAM);
}
@Override
protected void onResume() {
super.onResume();
// Use a new tread as this can take a while
Thread thread = new Thread(new Runnable() {
public void run() {
handler.post(new Runnable() {
public void run() {
playSound();
genTone();
}
});
}
});
thread.start();
}
void genTone(){
// fill out the array
while(play){
for (int i = 0; i < numSamples; ++i) {
// float angular_frequency =
sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone));
}
int idx = 0;
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
for (double dVal : sample) {
short val = (short) (dVal * 32767);
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
audioTrack.write(generatedSnd, 0, numSamples);
}
}
void playSound(){
play = true;
audioTrack.play();
}
}
float synth_frequency = 440;
int minSize = AudioTrack.getMinBufferSize(SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
minSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
short[] buffer = new short[minSize];
float angle = 0;
while (true)
{
if (play)
{
for (int i = 0; i < buffer.length; i++)
{
float angular_frequency =
(float)(2*Math.PI) * synth_frequency / SAMPLE_RATE;
buffer[i] = (short)(Short.MAX_VALUE * ((float) Math.sin(angle)));
angle += angular_frequency;
}
audioTrack.write(buffer, 0, buffer.length);
}
// คุณสามารถเพิ่มค่าโดยพลการใน synth_frequency เพื่อรับเสียงการเปลี่ยนแปลงตัวอย่างเช่นคุณสามารถเพิ่มตัวแปรสุ่มเพื่อรับเสียง
ทำที่สำคัญ (16 หมายเหตุ)
public class MainActivity extends AppCompatActivity {
private double mInterval = 0.125;
private int mSampleRate = 8000;
private byte[] generatedSnd;
private final double mStandardFreq = 440;
Handler handler = new Handler();
private AudioTrack audioTrack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
// Use a new tread as this can take a while
final Thread thread = new Thread(new Runnable() {
public void run() {
byte[] tempByte = new byte[0];
for (int i = 0; i < 16 ; i++ ){
double note = getNoteFrequencies(i);
byte[] tonByteNote = getTone(mInterval, mSampleRate, note);
tempByte = concat(tonByteNote, tempByte);
}
generatedSnd = tempByte;
handler.post(new Runnable() {
public void run() {
playTrack(generatedSnd);
}
});
}
});
thread.start();
}
public byte[] concat(byte[] a, byte[] b) {
int aLen = a.length;
int bLen = b.length;
byte[] c= new byte[aLen+bLen];
System.arraycopy(a, 0, c, 0, aLen);
System.arraycopy(b, 0, c, aLen, bLen);
return c;
}
private double getNoteFrequencies(int index){
return mStandardFreq * Math.pow(2, (double) index/12.0d);
}
private byte[] getTone(double duration, int rate, double frequencies){
int maxLength = (int)(duration * rate);
byte generatedTone[] = new byte[2 * maxLength];
double[] sample = new double[maxLength];
int idx = 0;
for (int x = 0; x < maxLength; x++){
sample[x] = sine(x, frequencies / rate);
}
for (final double dVal : sample) {
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedTone[idx++] = (byte) (val & 0x00ff);
generatedTone[idx++] = (byte) ((val & 0xff00) >>> 8);
}
return generatedTone;
}
private AudioTrack getAudioTrack(int length){
if (audioTrack == null)
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
mSampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, length,
AudioTrack.MODE_STATIC);
return audioTrack;
}
private double sine(int x, double frequencies){
return Math.sin( 2*Math.PI * x * frequencies);
}
void playTrack(byte[] generatedSnd){
getAudioTrack(generatedSnd.length)
.write(generatedSnd, 0, generatedSnd.length);
audioTrack.play();
}
}
ดูห้องสมุดที่มีประโยชน์นี้
https://github.com/karlotoy/perfectTune
ใช้งานง่าย
เพิ่มสิ่งนี้ในการอ้างอิงของคุณ
compile 'com.github.karlotoy:perfectTune:1.0.2'
และคุณใช้สิ่งนี้:
PerfectTune perfectTune = new PerfectTune();
perfectTune.setTuneFreq(desire_freq);
perfectTune.playTune();
เพื่อหยุดการปรับแต่ง:
perfectTune.stopTune();
มีหลายโปรแกรมสำหรับสิ่งนี้ แต่มันห่วย ฉันวัดไม่กี่:
http://www.endolith.com/wordpress/2009/11/24/android-audio-applications/
ดังนั้นอย่าทำสิ่งที่พวกเขาทำ : ง