Audio routing between Android and PC produces white noise - android

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.

Related

audiotrack: playing noise for a raw pcm 16bit wav file

I am trying to play a wav file of size 230mb and 20 min whose properties are as below:
ffmpeg -i 1.wav
Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
I am learning how to use audiotrack.
I found two solutions to play this audio play using audiotrack.
Solution 1: the following plays the audio
int frequency = 44100;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration,audioEncoding);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency,
channelConfiguration, audioEncoding, bufferSize,
AudioTrack.MODE_STREAM);
int count = 0;
byte[] data = new byte[bufferSize];
try{
FileInputStream fileInputStream = new FileInputStream(listMusicFiles.get(0).listmusicfiles_fullfilepath);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
audioTrack.play();
while((count = dataInputStream.read(data, 0, bufferSize)) > -1){
audioTrack.write(data, 0, count);
}
audioTrack.stop();
audioTrack.release();
dataInputStream.close();
fileInputStream.close();
}
catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Second Solution: Playing noise
int frequency = 44100;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration,audioEncoding);
short[] audiodata = new short[bufferSize];
try {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(
listMusicFiles.get(0).listmusicfiles_fullfilepath)));
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency,
channelConfiguration, audioEncoding, bufferSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
while (dis.available() > 0) {
int i = 0;
while (dis.available() > 0 && i < audiodata.length) {
audiodata[i] = dis.readShort();
i++;
}
audioTrack.write(audiodata, 0, audiodata.length);
}
dis.close();
} catch (Throwable t) {
Log.e("AudioTrack", "Playback Failed");
}
I am new to short sample and byte samples. I tried to understand but it not so easy.
I could understand the first solution is using byte sample and the second solution is using short samples.
So why is the second solution not working.
The default size of a short type is two bytes. You might have a look at this documentation as well.
The audio track has a recommended buffer size and sample rate which can be found using the way suggested here in this answer. Please have a look.
However, it is important to play the audio using the recommended sample rate which is 44100 Hz in your case and the recommended buffer size that you get using the following code segment.
AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding)
In your implementation with short array, the buffer size is doubled and hence its creating noises in case of playing the audio. I would suggest, you might consider changing the buffer size by dividing the size by two in your implementation using short.
short[] audiodata = new short[(int) bufferSize / 2];
Hope you have understood the problem.

AudioRecord produces choppy audio

I am using AudioRecord to capture audio packets and stream them to a voice recognition server.
In my Galaxy Note 4, Android M device, it works perfectly fine.
However, when I use other devices (Nexus 7/Android L and HTC combo/android ICS) the resulting audio is choppy, with glitchy noises in the sound every half a second that spoil the speech recognition process at the server.
I know this is a complicated topic, does somebody know how to deal with this audio capture irregularities in android?
This is my code setup:
private static final int AUDIO_SOURCE = MediaRecorder.AudioSource.VOICE_RECOGNITION;
private static final int SAMPLING_RATE = 16000; //44100,
private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
private static final int BIT_RATE = AudioFormat.ENCODING_PCM_16BIT;
bufferSize = AudioRecord.getMinBufferSize(SAMPLING_RATE,
CHANNEL_CONFIG,
BIT_RATE;
audioBuffer = new byte[bufferSize];
AudioRecord recorder = new AudioRecord(AUDIO_SOURCE,
SAMPLING_RATE,
CHANNEL_CONFIG,
BIT_RATE,
bufferSize);
recorder.startRecording();
while (isRecording) {
short[] buffer = new short[bufferSize];
int shorts_recorded = recorder.read(buffer, 0, buffer.length);
byte[] audioBytes = new byte[bufferSize*2]; //bufferSize*2
ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(buffer);//runningBuffer.get(0));
Integer.toString(audioBytes.length)+","+audioBuffer.length);
handler.onAudioDataCapture(audioBytes); //expose audio data to upload callback interface
proceed();
}

Change Bitrate of AudioRecord on Android

How can on Android the bits per sample be changed from 16 bit to 8 bit (or other bitrates) in a AudioRecord object when recording in wav format?
This doesn't work (as discussed for example here: Using AudioRecord with 8-bit encoding in android):
private static int RECORDER_AUDIO_ENCODING =
AudioFormat.ENCODING_PCM_8BIT; -->ERROR: Invalid Audio Format.
bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,RECORDER_SAMPLERATE,
RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
With the header:
private void WriteWaveFileHeader(
FileOutputStream out, long totalAudioLen,
long totalDataLen, long longSampleRate, int channels,
long byteRate) throws IOException {
byte[] header = new byte[44];
header[0] = 'R'; // RIFF/WAVE header
...header content...
header[32] = (byte) (2 * 16 / 8); // block align
header[33] = 0;
header[34] = (byte)RECORDER_BPP; // bits per sample, set to 8
...header content...
out.write(header, 0, 44);
}
You need to record 16 Bit samples by using AudioFormat.ENCODING_PCM_16BIT because 8 Bit encoding is not generally working on Android devices.
Then convert your 16 Bit buffer to 8 Bit on the fly prior to writing it into a wave sound file. But because 8 Bit wave files use unsigned integers (0-255) with silence being 128 instead of 0, my suggested conversion corrects for that. The following is just a code skeleton:
// Your source above already defined a value for bufferSize and the recorder object
sampleBufferShorts = new short[bufferSize];
sampleBufferBytes = new short[bufferSize];
int numberOfShortsRead = recorder.read(samleBufferShorts, 0, bufferSize);
if (numberOfShortsRead != ERROR_INVALID_OPERATION && numberOfShortsRead != ERROR_BAD_VALUE) {
for (int i = 0 ; i < numberOfShortsRead ; ++i)
sampleBufferBytes[i] = (byte)((sampleBufferShorts[i]>>8)+128);
}
See https://stackoverflow.com/a/18777515/1986583 for a useful explanation why additional dithering would improve the resulting quality.
Its not possible, you'll have to postprocess your .wav file.

