searching throw the stack i got confused. So, what is my problem:
I'm using AudioRecord class to record some audio, here's the code:
AudioRecord record = new AudioRecord(AudioSource.VOICE_RECOGNITION,
SAMPLING_RATE,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
mBufferSize);
record.startRecording();
int read = 0;
while (mIsRecording) {
read = record.read(mAudioBuffer, 0, mBufferSize);
if ((read == AudioRecord.ERROR_INVALID_OPERATION) ||
(read == AudioRecord.ERROR_BAD_VALUE) ||
(read <= 0)) {
continue;
}
proceed();
write(out);
}
After recording is completed, i'm converting this .pcm raw data from AudioRecord to .wav:
private void convertRawToWav() {
File file_raw = new File(mFileNameRaw);
if (!file_raw.exists()) { return; }
File file_wav = new File(mFileNameWav);
try {
PcmAudioHelper.convertRawToWav(WavAudioFormat.mono16Bit(SAMPLING_RATE), file_raw, file_wav);
if (handler != null) {
handler.onRecordSuccess();
}
} catch (IOException e) {
e.printStackTrace();
if (handler != null) {
handler.onRecordSaveError();
}
}
}
I need .wav format in future, because there is trim function in my application, copied from Ringdroid which doesn't support OGG format, therefor : PLEASE DO NOT RECOMMEND ME TO RECORD AUDIO AS OGG ON THE FLY
MAIN ISSUE:
Wav format is too heavy weight, and i need to convert it to smaller one, which is either MP3 or OGG. MP3 is patented, so it's not an option. What i need is:
To convert .wav file to .ogg file so it's weight will be much smaller
What i found:
This library, but it only converts .pcm data to .ogg while recording, and i need to convert whole file after trimmiing it as .wav
Take a look at this lame wrapper project.
Wav file is just a big header + PCM. All you need to do is remove the 44 bytes in the front of the WAV to get the PCM and use the code that you shared with us to convert to OGG.
Related
it's the second week which I'm trying to just record audio in xamarin which has an RIFF header.
I tried Audio Recorder Plugin and Audio Record and Media Recorder . I asked many questions but got no answer.
the easiest way was Audio Recorder Plugin, but the output hasn't RIFF header.
the output of Media Recorder was .3gp which I couldn't convert it to .wav.
and the output of Media Recorder was .pcm which also couldn't convert to .wav
here is the last code I tried :
#region Properties
int SAMPLING_RATE_IN_HZ = 44100;
ChannelIn CHANNEL_CONFIG = ChannelIn.Mono;
Android.Media.Encoding AUDIO_FORMAT = Android.Media.Encoding.Pcm16bit;
int BUFFER_SIZE_FACTOR = 2;
int BUFFER_SIZE;
bool RecordingInProgress = false;
private AudioRecord recorder = null;
#endregion
public void Record()
{
BUFFER_SIZE = AudioRecord.GetMinBufferSize(SAMPLING_RATE_IN_HZ,
CHANNEL_CONFIG, AUDIO_FORMAT) * BUFFER_SIZE_FACTOR;
recorder = new AudioRecord(AudioSource.Mic, SAMPLING_RATE_IN_HZ,
CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE);
recorder.StartRecording();
RecordingInProgress = true;
RecordingTask();
}
public Task RecordingTask()
{
return Task.Run(() =>
{
string path = "appdir/demo.pcm";
MemoryStream buffer = new MemoryStream(BUFFER_SIZE);
FileOutputStream outStream = new FileOutputStream(path);
var demo2 = RecordingInProgress;
while (RecordingInProgress)
{
int result = recorder.Read(buffer.GetBuffer(), 0, BUFFER_SIZE);
if (result < 0)
{
throw new Exception("Reading of audio buffer failed: ");
}
outStream.Write(buffer.GetBuffer(), 0, BUFFER_SIZE);
}
});
}
public void Stop()
{
if (null == recorder)
{
return;
}
RecordingInProgress = false;
recorder.Stop();
recorder.Release();
recorder = null;
}
}
this code makes a .pcm file that can't convert to anything with even cloud converters.
I also tried this :
NWaves.Audio.WaveFile waveFile = new NWaves.Audio.WaveFile(buffer.GetBuffer());
waveFile.SaveTo(new FileStream("appdir/demo.wav", FileMode.Create));
insted of outStream.Write(buffer.GetBuffer(), 0, BUFFER_SIZE); at the bottom of while closing block
but it says : No RIFF found
there is about 4 or 5 way to record audio. but a package like Nwaves can't work with any of them.
the last try I want to do is add RIFF header to the recorded audio buffer(bytes) programmatically or convert .3gp or .pcm to .wav programmatically .
summery: someone help me to record an audio in xamarin which Nwaves can work with.
thanks
The following Code A is from the sample project.
It use the Code B to record voice and save as file.
Code B
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
You know the getExtensionText() will return two audio format, mp3 or m4a by user settings , there are different audio format.
I don't know why the author can use only Code B to generate two different audio format file.
Can MediaRecorder save voice as mp3 or m4a audio format file automatically based file name extension in Android Studio?
Code A
// mp4 output format with aac encoding should produce good enough m4a files according to https://stackoverflow.com/a/33054794/1967672
private fun startRecording() {
val baseFolder = if (isQPlus()) {
cacheDir
} else {
val defaultFolder = File(config.saveRecordingsFolder)
if (!defaultFolder.exists()) {
defaultFolder.mkdir()
}
defaultFolder.absolutePath
}
currFilePath = "$baseFolder/${getCurrentFormattedDateTime()}.${config.getExtensionText()}"
recorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setAudioEncodingBitRate(128000)
setAudioSamplingRate(44100)
...
}
class Config(context: Context) : BaseConfig(context) {
...
fun getExtensionText() = context.getString(when (extension) {
EXTENSION_M4A -> R.string.m4a
else -> R.string.mp3
})
}
Actually, they already defined the file extension in the SettingsActivity.kt but you can use the file name with the extension. Actually, In this code, you can use any extension of the supported media formats.
mediaRecorder = MediaRecorder()
output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3"
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
mediaRecorder.setOutputFile(output)
for the full code see this or here
I created a WAV file based from https://developer.xamarin.com/samples/monodroid/Example_WorkingWithAudio/ where the relevant code are as follows:
private const int RECORDER_SAMPLERATE = 16000;
private const ChannelIn RECORDER_CHANNELS = ChannelIn.Mono;
private const Android.Media.Encoding RECORDER_AUDIO_ENCODING = Android.Media.Encoding.Pcm16bit;
...
var bufferSize = AudioRecord.GetMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
audioBuffer = new byte[bufferSize];
audioRecord = new AudioRecord(
// Hardware source of recording.
AudioSource.Mic,
// Frequency
RECORDER_SAMPLERATE,
// Mono or stereo
RECORDER_CHANNELS,
// Audio encoding
RECORDER_AUDIO_ENCODING,
// Length of the audio clip.
audioBuffer.Length
);
audioRecord.StartRecording();
...
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
while (true)
{
if (endRecording)
{
endRecording = false;
break;
}
try
{
// Keep reading the buffer while there is audio input.
int numBytes = await audioRecord.ReadAsync(audioBuffer, 0, audioBuffer.Length);
await fileStream.WriteAsync(audioBuffer, 0, numBytes);
// Do something with the audio input.
}
catch (Exception ex)
{
Console.Out.WriteLine(ex.Message);
break;
}
}
fileStream.Close();
}
audioRecord.Stop();
audioRecord.Release();
My question is - how can I play this .WAV file after copying in Windows (or other OS)? Here are my observations:
.WAV file can be played using Android's AudioTrack class, as shown in the https://developer.xamarin.com/samples/monodroid/Example_WorkingWithAudio/ code sample.
The code sample will create a testAudio.wav file in the /data/Example_WorkingWithAudio/files/ directory.
Try to copy this file to your Windows PC, then try playing the .WAV file with an audio player which supports .WAV. See that it won't be able to play the file.
to revert back, I found out that the created track needs to be converted to .WAV first. Here are 2 posts which helped me do it.
http://www.edumobile.org/android/audio-recording-in-wav-format-in-android-programming/
Recorded audio Using AndroidRecord API fails to play
I want to compress locally saved video file to a smaller size in order to upload to a server.
Since i used MediaCodec , i have found some tips to compress video . Here are the steps that i followed
1) . Extracted the media file using MediaExrtactor and Decoded it.
2) . Creates the Encoder with required file format
3) . Create muxer to save file in local storage. (not complete)
Question : But i dont know how to encode the already decoded stream and save the stream in to the local storage using MediaMuxer.
public class CompressMedia {
private static final String SAMPLE = Environment
.getExternalStorageDirectory() + "/DCIM/Camera/20140506_174959.mp4";
private static final String OUTPUT_PATH = Environment
.getExternalStorageDirectory()
+ "/DCIM/Camera/20140506_174959_REC.mp4";
private MediaExtractor extractor;
private MediaCodec decoder;
private MediaCodec encoder;
String mime;
private static final String MIME_TYPE = "video/avc";
public void extractMediaFile() {
// work plan
// locate media file
// extract media file using Media Extractor
// retrieve decoded frames
extractor = new MediaExtractor();
try {
extractor.setDataSource(SAMPLE);
} catch (IOException e) {
// TODO Auto-generated catch block
// file not found
e.printStackTrace();
}
// add decoded frames
for (int i = 0; i < extractor.getTrackCount(); i++) {
MediaFormat format = extractor.getTrackFormat(i);
mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
extractor.selectTrack(i);
decoder = MediaCodec.createDecoderByType(mime);
decoder.configure(format, null, null, 0);
break;
}
}
if (decoder == null) {
Log.e("DecodeActivity", "Can't find video info!");
return;
}
// - start decoder -
decoder.start();
extractor.selectTrack(0);
// - decoded frames can obtain in here -
}
private void createsEncoder() {
// creates media encoder to set formats
encoder = MediaCodec.createDecoderByType(MIME_TYPE);
// init media format
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, /* 640 */
320, /* 480 */240);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 400000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
encoder.configure(mediaFormat, null, null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
// - encoded data format is avaiable in here
}
private void createMuxer() {
// creates media muxer - media muxer will be used to write the final
// strem in to a desired file :)
try {
MediaMuxer muxer = new MediaMuxer(OUTPUT_PATH,
OutputFormat.MUXER_OUTPUT_MPEG_4);
int videoTrackIndex = muxer.addTrack(encoder.getOutputFormat());
//muxer.writeSampleData(videoTrackIndex, inputBuffers, bufferInfo);
muxer.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Here are the links that i follwed
Android MediaCodec: Reduce mp4 video size and
Video compression on android using new MediaCodec Library
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 transcode=recompress video files. You can set smaller resolution and\or bitrate to output to get smaller file
in ComposerTranscodeCoreActivity.java
protected void setTranscodeParameters(MediaComposer mediaComposer) throws IOException {
mediaComposer.addSourceFile(mediaUri1);
mediaComposer.setTargetFile(dstMediaPath);
configureVideoEncoder(mediaComposer, videoWidthOut, videoHeightOut);
configureAudioEncoder(mediaComposer);
}
protected void transcode() throws Exception {
factory = new AndroidMediaObjectFactory(getApplicationContext());
mediaComposer = new MediaComposer(factory, progressListener);
setTranscodeParameters(mediaComposer);
mediaComposer.start();
}
I am accessing the videos directly from content provider without storing it in my database. But it is giving me 3gp audio file along with other video and 3gp videos. how could i filter only the video files.I am working for API 8
Try this since you are working on API 8, otherwise METADATA_KEY_HAS_VIDEO could have done the job if API level >= 10.
A work around using MediaPlayer. If media has height it means it is a video.
public boolean isVideoFile(File file) {
int height = 0;
try {
MediaPlayer mp = new MediaPlayer();
FileInputStream fs;
FileDescriptor fd;
fs = new FileInputStream(file);
fd = fs.getFD();
mp.setDataSource(fd);
mp.prepare();
height = mp.getVideoHeight();
mp.release();
} catch (Exception e) {
Log.e(TAG, "Exception trying to determine if 3gp file is video.", e);
}
return height > 0;
}
Source