i have a method that generates an audio signal from a smartphone's speaker, i set the duration time for 1 second. i want to know if the played sound is really played for one second or not.
for that i created a playSound() method and used getTimeInMillis() method to measure the time.
the result was varying from 11 ms to 679 ms. it seemed to me that it just measures the excution time of the method not the whole running time (the time till the playSound() stops).
is there a way to measure that time in java?
void genTone() throws IOException{
double instfreq=0, numerator;
for (int i=0;i<numSample; i++ )
{
numerator=(double)(i)/(double)numSample;
instfreq =freq1+(numerator*(freq2-freq1));
if ((i % 1000) == 0) {
Log.e("Current Freq:", String.format("Freq is: %f at loop %d of %d", instfreq, i, numSample));
}
sample[i]=Math.sin(2*Math.PI*i/(sampleRate/instfreq));
}
int idx = 0;
for (final double dVal : sample) {
// scale to maximum amplitude
final short val = (short) ((dVal * 32767)); // max positive sample for signed 16 bit integers is 32767
// in 16 bit wave PCM, first byte is the low order byte (pcm: pulse control modulation)
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
DataOutputStream dd=new DataOutputStream( new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath()+"/tessst.wav" ));
dd.writeBytes("RIFF");
dd.writeInt(48000); // Final file size not known yet, write 0
dd.writeBytes("WAVE");
dd.writeBytes("fmt ");
dd.writeInt(Integer.reverseBytes(16)); // Sub-chunk size, 16 for PCM
dd.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM
dd.writeShort(Short.reverseBytes( (short) myChannels));// Number of channels, 1 for mono, 2 for stereo
dd.writeInt(Integer.reverseBytes(sampleRate)); // Sample rate
dd.writeInt(Integer.reverseBytes( (sampleRate*myBitsPerSample*myChannels/8))); // Byte rate, SampleRate*NumberOfChannels*BitsPerSample/8
dd.writeShort(Short.reverseBytes((short) (myChannels*myBitsPerSample/8))); // Block align, NumberOfChannels*BitsPerSample/8
dd.writeShort(Short.reverseBytes( myBitsPerSample)); // Bits per sample
dd.writeBytes("data");
dd.write(generatedSnd,0,numSample);
dd.close();
void playSound(){
AudioTrack audioTrack= null;
try{
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length, AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
audioTrack.play();
}
catch(Exception e)
{
System.out.print(e);
}
}
void excute() throws IOException
{
long time=Calendar.getInstance().getTimeInMillis();
playSound();
time=Calendar.getInstance().getTimeInMillis()-time;
Log.e("time", String.format(" "+time+" ms"));
}
Related
In my app I generate list of sounds with different frequencies, for example 1000Hz, 2000Hz, 4000Hz ... for left and right channel:
private AudioTrack generateTone(Ear ear) {
// fill out the array
int numSamples = SAMPLE_RATE * getDurationInSeconds();
double[] sample = new double[numSamples];
byte[] generatedSnd = new byte[2 * numSamples];
for (int i = 0; i < numSamples; ++i) {
sample[i] = Math.sin(2 * Math.PI * i / (SAMPLE_RATE / getLatestFreqInHz()));
}
// 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);
}
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, numSamples,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
int channel;
if (ear == Ear.LEFT) {
audioTrack.setStereoVolume(1.0f, 0.0f);
} else if (ear == Ear.RIGHT) {
audioTrack.setStereoVolume(0.0f, 1.0f);
}
return audioTrack;
}
I use this code to control volume:
audioManager = (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
MAX_VOLUME = audioManager.getStreamMaxVolume(STREAM_MUSIC);
MIN_VOLUME = audioManager.getStreamMinVolume(STREAM_MUSIC);
APP_DEFAULT_VOLUME = (MAX_VOLUME + MIN_VOLUME) /2 ;
#Override
public void setToAppDefaultVolume() {
Timber.tag(TAG).d("Resetting to app default sound volume.");
audioManager.setStreamVolume(STREAM_MUSIC, APP_DEFAULT_VOLUME, 0);
}
#Override
public void increaseVolume() {
if (!isReachedMaxVolume()) {
Timber.tag(TAG).d("Increasing device sound volume from %d to %d", currentVolumeLevel(), currentVolumeLevel() + STEP);
audioManager.setStreamVolume(STREAM_MUSIC, currentVolumeLevel() + STEP, 0);
} else {
Timber.tag(TAG).d("Reached the maximum device volume.");
}
}
#Override
public void decreaseVolume() {
if (!isReachedMinVolume()) {
Timber.tag(TAG).d("Decreasing device sound volume from %d to %d", currentVolumeLevel(), currentVolumeLevel() - STEP);
audioManager.setStreamVolume(STREAM_MUSIC, currentVolumeLevel() - STEP, 0);
} else {
Timber.tag(TAG).d("Reached the preferred minimum volume");
}
}
I start playing each tone (frequency) with APP_DEFAULT_VOLUME and gradually increase tone. When user confirm he/she heard specific tone with specific volume, I want to calculate it's amplitude and log it for latter so I can review user hearing ...
But i have no clue how to do so!
All solutions I found was about reading data from microphone and calculate the amplitude to visualize it on screen ...
My scenario is much simpler. I have device volume recorded, frequency is fixed and recorded, and Audio is generated dynamically and is not a file.
Will anyone help me with this scenario ?
Thanks in advance.
I am a high school student currently working on an arduidroid project using potentiometers, bluetooth, and a guitar app. My issue is that when I play my guitar app that I get an error that won't crashes.
AudioFlinger could not create track, status: -12
Error -12 initializing AudioTrack
Error code -20 when initializing AudioTrack.
How do I make it so that I can play as many sounds as I want with 3 different buttons using a tone generator from this question.
void genTone(double freq){
// fill out the array
for (int i = 0; i < numSamples; ++i) {
sample[i]= 2*(i%(sampleRate/freq))/(sampleRate/freq)-1;
}
// 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_STEREO,
AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
audioTrack.play();
}
if(BString.isPressed()){
/*genTone(BStringFreq);
playSound();*/
final Thread thread = new Thread(new Runnable() {
public void run() {
genTone(BStringFreq);
handler.post(new Runnable() {
public void run() {
playSound();
}
});
}
});
thread.start();
}
}
Here are some snippets of my code. Please help me out here as I am lost on this subject.
I'm building an android app that pulses an icon - simple pulse, 2x size at loudest volume and 1x at no volume - based on audio. Worth noting my min api is 15.
The user selects the mode (file)to play and I use AudioTrack to play it back on an infinite loop. Each wav sample ranges from < second to 2 or 3 seconds. Audiotrack lets me set the volume and pitch in real-time based on user input (SoundPool wasn't correctly changing pitch in Kitkat).
As the volume changes within each audiotrack, I'm trying to shrink and grow the icon. So far I've tried visualizer to get the waveform and fft data as the track is playing, but I'm not sure that's correct.
Is there a way to get the (nearest possible) real-time db changes from an audiotrack? The wave form function seems to always be between 108 and 112, so I don't think I'm using it correctly. The easiest pulse.wav example is here
My audiotrack init using a byte[] from pcm data
AudioTrack mAudioTrack = new AudioTrack(AudioAudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, getMinBuffer(sound), AudioTrack.MODE_STATIC);
mAudioTrack.write(mSound, 0, mSound.length);
mAudioTrack.setLoopPoints(0, (int)(mSound.length / 4), -1);
My Visualizer
Visualizer mVisualizer = new Visualizer(mAudioTrack.getAudioSessionId());
mVisualizer.setEnabled(false);
mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener {
#Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
double sum = 0;
for (int i = 0; i < bytes.length; i++) {
sum += Math.abs(bytes[i]) * Math.abs(bytes[i]);
}
double volume = (double) Math.sqrt(1.0d * sum / bytes.length);
//THIS IS THE RESIZE FUNCTION//
//resizeHeart((double) volume);
System.out.println("Volume: " + volume); //always prints out between 108 and 112.
}
#Override
public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
//not sure what to do here.
}
}, Visualizer.getMaxCaptureRate() / 2, true, true);
mVisualizer.setEnabled(true);
The problem is that you're treating the bytes as samples even though you've specified a 16-bit sample size. Try something like this (note the abs is unnecessary since you're squaring anyway):
for (int i = 0; i < bytes.length/2; i+=2) {
int sample = bytes[i] << 8 || bytes[i+1];
sum += sample * sample;
}
I generate a PCM and want to loop the sound.
I follow the documentation, but Eclipse keep telling me that
08-05 15:46:26.675: E/AudioTrack(27686): setLoop invalid value: loopStart 0, loopEnd 44100, loopCount -1, framecount 11025, user 11025
here is my code:
void genTone() {
// fill out the array
for (int i = 1; i < numSamples - 1; i = i + 2) {
sample[i] = Math.sin(2 * Math.PI * i / (sampleRate / -300));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
for (double dVal : sample) {
short val = (short) (dVal * 32767);
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
//write it to audio Track.
audioTrack.write(generatedSnd, 0, numSamples);
audioTrack.setLoopPoints(0, numSamples, -1);
//from 0.0 ~ 1.0
audioTrack.setStereoVolume((float)0.5, (float)1); //change amplitude
}
public void buttonPlay(View v) {
audioTrack.reloadStaticData();
audioTrack.play();
}
please help ~~
From the documentation: "endInFrames loop end marker expressed in frames"
The log print indicates that your track contains 11025 frames, which is less than the 44100 that you're trying to specify as the end marker (for 16-bit stereo PCM audio, the frame size would be 4 bytes).
Another thing worth noting is that "the track must be stopped or paused for the position to be changed".
I using AudioTrack in static mode to reproduce the same signal over and over again.
I have followed the example in here and sometimes it works perfectly, but sometimes it throws this error and it produces no sound:
AudioTrack: start called from a thread
01-23 15:26:16.902: W/libutils.threads(1133): Thread (this=0x3973b8): don't call waitForExit() from this Thread object's thread. It's a guaranteed deadlock!
This is the source code. I'm trying to ensure that I call stop and reload the data for the next "play" execution.
public class SoundPlayer {
// originally from http://marblemice.blogspot.com/2010/04/generate-and-play-tone-in-android.html
private int numSamples;
private double sample[];
private byte generatedSnd[];
private AudioTrack audioTrack;
public SoundPlayer(float duration, int sampleRate, double freqOfTone) {
super();
this.numSamples = (int) (duration * sampleRate);
this.sample = new double[numSamples];
this.generatedSnd = new byte[2 * numSamples];
// 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);
}
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, numSamples,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
}
public void playSound() {
if ( audioTrack.getPlayState() == (AudioTrack.PLAYSTATE_PLAYING | AudioTrack.PLAYSTATE_PAUSED )) {
audioTrack.stop();
audioTrack.reloadStaticData();
}
Log.i("Audio", "playState: " + audioTrack.getPlayState());
audioTrack.play();
audioTrack.stop();
audioTrack.reloadStaticData();
}
}
If we open the android source code, it does not explains a lot:
void AudioTrack::start()
{
sp<AudioTrackThread> t = mAudioTrackThread;
LOGV("start");
if (t != 0) {
if (t->exitPending()) {
if (t->requestExitAndWait() == WOULD_BLOCK) {
LOGE("AudioTrack::start called from thread");
return;
}
}
t->mLock.lock();
}
Does anyone know how to handle this?
I had a similar problem once.
Simply put, if you happen to have stuff running in more than one thread you have to make sure that creating the AudioTrack and calling play() and stop() on it must happen in the same thread.
However, it doesn't mean that you have to create the audio samples in that thread, too. In case you use static audio data (AudioTrack.MODE_STATIC) you can preload or generate them elsewhere and then use it even multiple times throughout the lifetime of your app.