I'm building an application that is always recording audio from the microphone, whenever the audio reaches a certain threshold I perform a certain action.
However how should I calc the appropriate volume for the threshold ? I've a static volume coded which works well across some devices but not all devices (in somes cases it is too sensitive or vice versa).
I'm using AudioRecord, here's part of the code:
int bufferSize = AudioRecord.getMinBufferSize(Constants.RECORDING_FREQUENCY,Constants.channelConfiguration,Constants.audioEncoding);
AudioRecord audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC,Constants.RECORDING_FREQUENCY,Constants.channelConfiguration,Constants.audioEncoding, bufferSize);
short[] buffer = new short[bufferSize];
while(true) {
int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
for (int i = 0; i < bufferReadResult; i++) {
currentVolume = java.lang.Math.abs(buffer[i]);
if (currentVolume > Constants.NO_VOLUME_AMPLITUDE)
// ALRIGHT ! This is what I'm looking for :D
}
}
So, my question is: how do I calculate Constants.NO_VOLUME_AMPLITUDE instead of having it hard coded ?
Thanks so much in advance,
Ze
Related
I am trying to send audio between windows and android, I was successfully able to do that windows to windows but when I stream audio from android, it produces a white noise only. I think it is an issue with the AudioFormat in android and Windows because when I changed the sample Bits to 8 I guess, I heard the voice in one side of my headphones but then it went away too.
On Android Side
int BUFFER_MS = 15; // do not buffer more than BUFFER_MS milliseconds
int bufferSize = 48000 * 2 * BUFFER_MS / 1000;
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, 2,
AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
byte[] buffer = new byte[bufferSize];
int bytesRead;
audioTrack.play();
while (socket.isConnected()) {
bytesRead = inputStream.read(buffer, 0, buffer.length);
audioTrack.write(buffer,0,bytesRead);
}
On Windows Side
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
throw new LineUnavailableException(
"The system does not support the specified format.");
}
TargetDataLine audioLine = AudioSystem.getTargetDataLine(format);
audioLine.open(format);
audioLine.start();
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while (socket.isConnected()) {
bytesRead = audioLine.read(buffer, 0, buffer.length);
outputStream.write(buffer,0,bytesRead);
}
and getAudioFormat function is
AudioFormat getAudioFormat() {
float sampleRate = 48000;
int sampleSizeInBits = 16;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,
bigEndian);
}
Only hearing a white noise, if someone can help please do.
Okayyyy So I found out the problem. I just had to put bigEndian to false -_-
It's the byte order difference. I don't understand why it's different in android and pc but seems like it does the trick.
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;
}
Scenario:
There is a imageview which implements ontouchlistener. When the user touches on the screen i am starting a separate thread which will be generating sin wave and i am writing it to the audiotrack here is the code of generation of sin wave.
t = new Thread() {
public void run() {
setPriority(Thread.MAX_PRIORITY);
// set the buffer size
int buffsize = AudioTrack.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
// create an audiotrack object
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
samplerate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, buffsize,
AudioTrack.MODE_STREAM);
float amp = (float)10000;
double twopi = 2*Math.PI;
// start audio
audioTrack.play();
short samples[] = new short[buffsize];
// synthesis loop
while(isRunning){
for(int i=0; i < buffsize; i++){
samples[i] = (short) (amp*Math.sin(ph));
ph += (twopi*fr)/sr;
if (ph > twopi)
{
ph -= twopi;
}
if(isRunning==false)
break;
}
if(isRunning==false)
break;
audioTrack.write(samples, 0, buffsize);
}
audioTrack.stop();
audioTrack.release();
}
};
As the user moves his hand on ACTION_MOVE call back i am changing the frequency . Frequency mainly varies on the distance moved by the user from the initial point.
isRunnig will become true on ACTION_UP callback.
Problem:
I am missing the some of the frequencies. I mean if the user moves his hand very fastly some of the frequency values are getting missed.
This is mainly because of audioTrack.write(samples, 0, buffsize) This function takes some time..
i.e if i get 4 time ACTION_MOVE callback, 4 points i will get, 4 time the frequency gets varied. But audio track gets updated 2 times. What i have to do to remove this lag. Is there any better approach to do this.
I'm recording male voice, when I click playback button how to convert male voice in to female voice and play it in female voice. I refer some links I tried sound pool to change voice by changing the float rate but I'm not getting female voice and change frequency for audio track.
Here is my code:
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
loaded = true;
}
});
soundID = soundPool.load(mFileName, 1);
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
float actualVolume = (float) audioManager
.getStreamVolume(AudioManager.STREAM_MUSIC);
float maxVolume = (float) audioManager
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = actualVolume / maxVolume;
/* if (loaded) {
iv.setImageDrawable(getResources().getDrawable(R.drawable.palyrec));
soundPool.play(soundID, volume, volume, 1, 0, 1.8f);
Log.e("Test", "Played sound");
}*/
int streamID = -1;
do {
iv.setImageDrawable(getResources().getDrawable(R.drawable.palyrec));
streamID = soundPool.play(soundID, volume, volume, 1, 0, 1.7f);
Log.e("Test", "Played sound");
} while(streamID==0);
Changing frequency Code:
Integer[] freqset = {11025, 16000, 22050, 44100};
File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");
int shortSizeInBytes = Short.SIZE/Byte.SIZE;
int bufferSizeInBytes = (int)(file.length()/shortSizeInBytes);
short[] audioData = new short[bufferSizeInBytes];
try {
InputStream inputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);
int i = 0;
while(dataInputStream.available() > 0){
audioData[i] = dataInputStream.readShort();
i++;
}
dataInputStream.close();
int sampleFreq = (Integer)spFrequency.getSelectedItem();
AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
sampleFreq,
AudioFormat .CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSizeInBytes,
AudioTrack.MODE_STREAM);
audioTrack.play();
audioTrack.write(audioData, 0, bufferSizeInBytes);
How it is possible to convert to female voice in android ?
I believe there might be a misunderstanding on the term frequency: the sample rate frequency is not the same as the perceived pitch frequency.
Changing sample rate frequency will change both pitch and time, like an audio tape played faster (for those old enough to remember :-)).
Changing pitch frequency upwards will not make it sound faster, but you will rapidly notice the digital artefacts introduced (chipmunk voice).
Might sound a bit better when going downwards (like in Luka's video).
But, in both case, the pitch change you have to apply is quite drastic and will produce some weird side effects.
The closest would be looking at the results obtained by software specialized in harmonizing or tuning signing vocal tracks. These softs look at the formants and do not change them to change the pitch.
e.g.:
https://documentation.apple.com/en/logicstudio/effects/index.html#chapter=10%26section=3%26tasks=true
One of the most famous soft for this :
http://www.celemony.com/en/melodyne/what-is-melodyne
I think that in this case, you might want to slightly change some formants of the voice (which contain some of the characteristics our brain uses to recognize someone's voice) and the overall pitch but independently.
Also note the formants are numbered (F0, F1, F2, F3, ...) and F1 and F2 allow us to tell the difference between the vowel sounds. http://home.cc.umanitoba.ca/~krussll/phonetics/acoustic/formants.html
As Merlevede mentioned, I believe a one size-fits-all algorithm is quite difficult to come up.
Afaict, there are no libraries on android doing this...
You need to work with the frequencies. According to:http://en.wikipedia.org/wiki/Voice_frequency andhttp://www.axiomaudio.com/blog/audio-oddities-frequency-ranges-of-male-female-and-children%E2%80%99s-voices/
A male voice's frequency is 85 to 180 Hz while a female's 165 to 255 Hz. From this, if you simple increase the overall sound frequency, you might get a female-like voice.
The above will not get you a perfect result. To get a perfect result you need to research more. You can get this also by by trial and error. Get a male to say some words. Get a female to say some words. Now, compare the frequencies. Obviously, the frequency of each persons voice is different.
Finally check out this video: http://www.youtube.com/watch?v=hD0HAo2iHbE
I'm getting buffer overflow while RECORDING with my app. The recording is performed in a Service. I could not figure out why I'm getting this message from AudioFlinger.
Below I instantiate the AudioRecord object and set it's callbacks.
bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
aRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize);
aRecorder.setRecordPositionUpdateListener(updateListener);
bytesPerSample = bitsPerSample / 8;
int bytesPerFrame = nChannels * bytesPerSample;
framePeriod = bufferSize / bytesPerFrame; // nr of frames that can be kept in a bufferSize dimension
int result = aRecorder.setPositionNotificationPeriod(framePeriod);
buffer = new byte[bufferSize];
The audioRecord callback:
private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener(){
public void onPeriodicNotification(AudioRecord recorder){
int result = aRecorder.read(buffer, 0, buffer.length);
}
public void onMarkerReached(AudioRecord recorder)
{}
};
I suspect the problem is related to the:aRecorder.setPositionNotificationPeriod(framePeriod); - maybe the period is too big for this bufferSize and a faster(smaller) period will solve the issue.
Could someone tells me how to get rid of the buffer overflow?
To fix that issue, change the buffer size of AudioRecord to 2 times the minimum buffer size.
You can use AudioRecord.getMinBufferSize() static method. This will give you the minimum buffer size to use for your current format.
The syntax of getMinBufferSize() method is:
public static int getMinBufferSize (
int sampleRateInHz, int channelConfig, int audioFormat)
Anything less than this number will result in failure while creating the AudioRecord object.
You should have been reducing the buffer size, so as not to overwhelm the audio subsystem with demands for data.
Remember to put the overridden methods (#Override) for audioRecord callback as follows:
private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener(){
#Override
public void onPeriodicNotification(AudioRecord recorder){
int result = aRecorder.read(buffer, 0, buffer.length);
}
#Override
public void onMarkerReached(AudioRecord recorder)
{}
};
I recommend to read the post: Android audio recording, part 2
One more thing that you could try is to use threads on recording and the other process on the recorded bytes, thus avoiding too much overload on the main UI thread.
The open source sample code for this approach: musicg_android_demo
Check this post for more - android-audiorecord-class-process-live-mic-audio-quickly-set-up-callback-func
Thats because :
framePeriod = bufferSize / bytesPerFrame;
You need to multiply and not divide your buffersize.
Try with :
framePeriod = bufferSize * bytesPerFrame;
And if you need a sample : here is a complete audio capture class
hope it helps