Mix audio in android - android

I tried to follow this link:
http://mobilengineering.blogspot.com/2012/06/audio-mix-and-record-in-android.html?showComment=1369622288028#c2333829870074273419
But after mixing audio files, file (mixed.wav) on sdcard can not be played, I do not know why.
Can you help me?. Thank you very much ..
This my code:
public class MainActivity extends Activity {
public static final int FREQUENCY = 44100;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
mixSound();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
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);
InputStream in1 = getResources().openRawResource(R.raw.media_b);
InputStream in2 = getResources().openRawResource(R.raw.media_c);
byte[] arrayMusic1 = null;
arrayMusic1 = new byte[in1.available()];
arrayMusic1 = createMusicArray(in1);
in1.close();
byte[] arrayMusic2 = null;
arrayMusic2 = new byte[in2.available()];
arrayMusic2 = createMusicArray(in2);
in2.close();
byte[] output = new byte[arrayMusic1.length];
audioTrack.play();
for (int i = 0; i < output.length; i++) {
float samplef1 = arrayMusic1[i] / 128.0f;
float samplef2 = arrayMusic2[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;
}
audioTrack.write(output, 0, output.length);
convertByteToFile(output);
}
public static byte[] createMusicArray(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[10240];
int i = Integer.MAX_VALUE;
while ((i = is.read(buff, 0, buff.length)) > 0) {
baos.write(buff, 0, i);
}
return baos.toByteArray(); // be sure to close InputStream in calling function
}
public static void convertByteToFile(byte[] fileBytes) throws FileNotFoundException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Environment.getExternalStorageDirectory().getPath()+"/mixed.wav"));
try {
bos.write(fileBytes);
bos.flush();
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

What you're outputting is just the PCM data. A valid WAV file also needs a header:
Offset Size Name Description
------------------------------------------------------------------------
0 4 ChunkID Contains the letters "RIFF" in ASCII form
(0x52494646 big-endian form).
4 4 ChunkSize 36 + SubChunk2Size, or more precisely:
4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
This is the size of the rest of the chunk
following this number. This is the size of the
entire file in bytes minus 8 bytes for the
two fields not included in this count:
ChunkID and ChunkSize.
8 4 Format Contains the letters "WAVE"
(0x57415645 big-endian form).
12 4 Subchunk1ID Contains the letters "fmt "
(0x666d7420 big-endian form).
16 4 Subchunk1Size 16 for PCM. This is the size of the
rest of the Subchunk which follows this number.
20 2 AudioFormat PCM = 1 (i.e. Linear quantization)
Values other than 1 indicate some
form of compression.
22 2 NumChannels Mono = 1, Stereo = 2, etc.
24 4 SampleRate 8000, 44100, etc.
28 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8
32 2 BlockAlign == NumChannels * BitsPerSample/8
The number of bytes for one sample including
all channels. I wonder what happens when
this number isn't an integer?
34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc.
2 ExtraParamSize if PCM, then doesn't exist
X ExtraParams space for extra parameters
36 4 Subchunk2ID Contains the letters "data"
(0x64617461 big-endian form).
40 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
This is the number of bytes in the data.
You can also think of this as the size
of the read of the subchunk following this
number.
After this you write the PCM data.
(Reference).

Related

track gets very slow after generating it in my code

what could have made this generation of a track get too slow?
Basically I'm creating a track in the audio format: "wav".
But after completing the track it is in slow motion.
What can set the speed of a track?
resume of the code:
for (int i = 0; i < output.length; i++)
{
if ( i <= arrayMusic1.length - 1)
samplef1 = arrayMusic1[i] / 128.0f;
if ( i <= arrayMusic2.length - 1)
samplef2 = arrayMusic2[i] / 128.0f;
output[i] = outputSample;
}
public void saveToFile(byte[] output, String globalName, String nameMix)
{
long mySubChunk1Size = 16;
int myBitsPerSample = 16;
int myFormat = 1;
long myChannels = 1;
long mySampleRate = 44400;
long myByteRate = mySampleRate * myChannels * myBitsPerSample /8;
int myBlockAlign = (int) (myChannels * myBitsPerSample/8);
//long teste = output.length;
long myDataSize = output.length - 100000;//output.length //aqui ta o problema
long myChunk2Size = myDataSize * myChannels * myBitsPerSample/8;
long myChunkSize = 36 + myChunk2Size;
OutputStream os = new FileOutputStream(new File(Environment.getExternalStorageDirectory()+ "/ssmultitrackPlus/"+globalName.trim()+"/"+nameMix+".wav"));
BufferedOutputStream bos = new BufferedOutputStream(os);
DataOutputStream outFile = new DataOutputStream(bos);
outFile.writeBytes("RIFF"); // 00 - RIFF
outFile.write(intToByteArray((int)myChunkSize), 0, 4); // 04 - how big is the rest of this file?
outFile.writeBytes("WAVE"); // 08 - WAVE
outFile.writeBytes("fmt "); // 12 - fmt
outFile.write(intToByteArray((int)mySubChunk1Size), 0, 4); // 16 - size of this chunk
outFile.write(shortToByteArray((short)myFormat), 0, 2); // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation
outFile.write(shortToByteArray((short)myChannels), 0, 2); // 22 - mono or stereo? 1 or 2? (or 5 or ???)
outFile.write(intToByteArray((int)mySampleRate), 0, 4); // 24 - samples per second (numbers per second)
outFile.write(intToByteArray((int)myByteRate), 0, 4); // 28 - bytes per second
outFile.write(shortToByteArray((short)myBlockAlign), 0, 2); // 32 - # of bytes in one sample, for all channels
outFile.write(shortToByteArray((short)myBitsPerSample), 0, 2); // 34 - how many bits in a sample(number)? usually 16 or 24
outFile.writeBytes("data"); // 36 - data
outFile.write(intToByteArray((int)myDataSize), 0, 4); // 40 - how big is this data chunk
outFile.write(output); // 44 - the actual data itself - just a long string of numbers
outFile.flush();
outFile.close();
}
Byterate must be mySampleRate*myChannels*2 and function to write byterate mast be look like:
writeInt(output, mySampleRate*2); // byte rate because 16bit and 1 channel
public static void writeInt(final DataOutputStream output, final int value) throws IOException {
output.write(value >> 0);
output.write(value >> 8);
output.write(value >> 16);
output.write(value >> 24);
}
Standart samplerate audio CD quality is 44100. If audio slows down without any artifacts like "popcorn" then problem with samplerate or byterate in header. You may look what actually was recorded with some binary/hex reader/writer app like GHex. Also for PCM there should be choosed 1.

MediaMuxer unable to make MP4s that are streamable

I'm editing an MP4 on Android using MediaExtractor to fetch audio and video tracks then creating a new file using MediaMuxer. It works fine. I can play the new MP4 on the phone (and other players) but am unable to stream the file on the web. When I stop the MediaMuxer it generates a log message
"The mp4 file will not be streamable."
I looked at the underlying native code (MPEG4Writer.cpp) and it would appear that the writer is having trouble calculating the needed moov box size. It tries to guess using some heuristic if a bit rate is not supplied as a parameter to the writer. The problem is the MediaMuxer doesn't provider the ability to set MPEG4Writer's parameters. Am I missing something or am I stuck looking a some other means of generating the file (or header)? Thanks.
In MPEG4Writer.cpp:
// The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2,
// where 1MB is the common file size limit for MMS application.
// The default MAX _MOOV_BOX_SIZE value is based on about 3
// minute video recording with a bit rate about 3 Mbps, because
// statistics also show that most of the video captured are going
// to be less than 3 minutes.
This is a bad assumption on how MediaMuxer might be used. We are recording a max of 15 seconds of higher res video and MIN_MOOV_BOX_SIZE is way too small. So to make the file streamable I have to rewrite the file to move the moov header before mdat and patch up some offsets. Here is my code. It's not great. Error paths aren't handled correctly and it makes assumptions about the order of the boxes.
public void fastPlay(String srcFile, String dstFile) {
RandomAccessFile inFile = null;
FileOutputStream outFile = null;
try {
inFile = new RandomAccessFile(new File(srcFile), "r");
outFile = new FileOutputStream(new File(dstFile));
int moovPos = 0;
int mdatPos = 0;
int moovSize = 0;
int mdatSize = 0;
byte[] boxSizeBuf = new byte[4];
byte[] pathBuf = new byte[4];
int boxSize;
int dataSize;
int bytesRead;
int totalBytesRead = 0;
int bytesWritten = 0;
// First find the location and size of the moov and mdat boxes
while (true) {
try {
boxSize = inFile.readInt();
bytesRead = inFile.read(pathBuf);
if (bytesRead != 4) {
Log.e(TAG, "Unexpected bytes read (path) " + bytesRead);
break;
}
String pathRead = new String(pathBuf, "UTF-8");
dataSize = boxSize - 8;
totalBytesRead += 8;
if (pathRead.equals("moov")) {
moovPos = totalBytesRead - 8;
moovSize = boxSize;
} else if (pathRead.equals("mdat")) {
mdatPos = totalBytesRead - 8;
mdatSize = boxSize;
}
totalBytesRead += inFile.skipBytes(dataSize);
} catch (IOException e) {
break;
}
}
// Read the moov box into a buffer. This has to be patched up. Ug.
inFile.seek(moovPos);
byte[] moovBoxBuf = new byte[moovSize]; // This shouldn't be too big.
bytesRead = inFile.read(moovBoxBuf);
if (bytesRead != moovSize) {
Log.e(TAG, "Couldn't read full moov box");
}
// Now locate the stco boxes (chunk offset box) inside the moov box and patch
// them up. This ain't purdy.
int pos = 0;
while (pos < moovBoxBuf.length - 4) {
if (moovBoxBuf[pos] == 0x73 && moovBoxBuf[pos + 1] == 0x74 &&
moovBoxBuf[pos + 2] == 0x63 && moovBoxBuf[pos + 3] == 0x6f) {
int stcoPos = pos - 4;
int stcoSize = byteArrayToInt(moovBoxBuf, stcoPos);
patchStco(moovBoxBuf, stcoSize, stcoPos, moovSize);
}
pos++;
}
inFile.seek(0);
byte[] buf = new byte[(int) mdatPos];
// Write out everything before mdat
inFile.read(buf);
outFile.write(buf);
// Write moov
outFile.write(moovBoxBuf, 0, moovSize);
// Write out mdat
inFile.seek(mdatPos);
bytesWritten = 0;
while (bytesWritten < mdatSize) {
int bytesRemaining = (int) mdatSize - bytesWritten;
int bytesToRead = buf.length;
if (bytesRemaining < bytesToRead) bytesToRead = bytesRemaining;
bytesRead = inFile.read(buf, 0, bytesToRead);
if (bytesRead > 0) {
outFile.write(buf, 0, bytesRead);
bytesWritten += bytesRead;
} else {
break;
}
}
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} finally {
try {
if (outFile != null) outFile.close();
if (inFile != null) inFile.close();
} catch (IOException e) {}
}
}
private void patchStco(byte[] buf, int size, int pos, int moovSize) {
Log.e(TAG, "stco " + pos + " size " + size);
// We are inserting the moov box before the mdat box so all of
// offsets in the stco box need to be increased by the size of the moov box. The stco
// box is variable in length. 4 byte size, 4 byte path, 4 byte version, 4 byte flags
// followed by a variable number of chunk offsets. So subtract off 16 from size then
// divide result by 4 to get the number of chunk offsets to patch up.
int chunkOffsetCount = (size - 16) / 4;
int chunkPos = pos + 16;
for (int i = 0; i < chunkOffsetCount; i++) {
int chunkOffset = byteArrayToInt(buf, chunkPos);
int newChunkOffset = chunkOffset + moovSize;
intToByteArray(newChunkOffset, buf, chunkPos);
chunkPos += 4;
}
}
public static int byteArrayToInt(byte[] b, int offset)
{
return b[offset + 3] & 0xFF |
(b[offset + 2] & 0xFF) << 8 |
(b[offset + 1] & 0xFF) << 16 |
(b[offset] & 0xFF) << 24;
}
public void intToByteArray(int a, byte[] buf, int offset)
{
buf[offset] = (byte) ((a >> 24) & 0xFF);
buf[offset + 1] = (byte) ((a >> 16) & 0xFF);
buf[offset + 2] = (byte) ((a >> 8) & 0xFF);
buf[offset + 3] = (byte) (a & 0xFF);
}
Currently MediaMuxer does not create streamable MP4 files
You can try Intel INDE on https://software.intel.com/en-us/intel-inde and Media Pack for Android which is a part of INDE, tutorials on https://software.intel.com/en-us/articles/intel-inde-media-pack-for-android-tutorials. It has a sample that shows how to use media pack to create and stream files over the network
For example for camera streaming it have sample CameraStreamerActivity.java
public void onCreate(Bundle icicle) {
capture = new CameraCapture(new AndroidMediaObjectFactory(getApplicationContext()), progressListener);
parameters = new StreamingParameters();
parameters.Host = getString(R.string.streaming_server_default_ip);
parameters.Port = Integer.parseInt(getString(R.string.streaming_server_default_port));
parameters.ApplicationName = getString(R.string.streaming_server_default_app);
parameters.StreamName = getString(R.string.streaming_server_default_stream);
parameters.isToPublishAudio = false;
parameters.isToPublishVideo = true;
}
public void startStreaming() {
configureMediaStreamFormat();
capture.setTargetVideoFormat(videoFormat);
capture.setTargetAudioFormat(audioFormat);
capture.setTargetConnection(prepareStreamingParams());
capture.start();
}
In addition there are simular samples for files streaming or game process capturing and streaming

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.

Amplitude from AudioRecord

I have some code that is supposed to be getting the amplitude from an AudioRecord. Problem is that the math is only returning -Infinity. Can I get some more eyes to look at it with me please:
private class measureSnoreAudio extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
Log.d(TAG, "Creating the buffer of size " + BUFFER_SIZE);
byte[] buffer = new byte[BUFFER_SIZE];
Log.d(TAG, "Creating the AudioRecord");
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDING_RATE, CHANNEL, FORMAT, BUFFER_SIZE * 10);
Log.d(TAG, "AudioRecord recording...");
recorder.startRecording();
while (isRecordingSnore) {
// read the data into the buffer
int read = recorder.read(buffer, 0, buffer.length);
int amplitude = (buffer[0] & 0xff) << 8 | buffer[1];
// Determine amplitude
double amplitudeDb = 20 * Math
.log10(Math.abs(amplitude) / 32768);
String dbString = String.valueOf(amplitudeDb);
Log.d("Snore DB", "dB " + dbString);
//TextView textAmplitude = (TextView) findViewById(R.id.tvAmplitude);
//textAmplitude.setText(dbString);
}
Log.d(TAG, "AudioRecord finished recording");
return null;
}
}
double amplitudeDb = 20 * Math.log10(Math.abs(amplitude) / 32768);
I think maybe the problem is from Math.abs(amplitude) / 32768, amplitude is integer, so Math.abs(amplitude) will also return integer, as Math.abs(amplitude) is less than 32768 (perhaps I am not correct, byte is maximum 2^7 - 1, can here amplitude bigger than 32768? ). So Math.abs(amplitude) / 32768 is equal to 0. Log10(0) is -Infinity, I have tested with a Java project in Eclipse.
You can change to
double amplitudeDb = 20 * Math.log10((double)Math.abs(amplitude) / 32768);

