Merge branch 'audiorecorder-1.0.8'

This commit is contained in:
Alexey Kuznetsov 2016-03-13 13:44:34 +03:00
commit 957208380f
9 changed files with 214 additions and 124 deletions

View file

@ -8,8 +8,8 @@ android {
applicationId "com.github.axet.audiorecorder"
minSdkVersion 16
targetSdkVersion 23
versionCode 8
versionName "1.0.7"
versionCode 9
versionName "1.0.8"
}
signingConfigs {
release {

View file

@ -52,6 +52,8 @@ import java.util.Map;
import java.util.TreeMap;
public class MainActivity extends AppCompatActivity implements AbsListView.OnScrollListener {
public final static String TAG = MainActivity.class.getSimpleName();
static final int TYPE_COLLAPSED = 0;
static final int TYPE_EXPANDED = 1;
static final int TYPE_DELETED = 2;
@ -104,6 +106,8 @@ public class MainActivity extends AppCompatActivity implements AbsListView.OnScr
mp.release();
duration.put(f, d);
add(f);
} else {
Log.e(TAG, f.toString());
}
}
}
@ -170,6 +174,7 @@ public class MainActivity extends AppCompatActivity implements AbsListView.OnScr
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
playerStop();
dialog.cancel();
RemoveItemAnimation.apply(list, base, new Runnable() {
@Override
@ -271,6 +276,10 @@ public class MainActivity extends AppCompatActivity implements AbsListView.OnScr
void playerPlay(View v, File f) {
player = MediaPlayer.create(getContext(), Uri.fromFile(f));
if (player == null) {
Toast.makeText(MainActivity.this, "File not found", Toast.LENGTH_SHORT).show();
return;
}
player.start();
updatePlayer(v, f);

View file

@ -38,6 +38,7 @@ import com.github.axet.audiorecorder.app.Storage;
import com.github.axet.audiorecorder.encoders.Encoder;
import com.github.axet.audiorecorder.encoders.EncoderInfo;
import com.github.axet.audiorecorder.encoders.FileEncoder;
import com.github.axet.audiorecorder.encoders.Format3GP;
import com.github.axet.audiorecorder.encoders.FormatM4A;
import com.github.axet.audiorecorder.encoders.FormatWAV;
import com.github.axet.audiorecorder.widgets.PitchView;
@ -175,7 +176,7 @@ public class RecordingActivity extends AppCompatActivity {
updateBufferSize(false);
updateSamples(0);
updateSamples(getSamples(storage.getTempRecording().length()));
View cancel = findViewById(R.id.recording_cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@ -220,10 +221,6 @@ public class RecordingActivity extends AppCompatActivity {
});
}
});
if (permitted()) {
record();
}
}
boolean isEmulator() {
@ -244,6 +241,14 @@ public class RecordingActivity extends AppCompatActivity {
@Override
protected void onResume() {
super.onResume();
// start once
if (thread == null) {
if (permitted()) {
record();
}
}
Log.d(TAG, "onResume");
updateBufferSize(false);
pitch.resume();
@ -355,18 +360,7 @@ public class RecordingActivity extends AppCompatActivity {
AudioRecord recorder = null;
try {
File tmp = storage.getTempRecording();
{
long ss = tmp.length();
if (AUDIO_FORMAT == AudioFormat.ENCODING_PCM_16BIT) {
ss = ss / 2;
}
if (CHANNEL_CONFIG == AudioFormat.CHANNEL_IN_STEREO) {
ss = ss / 2;
}
samplesTime = ss;
}
samplesTime = getSamples(tmp.length());
os = new DataOutputStream(new BufferedOutputStream(storage.open(tmp)));
@ -461,6 +455,16 @@ public class RecordingActivity extends AppCompatActivity {
thread.start();
}
long getSamples(long len) {
if (AUDIO_FORMAT == AudioFormat.ENCODING_PCM_16BIT) {
len = len / 2;
}
if (CHANNEL_CONFIG == AudioFormat.CHANNEL_IN_STEREO) {
len = len / 2;
}
return len;
}
// calcuale buffer length dynamically, this way we can reduce thread cycles when activity in background
// or phone screen is off.
void updateBufferSize(boolean pause) {
@ -472,7 +476,6 @@ public class RecordingActivity extends AppCompatActivity {
}
bufferSize = CHANNEL_CONFIG == AudioFormat.CHANNEL_IN_MONO ? samplesUpdate : samplesUpdate * 2;
Log.d(TAG, "BufferSize: " + bufferSize);
}
}
@ -604,6 +607,9 @@ public class RecordingActivity extends AppCompatActivity {
if (ext.equals("m4a")) {
e = new FormatM4A(info, out);
}
if (ext.equals("3gp")) {
e = new Format3GP(info, out);
}
encoder = new FileEncoder(this, in, e);

View file

@ -144,11 +144,13 @@ public class Storage {
return list;
for (File f : ff) {
String[] ee = context.getResources().getStringArray(R.array.encodings_values);
String n = f.getName().toLowerCase();
for (String e : ee) {
if (n.endsWith("." + e))
list.add(f);
if (f.length() > 0) {
String[] ee = context.getResources().getStringArray(R.array.encodings_values);
String n = f.getName().toLowerCase();
for (String e : ee) {
if (n.endsWith("." + e))
list.add(f);
}
}
}

View file

@ -15,6 +15,8 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class FileEncoder {
public static final String TAG = FileEncoder.class.getSimpleName();
Context context;
Handler handler;
@ -23,8 +25,6 @@ public class FileEncoder {
Thread thread;
long samples;
long cur;
Runnable progress;
Runnable done;
Throwable t;
public FileEncoder(Context context, File in, Encoder encoder) {
@ -53,6 +53,7 @@ public class FileEncoder {
byte[] buf = new byte[(RecordingActivity.AUDIO_FORMAT == AudioFormat.ENCODING_PCM_16BIT ? 2 : 1) * 1000];
int len = is.read(buf);
Log.d("123", "len " + len);
if (len <= 0) {
handler.post(done);
return;
@ -66,7 +67,12 @@ public class FileEncoder {
}
}
}
} catch (RuntimeException e) {
Log.e(TAG, "Exception", e);
t = e;
handler.post(error);
} catch (IOException e) {
Log.e(TAG, "Exception", e);
t = e;
handler.post(error);
} finally {

View file

@ -0,0 +1,37 @@
package com.github.axet.audiorecorder.encoders;
import android.annotation.TargetApi;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import java.io.File;
@TargetApi(21)
public class Format3GP extends MuxerMP4 {
public Format3GP(EncoderInfo info, File out) {
MediaFormat format = new MediaFormat();
// for high bitrate AMR_WB
{
// final int kBitRates[] = {6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850};
// format.setString(MediaFormat.KEY_MIME, "audio/amr-wb");
// format.setInteger(MediaFormat.KEY_SAMPLE_RATE, info.sampleRate);
// format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, info.channels);
// format.setInteger(MediaFormat.KEY_BIT_RATE, 23850); // set maximum
}
// for low bitrate, AMR_NB
{
// final int kBitRates[] = {4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200};
format.setString(MediaFormat.KEY_MIME, "audio/3gpp");
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, info.sampleRate); // 8000 only supported
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, info.channels);
format.setInteger(MediaFormat.KEY_BIT_RATE, 12200); // set maximum
}
create(info, format, out);
}
}

View file

@ -1,112 +1,22 @@
package com.github.axet.audiorecorder.encoders;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@TargetApi(21)
public class FormatM4A implements Encoder {
EncoderInfo info;
MediaCodec encoder;
MediaMuxer muxer;
int audioTrackIndex;
long NumSamples;
public FormatM4A() {
}
public class FormatM4A extends MuxerMP4 {
public FormatM4A(EncoderInfo info, File out) {
this.info = info;
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, info.sampleRate);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, info.channels);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64000);
try {
encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
muxer = new MediaMuxer(out.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException e) {
throw new RuntimeException(e);
}
create(info, format, out);
}
public void encode(short[] buf) {
int len = buf.length * 2;
int inputIndex = encoder.dequeueInputBuffer(-1);
if (inputIndex >= 0) {
ByteBuffer input = encoder.getInputBuffer(inputIndex);
input.clear();
for (int i = 0; i < buf.length; i++)
input.putShort(buf[i]);
encoder.queueInputBuffer(inputIndex, 0, len, getCurrentTimeStamp(), 0);
}
NumSamples += buf.length / info.channels;
encode();
}
void encode() {
MediaCodec.BufferInfo outputInfo = new MediaCodec.BufferInfo();
int outputIndex = encoder.dequeueOutputBuffer(outputInfo, 0);
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
audioTrackIndex = muxer.addTrack(encoder.getOutputFormat());
muxer.start();
}
while (outputIndex >= 0) {
ByteBuffer output = encoder.getOutputBuffer(outputIndex);
output.position(outputInfo.offset);
output.limit(outputInfo.offset + outputInfo.size);
// byte[] out = new byte[outputInfo.size];
// output.get(out);
muxer.writeSampleData(audioTrackIndex, output, outputInfo);
encoder.releaseOutputBuffer(outputIndex, false);
outputIndex = encoder.dequeueOutputBuffer(outputInfo, 0);
}
}
public void close() {
end();
encode();
encoder.stop();
encoder.release();
muxer.stop();
muxer.release();
}
long getCurrentTimeStamp() {
return NumSamples * 1000 * 1000 / info.sampleRate;
}
void end() {
int inputIndex = encoder.dequeueInputBuffer(-1);
if (inputIndex >= 0) {
ByteBuffer input = encoder.getInputBuffer(inputIndex);
input.clear();
encoder.queueInputBuffer(inputIndex, 0, 0, getCurrentTimeStamp(), MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
}
public EncoderInfo getInfo() {
return info;
}
}
}

View file

@ -16,9 +16,6 @@ public class FormatWAV implements Encoder {
ByteOrder order = ByteOrder.LITTLE_ENDIAN;
public FormatWAV() {
}
public FormatWAV(EncoderInfo info, File out) {
this.info = info;
NumSamples = 0;

View file

@ -0,0 +1,123 @@
package com.github.axet.audiorecorder.encoders;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.util.Log;
import com.github.axet.audiorecorder.activities.SettingsActivity;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@TargetApi(21)
public class MuxerMP4 implements Encoder {
EncoderInfo info;
MediaCodec encoder;
MediaMuxer muxer;
int audioTrackIndex;
long NumSamples;
public void create(EncoderInfo info, MediaFormat format, File out) {
this.info = info;
try {
encoder = MediaCodec.createEncoderByType(format.getString(MediaFormat.KEY_MIME));
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
muxer = new MediaMuxer(out.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void encode(short[] buf) {
Log.d("123", "encode " + buf.length);
for (int offset = 0; offset < buf.length; ) {
int len = buf.length - offset;
int inputIndex = encoder.dequeueInputBuffer(-1);
if (inputIndex < 0)
throw new RuntimeException("unable to open encoder input buffer");
ByteBuffer input = encoder.getInputBuffer(inputIndex);
input.clear();
len = Math.min(len, input.limit() / 2);
for (int i = 0; i < len; i++)
input.putShort(buf[i]);
int bytes = len * 2;
encoder.queueInputBuffer(inputIndex, 0, bytes, getCurrentTimeStamp(), 0);
Log.d("123", "put " + bytes);
NumSamples += len / info.channels;
offset += len;
while (encode())
;// do encode()
}
}
boolean encode() {
MediaCodec.BufferInfo outputInfo = new MediaCodec.BufferInfo();
int outputIndex = encoder.dequeueOutputBuffer(outputInfo, 0);
if (outputIndex == MediaCodec.INFO_TRY_AGAIN_LATER)
return false;
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
Log.d("123", "output set " + encoder.getOutputFormat());
audioTrackIndex = muxer.addTrack(encoder.getOutputFormat());
muxer.start();
}
if (outputIndex >= 0) {
ByteBuffer output = encoder.getOutputBuffer(outputIndex);
output.position(outputInfo.offset);
output.limit(outputInfo.offset + outputInfo.size);
Log.d("123", "get " + outputInfo.size);
muxer.writeSampleData(audioTrackIndex, output, outputInfo);
encoder.releaseOutputBuffer(outputIndex, false);
}
return true;
}
public void close() {
end();
encode();
encoder.stop();
encoder.release();
muxer.stop();
muxer.release();
}
long getCurrentTimeStamp() {
return NumSamples * 1000 * 1000 / info.sampleRate;
}
void end() {
int inputIndex = encoder.dequeueInputBuffer(-1);
if (inputIndex >= 0) {
ByteBuffer input = encoder.getInputBuffer(inputIndex);
input.clear();
encoder.queueInputBuffer(inputIndex, 0, 0, getCurrentTimeStamp(), MediaCodec.BUFFER_FLAG_END_OF_STREAM);
Log.d("123", "set end ");
}
}
public EncoderInfo getInfo() {
return info;
}
}