I am using The Visualizer to draw a Visualization of a Sound File that I am Playing. The sound wave is displaying, however I would like to make it less detailed, as it has an effect on my frame-rate. There is very limited documentation for this. So I have attempted to do the following:
mVisualizer.setCaptureSize(2);
To set the capture rate to a very low value. However, it appears that the line is being drawn with the same amount of detail. I have read in the documentation that:
Sets the capture size, i.e. the number of bytes returned by getWaveForm(byte[]) and getFft(byte[]) methods.
Another issue that I have, is that I would like to detect sounds with a high energy level in the sound File that I am playing so I can visually represent them on the screen. For example: The screen flashes along with the baseline. Here is what I have so far:
public static void setupVisualizer() {
mVisualizer = new Visualizer(mpSong.getAudioSessionId());
mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
mVisualizer.setDataCaptureListener(
new Visualizer.OnDataCaptureListener() {
public void onWaveFormDataCapture(Visualizer visualizer,
byte[] bytes, int samplingRate) {
Game.updateVisualizer(bytes);
}
public void onFftDataCapture(Visualizer visualizer,
byte[] bytes, int samplingRate) {
}
}, Visualizer.getMaxCaptureRate() / 2, true, false);
}
Is it possible to detect certain sounds inside this listener? Or what are the alternatives? Sorry for my bad english. Thank you very much for your time friends.
Ok after several Hours of Testing and Researching I have found a solution. It may not be very accurate, but it is the only alternative I could come up with. I made a class called BeatDetector:
public class BeatDetectorByFrequency {
private static final String TAG = "TEST";
private Visualizer mVisualizer = null;
private double mRunningSoundAvg[];
private double mCurrentAvgEnergyOneSec[];
private int mNumberOfSamplesInOneSec;
private long mSystemTimeStartSec;
// FREQS
private static final int LOW_FREQUENCY = 300;
private static final int MID_FREQUENCY = 2500;
private static final int HIGH_FREQUENCY = 10000;
private OnBeatDetectedListener onBeatDetectedListener = null;
public BeatDetectorByFrequency() {
init();
}
private void init() {
mRunningSoundAvg = new double[3];
mCurrentAvgEnergyOneSec = new double[3];
mCurrentAvgEnergyOneSec[0] = -1;
mCurrentAvgEnergyOneSec[1] = -1;
mCurrentAvgEnergyOneSec[2] = -1;
}
public void link(MediaPlayer player) {
if (player == null) {
throw new NullPointerException("Cannot link to null MediaPlayer");
}
mVisualizer = new Visualizer(player.getAudioSessionId());
mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener() {
@Override
public void onWaveFormDataCapture(Visualizer visualizer,
byte[] bytes, int samplingRate) {
// DO NOTHING
}
@Override
public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate) {
updateVisualizerFFT(bytes);
}
};
mVisualizer.setDataCaptureListener(captureListener,
Visualizer.getMaxCaptureRate() / 2, false, true);
mVisualizer.setEnabled(true);
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mVisualizer.setEnabled(false);
}
});
mSystemTimeStartSec = System.currentTimeMillis();
}
public void release() {
if (mVisualizer != null) {
mVisualizer.setEnabled(false);
mVisualizer.release();
}
}
public void pause() {
if (mVisualizer != null) {
mVisualizer.setEnabled(false);
}
}
public void resume() {
if (mVisualizer != null) {
mVisualizer.setEnabled(true);
}
}
public void updateVisualizerFFT(byte[] audioBytes) {
int energySum = 0;
energySum += Math.abs(audioBytes[0]);
int k = 2;
double captureSize = mVisualizer.getCaptureSize() / 2;
int sampleRate = mVisualizer.getSamplingRate() / 2000;
double nextFrequency = ((k / 2) * sampleRate) / (captureSize);
while (nextFrequency < LOW_FREQUENCY) {
energySum += Math.sqrt((audioBytes[k] * audioBytes[k])
* (audioBytes[k + 1] * audioBytes[k + 1]));
k += 2;
nextFrequency = ((k / 2) * sampleRate) / (captureSize);
}
double sampleAvgAudioEnergy = (double) energySum
/ (double) ((k * 1.0) / 2.0);
mRunningSoundAvg[0] += sampleAvgAudioEnergy;
if ((sampleAvgAudioEnergy > mCurrentAvgEnergyOneSec[0])
&& (mCurrentAvgEnergyOneSec[0] > 0)) {
fireBeatDetectedLowEvent(sampleAvgAudioEnergy);
}
energySum = 0;
while (nextFrequency < MID_FREQUENCY) {
energySum += Math.sqrt((audioBytes[k] * audioBytes[k])
* (audioBytes[k + 1] * audioBytes[k + 1]));
k += 2;
nextFrequency = ((k / 2) * sampleRate) / (captureSize);
}
sampleAvgAudioEnergy = (double) energySum / (double) ((k * 1.0) / 2.0);
mRunningSoundAvg[1] += sampleAvgAudioEnergy;
if ((sampleAvgAudioEnergy > mCurrentAvgEnergyOneSec[1])
&& (mCurrentAvgEnergyOneSec[1] > 0)) {
fireBeatDetectedMidEvent(sampleAvgAudioEnergy);
}
energySum = Math.abs(audioBytes[1]);
while ((nextFrequency < HIGH_FREQUENCY) && (k < audioBytes.length)) {
energySum += Math.sqrt((audioBytes[k] * audioBytes[k])
* (audioBytes[k + 1] * audioBytes[k + 1]));
k += 2;
nextFrequency = ((k / 2) * sampleRate) / (captureSize);
}
sampleAvgAudioEnergy = (double) energySum / (double) ((k * 1.0) / 2.0);
mRunningSoundAvg[2] += sampleAvgAudioEnergy;
if ((sampleAvgAudioEnergy > mCurrentAvgEnergyOneSec[2])
&& (mCurrentAvgEnergyOneSec[2] > 0)) {
fireBeatDetectedHighEvent(sampleAvgAudioEnergy);
}
mNumberOfSamplesInOneSec++;
if ((System.currentTimeMillis() - mSystemTimeStartSec) > 1000) {
mCurrentAvgEnergyOneSec[0] = mRunningSoundAvg[0]
/ mNumberOfSamplesInOneSec;
mCurrentAvgEnergyOneSec[1] = mRunningSoundAvg[1]
/ mNumberOfSamplesInOneSec;
mCurrentAvgEnergyOneSec[2] = mRunningSoundAvg[2]
/ mNumberOfSamplesInOneSec;
mNumberOfSamplesInOneSec = 0;
mRunningSoundAvg[0] = 0.0;
mRunningSoundAvg[1] = 0.0;
mRunningSoundAvg[2] = 0.0;
mSystemTimeStartSec = System.currentTimeMillis();
}
}
// USE INTERFACES IN NEXT UPDATE:
private void fireBeatDetectedLowEvent(double power) {
// Utility.log("LOW BEAT DETECTED!");
Game.lowBeat(power);
if (onBeatDetectedListener != null) {
onBeatDetectedListener.onBeatDetectedLow();
}
}
private void fireBeatDetectedMidEvent(double power) {
// Utility.log("MEDIUM BEAT DETECTED!");
Game.mediumBeat(power);
if (onBeatDetectedListener != null) {
onBeatDetectedListener.onBeatDetectedMid();
}
}
private void fireBeatDetectedHighEvent(double power) {
// Utility.log("HIGH BEAT DETECTED!");
Game.highBeat(power);
if (onBeatDetectedListener != null) {
onBeatDetectedListener.onBeatDetectedHigh();
}
}
public void setOnBeatDetectedListener(OnBeatDetectedListener listener) {
onBeatDetectedListener = listener;
}
public interface OnBeatDetectedListener {
public abstract void onBeatDetectedLow();
public abstract void onBeatDetectedMid();
public abstract void onBeatDetectedHigh();
}
}
It takes a MediaPlayer Object as a parameter and then Calculates Three Different Frequencies Based on the EnergySum of the Byte Data. It is possible to Split the frequencies as many times as you like. I was considering Creating an Array of Frequencies, that each Have a Listener. I then used the Following to Draw a Rectangle:
public static void highBeat(double power) {
HIGH_FREQUENCY += (power * 1000); // ORIGINAL: * 1000
if (HIGH_FREQUENCY > GameValues.FREQ_MAX) {
HIGH_FREQUENCY = GameValues.FREQ_MAX;
}
updateHighFreq();
}
public static void updateHighFreq() {
prcnt = HIGH_FREQUENCY * 100 / GameValues.FREQ_MAX;
if (prcnt < 0)
prcnt = 0;
HIGH_F_HEIGHT = (int) (GameValues.FREQ_MAX_HEIGHT * (prcnt / 100));
}
This calculates the Height of the Rectangle by Calculating a percentage based on the Max Power and Maximum Height of a Bar. It is not very accurate, but it is the best thing I could come up with. Again, this can be done for as many frequencies as you like. Here are some Links that Helped me out:
Hope I can help anyone else with these issues.