Sample code for Android AudioTrack Mixing

I have two PCM sound file in resource folder. I used inputstream and converted them into bytearray.
Then I processed them by normalized and adding music1 and music2 and output to the byte array output. Finally, put the output array and feed it to the AudioTrack.
Obviously, I don't hear anything and something is wrong.
private void mixSound() throws IOException {
InputStream in1=getResources().openRawResource(R.raw.cheerapp2);
InputStream in2=getResources().openRawResource(R.raw.buzzer2);
byte[] music1 = null;
music1= new byte[in1.available()];
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;
audioTrack.write(output, 0, i);
} //for loop
public static byte[] convertStreamToByteArray(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[10240];
int i = Integer.MAX_VALUE;
while ((i = is.read(buff, 0, buff.length)) > 0) {
baos.write(buff, 0, i);
}
return baos.toByteArray(); // be sure to close InputStream in calling function
}
I tried your code (substituting in some audio files of my own). I initialised an AudioTrack instance like this, hopefully this is similar to how you did it:
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, 44100, AudioTrack.MODE_STREAM);
And tried running it. It made a high pitched noise, that got lower as time went on. I checked the code and the problem is that you are writing the entire output byte array to the audioTrack on every iteration of the loop in your mixSound() method.
the line
audioTrack.write(output, 0, i);
needs moved outside the loop and to be changed to
audioTrack.write(output, 0, output.length);
So you mix both files together into the output byte array, then write the whole thing at once.
So the code for the working mixSound method looks like this:
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);
InputStream in1=getResources().openRawResource(R.raw.track1);
InputStream in2=getResources().openRawResource(R.raw.track2);
byte[] music1 = null;
music1= new byte[in1.available()];
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);
}

Categories

Resources