YandexSpeechKit Recognizer with voice recording - android

Can I record sound from a microphone in a file at the same time when running YandexSpeechKit Recognizer?
There was a need for simultaneous speech recognition (using the class Recognizer) and recording sound from a devices microphone to a file. Use a standard mechanism MediaRecord is not possible, because MediaRecord and YandexSpeechKit used native methods and the same resource. It is causing the fall of some of the processes (MediaRecord or Recognizer).
I'm trying use RecognizerListener -> onSoundDataRecorded(Recognizer recognizer, byte[] bytes) code is bellow:
#Override
public void onSoundDataRecorded(Recognizer recognizer, byte[] bytes) {
Logger.d(TAG, "onSoundDataRecorded");
write(bytes);
}
public void write(byte[] bytes) {
File file = getTmpFile();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file, true);
fos.write(bytes);
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if(fos != null) {
try {
fos.flush();
fos.close();
} catch(IOException e) {
}
}
}
}
But while the resulting file is not possible to play.
Can somebody help me?
Thanks!

Yandex SpeechKit returns raw PCM (16 kHz mono 16 bit) data. You should add WAV header or play as PCM. For example in unix-like OS via sox:
play -r 16000 -b 16 -c 1 -e signed-integer filename.pcm
For adding WAV header you can use this class https://github.com/MohammadAG/Android-SoundRecorder/blob/master/src/com/mohammadag/soundrecorder/WavConverter.java with parameters
private static final long SAMPLE_RATE = 16000;
private static final int RECORDER_BPP = 16;
private static final int CHANNELS = 1;
private static final long BYTE_RATE = RECORDER_BPP * SAMPLE_RATE * CHANNELS/8;
#Override
public void onRecognizerRecordingBegin() {
try {
tempFileName = getFilename();
os = new FileOutputStream(tempFileName, true);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
#Override
public void onRecognizerRecordingDone() {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
int bufferSize = AudioRecord.getMinBufferSize(
16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
WavConverter.copyWaveFile(tempFileName, getFilename(), bufferSize);
deleteTempFile();
}
#Override
public void onRecognizerSoundDataRecorded(byte[] bytes) {
try {
os.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}

Related

How to Set Sample Rate on Text to Speech - Android

In the output of my text to Speech, I need to set Sampling rate about to 32000 Hz with Pitch - 1 and SpeechRate - 0.2 (which I already did). But I can't set Sample Rate.
tts = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR) {
tts.setLanguage(Locale.US);
tts.setSpeechRate((float) 0.2);
tts.setPitch((float) 1);
}
}
}, TextToSpeech.Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
I used AudioTrack to set Sample Rate but it took lots of time because I have to first TTS synthesizeToFile then I play it in AudioTrack.
HashMap<String, String> myHasRead = new HashMap<String, String>();
myHasRead.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, outPutS);
String StorePath = Environment.getExternalStorageDirectory().getAbsolutePath();
File myF = new File(StorePath+"/tempAudio.wav");
try {
myF.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
tts.setOnUtteranceProgressListener(new TtsUtteranceListener());
tts.synthesizeToFile("Bla Bla bla",myHasRead, StorePath+"/tempAudio.wav");
....
private class TtsUtteranceListener extends UtteranceProgressListener {
#Override
public void onStart(String utteranceId) {
}
#Override
public void onDone(String utteranceId) {
playWav();
}
#Override
public void onError(String utteranceId) {
}
}
public void playWav(){
int minBufferSize = AudioTrack.getMinBufferSize(32000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
int bufferSize = 512;
AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 32000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
String filepath = Environment.getExternalStorageDirectory().getAbsolutePath();
int i = 0;
byte[] s = new byte[bufferSize];
try {
FileInputStream fin = new FileInputStream(filepath + "/tempAudio.wav");
DataInputStream dis = new DataInputStream(fin);
at.play();
while((i = dis.read(s, 0, bufferSize)) > -1){
at.write(s, 0, i);
}
at.stop();
at.release();
dis.close();
fin.close();
} catch (FileNotFoundException e) {
// TODO
e.printStackTrace();
} catch (IOException e) {
// TODO
e.printStackTrace();
}
}
There is any way to set sample rate Direct to TTS like tts.setSampleRate(32000); or get Stream from TTS to AudioTrack like DataInputStream dis = new DataInputStream(tts.speak("bla bla bla").getDataInputStream); . In Short I need Chipmunk's Text to Speech for Android but without synthesizeToFile or direct stream TTS voice Data in AudioTrack without saving output of TTS.
You can't set TTS sampling Rate directly:
I did something like this in a project ( Dint use TTS )
This might help you,
To play record with different voice type :-
waveSampling=90000; (Chipmunk)
waveSampling=24200; ("SLOW MOTION")
waveSampling=30000;("BANE") /batman character
waveSampling=18000;(Ghost)
waveSampling=70000;(Bee)
waveSampling=60000;(Woman)
waveSampling=37000; (Normal)
void playRecord() throws IOException {
int minBufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
int bufferSize = 512;
at = new AudioTrack(AudioManager.STREAM_MUSIC, waveSampling, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
String filepath = Environment.getExternalStorageDirectory().getAbsolutePath();
int i = 0;
byte[] s = new byte[bufferSize];
try {
FileInputStream fin = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Voice Changer/temp/"+filename+".wav");
DataInputStream dis = new DataInputStream(fin);
at.play();
while((i = dis.read(s, 0, bufferSize)) > -1){
at.write(s, 0, i);
}
at.stop();
at.release();
dis.close();
fin.close();
openmenu();
} catch (FileNotFoundException e) {
// TODO
e.printStackTrace();
} catch (IOException e) {
// TODO
e.printStackTrace();
}
}
To save the Audio :-
public void save() throws IOException {
Random r = new Random();
final int i1 = r.nextInt(80 - 65) + 65;
File tempfile2=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Voice Changer/temp/"+i1+filename+".wav");
savedfile=Environment.getExternalStorageDirectory().getAbsolutePath()+"/Voice Changer/"+"VOICE CHANGER"+i1+filename+".mp3";
Toast.makeText(this, "File Saved", Toast.LENGTH_SHORT).show();
rawToWave(tempfile,tempfile2);
File wavFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Voice Changer/temp/"+i1+filename+".wav");
IConvertCallback callback = new IConvertCallback() {
#Override
public void onSuccess(File convertedFile) {
File newfile=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Voice Changer/"+"VOICE CHANGER"+i1+filename+".mp3");
File savedmp3=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Voice Changer/temp/"+i1+filename+".mp3");
Toast.makeText(MainActivity.this, "SUCCESS: " + newfile.getPath(), Toast.LENGTH_LONG).show();
try {
copyit(savedmp3,newfile);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Exception error) {
Toast.makeText(MainActivity.this, "ERROR: " + error.getMessage(), Toast.LENGTH_LONG).show();
}
};
Toast.makeText(this, "Converting audio file...", Toast.LENGTH_SHORT).show();
AndroidAudioConverter.with(this)
.setFile(wavFile)
.setFormat(cafe.adriel.androidaudioconverter.model.AudioFormat.MP3)
.setCallback(callback)
.convert();
}
The output will be a .mp3 file. If you want the output fast you can use .wav format.

Android Wear Audio Recorder using the ChannelAPI

I'm trying to build an audio recorder app for Android Wear. Right now, I'm able to capture the audio on the watch, stream it to phone and save it on a file. However, the audio file is presenting gaps or cropped parts.
I found this aswered questions related to my problem link1, link2, but they couldn't help me.
Here is my code:
First, on the watch side, I create the channel using the channelAPI and sucessfully send the audio being captured on the watch to the smartphone.
//here are the variables values that I used
//44100Hz is currently the only rate that is guaranteed to work on all devices
//but other rates such as 22050, 16000, and 11025 may work on some devices.
private static final int RECORDER_SAMPLE_RATE = 44100;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
int BufferElements2Rec = 1024;
int BytesPerElement = 2;
//start the process of recording audio
private void startRecording() {
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLE_RATE, RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING, BufferElements2Rec * BytesPerElement);
recorder.startRecording();
isRecording = true;
recordingThread = new Thread(new Runnable() {
public void run() {
writeAudioDataToPhone();
}
}, "AudioRecorder Thread");
recordingThread.start();
}
private void writeAudioDataToPhone(){
short sData[] = new short[BufferElements2Rec];
ChannelApi.OpenChannelResult result = Wearable.ChannelApi.openChannel(googleClient, nodeId, "/mypath").await();
channel = result.getChannel();
Channel.GetOutputStreamResult getOutputStreamResult = channel.getOutputStream(googleClient).await();
OutputStream outputStream = getOutputStreamResult.getOutputStream();
while (isRecording) {
// gets the voice output from microphone to byte format
recorder.read(sData, 0, BufferElements2Rec);
try {
byte bData[] = short2byte(sData);
outputStream.write(bData);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Then, on the smartphone side, I receive the audio data from the channel and write it to a PCM file.
public void onChannelOpened(Channel channel) {
if (channel.getPath().equals("/mypath")) {
Channel.GetInputStreamResult getInputStreamResult = channel.getInputStream(mGoogleApiClient).await();
inputStream = getInputStreamResult.getInputStream();
writePCMToFile(inputStream);
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "Audio file received!", Toast.LENGTH_SHORT).show();
}
});
}
}
public void writePCMToFile(InputStream inputStream) {
OutputStream outputStream = null;
try {
// write the inputStream to a FileOutputStream
outputStream = new FileOutputStream(new File("/sdcard/wearRecord.pcm"));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
System.out.println("Done writing PCM to file!");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
// outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
What am I doing wrong or what are your suggestions to achieve a perfect gapless audio file on the smartphone? Thanks in advance.
I noticed in your code that you are reading everything into a short[] array, and then converting it to a byte[] array for the Channel API to send. Your code also creates a new byte[] array through each iteration of the loop, which will create a lot of work for the garbage collector. In general, you want to avoid allocations inside loops.
I would allocate one byte[] array at the top, and let the AudioRecord class store it directly into the byte[] array (just make sure you allocate twice as many bytes as you did shorts), with code like this:
mAudioTemp = new byte[bufferSize];
int result;
while ((result = mAudioRecord.read(mAudioTemp, 0, mAudioTemp.length)) > 0) {
try {
mAudioStream.write(mAudioTemp, 0, result);
} catch (IOException e) {
Log.e(Const.TAG, "Write to audio channel failed: " + e);
}
}
I also tested this with a 1 second audio buffer, using code like this, and it worked nicely. I'm not sure what the minimum buffer size is before it starts to have problems:
int bufferSize = Math.max(
AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT),
44100 * 2);

File Transfer in Android on WiFi using TCP causes the app to crash if file size is too big

I was trying to transfer a music file from one android phone to another by having a server app running on one phone to send the file and a client app on the other to receive the file. I opened a TCP socket to establish the connection and transfer the bytes. However, as I increase the file size, the server keeps sending the files when the client is still blocking while receiving. This results in chunks of data missing, till finally the app crashes. I tried to space the sending action by sleeping the thread after every send so that the receive buffer doesn't overflow, but think this is a wasteful approach. Can anyone suggest a better way to coordinate between send and receive such that reliably an mp3 file of size approx 5 Mb can be sent?
Thanks,
Swetha
private BufferedInputStream in = null;
private BufferedOutputStream output = null;
ReceiveBytes str2;
private FileOutputStream out3;
private Socket clientsocket;
try
{
clientsocket= new Socket("192.168.1.70",9864);
}
catch(ActivityNotFoundException e)
{
Log.e(TAG, e.getMessage());
}
catch(Exception e)
{
Log.e(TAG, e.getMessage());
}
try
{
out3= new FileOutputStream(path,true);
}
catch (FileNotFoundException e)
{
Log.e(TAG, e.getMessage());
}
try
{
in = new BufferedInputStream(clientsocket.getInputStream());
output = new BufferedOutputStream(out3);
str2 = new ReceiveBytes(clientsocket,out3);
str2.copyfile();
clientsocket.close();
out3.close();
}
catch(IOException e)
{
Log.e(TAG, e.getMessage());
}
catch(Exception e1)
{
Log.e1(TAG, e1.getMessage());
}
I have include a snipet of my code. ReceiveBytes is a user defined class. It is as follows.
public class ReceiveBytes
{
public ReceiveBytes(){};
private Socket src_file;
private FileOutputStream dest_file;
private int MaxTransferbytes = 1024;
public ReceiveBytes (Socket Src, FileOutputStream Dest)
{
src_file = Src;
dest_file =Dest;
}
public int copyfile () throws Exception
{
BufferedInputStream in = null;
BufferedOutputStream output = null;
int off=0;
int received =0;
int k;
byte[] buffer = new byte[2*MaxTransferbytes];
try
{
in = new BufferedInputStream(src_file.getInputStream());
output = new BufferedOutputStream(dest_file);
while(in.available()>0)
{
k = in.available();
if(k>MaxTransferbytes)
{
try
{
received = in.read(buffer,0,(MaxTransferbytes));
output.write(buffer,0,received);
}
catch(IOException e1)
{
Log.e1(TAG, e1.getMessage());
}
}
else
{
try
{
received = in.read(buffer,0,(k));
output.write(buffer,0,received);
}
catch(IOException e1)
{
Log.e1(TAG, e1.getMessage());
}
}
}
}
catch(Exception e)
{
System.out.printf(e.getMessage());
}
return(1);
}
}
Here, in every iteration, I transfer 1024 bytes. So a large file needs quite a few iterations to copy the file. However, this causes the app to crash. Any suggestions as to how I can do this without making the app crash? Thanks a lot.

Android AudioTrack clicks at start and end of sound

I get clicks at the start and end of playing a sound (a wav from the sdcard). It must be something to do with the track buffering but I dont know the solution. Also, I create a new one of these every time the sound plays, is this ok or is there a better way? There are many sounds playing over and over. Here is the code:
public void PlayAudioTrack(final String filePath, final Float f) throws IOException
{
new Thread(new Runnable() { public void run() {
//play sound here
int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT );
AudioTrack track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT,
minSize, AudioTrack.MODE_STREAM);
track.setPlaybackRate((int) (44100*f));
if (filePath==null)
return;
int count = 512 * 1024;
//Read the file..
byte[] byteData = null;
File file = null;
file = new File(filePath);
byteData = new byte[(int)count];
FileInputStream in = null;
try {
in = new FileInputStream( file );
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int bytesread = 0, ret = 0;
int size = (int) file.length();
while (bytesread < size) {
try {
ret = in.read( byteData,0, count);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
track.play();
if (ret != -1) {
// Write the byte array to the track
track.write(byteData,0, ret); bytesread += ret;
}
else break; }
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} track.stop(); track.release();
}
}).start();
}
Many thanks for any help
Isn't it possible that you play the PCM wave file header too?
Each PCM wave file has a small header at the beginning of the file, if you play that, you play the header bytes which could result in a click at te beginning.
I have had these same clicks at the beginning of each track using AudioTrack. I solved it by turning track volume off, waiting half a second, and then restoring normal volume. I no longer have any clicks. Here's the relevant bit of the code.
at.play();
at.setStereoVolume (0.0f, 0.0f);
new Thread (new Runnable(){
public void run() {
try{
Thread.sleep(500);
} catch (InterruptedException ie) { ; }
at.setStereoVolume (1.0f, 1.0f);
}
}).start();
new Thread (new Runnable(){
public void run() {
int i = 0;
try{
buffer = new byte[512];
while(((i = is.read(buffer)) != -1) && !paused){
at.write(buffer, 0, i);
position += i;
}
} catch (IOException e) {
e.printStackTrace();
}
if (!paused){
parent.trackEnded();
}
}
}).start();
}

how to read bytes from a video file in android

I have a question that I want to read bytes from video resided in sdcard in chunk size 1024,
means I have to read 1024 bytes from the file at a time. I am able to fetch number of bytes from the video but I can't get it in chunks, I don't know how to achieve this. Please suggest me the right solution regarding the same.
Thanks in advance.
import java.io.*;
public class FileUtil {
private final int BUFFER_SIZE = 1024;
public void readFile(String fileName) {
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
byte[] buffer = new byte[BUFFER_SIZE];
try {
int n = 0;
while ((n = in.read(buffer, 0, BUFFER_SIZE)) > 0) {
/* do whatever you want with buffer here */
}
}
catch(Exception e) {
e.printStackTrace();
}
finally { // always close input stream
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Based on the code from http://www.xinotes.org/notes/note/648/

Categories

Resources