getting noise as output instead of mixed sounds

When I run the following code I have no sound as output instead it gives me noise.
I have two audio files in my resource folder and using 1 inputstream these are converted to bytearray.If I add mp3 then the app closes unfortunately.
private void mixSound() throws IOException {
AudioTrack audioTrack =new AudioTrack(AudioManager.STREAM_MUSIC,44100,AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, 44100, AudioTrack.MODE_STREAM);
Log.i(tag,"inside mixSound");
InputStream in1=getResources().openRawResource(R.raw.cut1); s
InputStream in2=getResources().openRawResource(R.raw.cut2);
byte[] music1 = null;
music1= new byte[in1.available()];
Log.i(tag,"in1");
music1=convertStreamToByteArray(in1);
in1.close();
byte[] music2 = null;
music2= new byte[in2.available()];
music2=convertStreamToByteArray(in2);
in2.close();
byte[] output = new byte[music1.length];
audioTrack.play();
for(int i=0; i < output.length; i++){
float samplef1 = music1[i] / 128.0f; // 2^7=128
float samplef2 = music2[i] / 128.0f;
float mixed = samplef1 + samplef2;
// reduce the volume a bit:
mixed *= 0.8;
// hard clipping
if (mixed > 1.0f) mixed = 1.0f;
if (mixed < -1.0f) mixed = -1.0f;
byte outputSample = (byte)(mixed * 128.0f);
output[i] = outputSample;
} //for loop
audioTrack.write(output, 0, output.length);
}
public static byte[] convertStreamToByteArray(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[10240];
int i = Integer.MAX_VALUE;
Log.i(tag,"in csb");
while ((i = is.read(buff, 0, buff.length)) > 0) {
baos.write(buff, 0, i);
}
return baos.toByteArray();
}
Thank you for help in advance.
A few issues here...
If you are working with 16-bit PCM audio (which by your initialization of AudioTrack it appears you are), then you should access your source audio and write to your AudioTrack in shorts (which are 16 bits) rather than bytes (8 bits). If you must read bytes from your source, you'll need to read two of them at a time in your loop and do something like
short curSample = (myByteArr[i] << 8) | myByteArr[i+1];
and then write the result to your stored buffer. This is assuming you have 16-bit shorts stored in the files you're reading from, which you should. Better to just read those as what they are, though.
Using AudioTrack.MODE_STREAM implies you will write continuously to the buffer while audio is playing. The way you've done it here fills the entire buffer and then writes it to the AudioTrack. If this is a one-off playback, you should probably use AudioTrack.MODE_STATIC.
This is a corner case, but consider what happens if mixed == 1.0f. If you multiply that by 128.0f and truncate to byte, you'll get 128, which is actually beyond the range of a signed byte (because of 0, the range is [-128, 127]).
I believe problem #1 is the source of your noise. You need to keep your 16-bit PCM data intact rather than splitting it up.

Audio playing back at the wrong speed using FFmpeg on Android

General problem in the following:
I decode the audio as follows:
ReSampleContext* rsc = av_audio_resample_init(
1, aCodecCtx->channels,
aCodecCtx->sample_rate, aCodecCtx->sample_rate,
av_get_sample_fmt("u8"), aCodecCtx->sample_fmt,
16, 10, 0, 1);
while (av_read_frame(pFormatCtx, &packet)>= 0) {
if (aCodecCtx->codec_type == AVMEDIA_TYPE_AUDIO) {
int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 2;
int size=packet.size;
int decoded = 0;
while(size > 0) {
int len = avcodec_decode_audio3(aCodecCtx, pAudioBuffer,
&data_size, &packet);
//Сonvert audio to sample 8bit
out_size = audio_resample(rsc, outBuffer, pAudioBuffer, len);
jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
memcpy(bytes, outBuffer, out_size);
(*env)->ReleaseByteArrayElements(env, array, bytes, 0);
(*env)->CallStaticVoidMethod(env, cls, mid, array, out_size, number);
size -= len;
number++;
}
}
}
Next release it AudioTrack. After that, I hear that song that was necessary, but with noise and speed of 2 times larger. In what may be the problem?
UPDATE:
This is Java code:
public static AudioTrack track;
public static byte[] bytes;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int bufSize = 2048;
track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_8BIT, bufSize, AudioTrack.MODE_STREAM);
bytes = new byte[bufSize];
Thread mAudioThread = new Thread(new Runnable() {
public void run() {
int res = main(2, "/sdcard/muzika_iz_reklami_bmw_5_series_-_bmw_5_series.mp3", bytes);
System.out.println(res);
}
});
mAudioThread.setPriority(Thread.MAX_PRIORITY);
mAudioThread.start();
}
private static void play(byte[] play, int length, int p) {
if (p==0){
track.play();
}
track.write(play, 0, length);
}
Perhaps your AudioTrack is expecting stereo data but you are sending it mono. You could try setting your AudioTrack channel configuration to CHANNEL_OUT_MONO.
This could be a problem of sampling rate mismatch. May be you are creating audioTrack with a lesser sampler rate compared to actual rate.
It could be also a problem with the AudioTrack channel configuration, as Matthew mentioned.
May be you should try something like I have answered in this question.
Because the way you are interacting with JNI from Java, I am not sure, whether that will work. I have little knowledge about JNI. But my code works with me and which is what I am currently using with my app.

Categories

Resources