如何在android中生成准确的频率

时间:2017-08-30 16:23:10

标签: android audio frequency

为了在android中按数字生成所需的频率,我知道2种方式,每种方式都有自己的大问题。

第一种方式是PCM_16_bit:

    frequency = (int) frequency;
    final int numberOfSamples = duration * sampleRate;
    final byte[] generatedSnd = new byte[numberOfSamples * 2];
    double dVal;

    int idx = 0;
    for (int i = 0; i < numberOfSamples; ++i) {
        dVal = Math.sin(2 * Math.PI * i / (sampleRate / frequency));

        // convert to 16 bit pcm sound array
        // assumes the sample buffer is normalised.

        // scale to maximum amplitude
        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);

       }

    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);

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        loudnessEnhancer = new LoudnessEnhancer(audioTrack.getAudioSessionId());

        loudnessEnhancer.setTargetGain(maxFrequency - (int) frequency);

    }

仅适用于短值,因此没有精确数和有限幅度,因此需要具有Android 4.4依赖性的LoudnessEnhancer,这会增加收益并降低质量。

第二种方式是:

 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                final int numberOfSamples = duration * sampleRate * 4;
                final float[] generatedSnd = new float[numberOfSamples];

                float amplitude = 2;//Increase in amplitude has a relation with increase in noise.  

                for (int i = 0; i < numberOfSamples; ++i)
                    generatedSnd[i] = (float) (Math.sin((2 * Math.PI * i * frequency) / sampleRate)) * amplitude;

                audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                        sampleRate, AudioFormat.CHANNEL_OUT_MONO,
                        AudioFormat.ENCODING_PCM_FLOAT, generatedSnd.length,
                        AudioTrack.MODE_STATIC);

                audioTrack.write(generatedSnd, 0, generatedSnd.length, AudioTrack.WRITE_BLOCKING);

这只增加了一个精度但是为我解决了振幅问题,但是它需要android 5效率低,而且1精度不足以用于非常低的音符,如第二钢琴八度音阶。

是否可以制作更精确的频率? 有另一种方式吗?

1 个答案:

答案 0 :(得分:0)

尝试一下,我认为还可以:

  isPlaying = true;
  int sampleRate = 44100;// 44.1 KHz
  double dnumSamples = (double) 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(freq * 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;

  int ramp = numSamples / 20;  // Amplitude ramp as a percent of sample count

  for (i = 0; i < ramp; ++i) {  // Ramp amplitude up (to avoid clicks)
    // Ramp up to maximum
    final short val = (short) (sample[i] * 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 = ramp; i < numSamples - ramp;
       ++i) {                        // Max amplitude for most of the samples
    // scale to maximum amplitude
    final short val = (short) (sample[i] * 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 = numSamples - ramp; i < numSamples; ++i) { // Ramp amplitude down
    // Ramp down to zero
    final short val = (short) (sample[i] * 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);
  }

  try {
    int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO,
            AudioFormat.ENCODING_PCM_16BIT);
    audioTrack =
            new AudioTrack(AudioManager.STREAM_SYSTEM, sampleRate, AudioFormat.CHANNEL_OUT_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);

    audioTrack.setNotificationMarkerPosition(numSamples);
    audioTrack.setPlaybackPositionUpdateListener(
            new AudioTrack.OnPlaybackPositionUpdateListener() {
              @Override public void onPeriodicNotification(AudioTrack track) {
                // nothing to do
              }

              @Override public void onMarkerReached(AudioTrack track) {
               // toneStoppedListener.onToneStopped();
              }
            });

    // Sanity Check for max volume, set after write method to handle issue in android
    // v 4.0.3
    float maxVolume = AudioTrack.getMaxVolume();

    if (volume > maxVolume) {
      volume = maxVolume;
    } else if (volume < 0) {
      volume = 0;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      audioTrack.setVolume(volume);
    } else {
      audioTrack.setStereoVolume(volume, volume);
    }

    audioTrack.play(); // Play the track
    audioTrack.write(generatedSnd, 0, generatedSnd.length);    // Load the track
  } catch (Exception e) {
    e.printStackTrace();
  }