Je veux enregistrer l'audio du micro et y accéder pour une lecture possible en temps quasi réel. Je ne sais pas comment utiliser la classe Android AudioRecord pour enregistrer du son micro et y accéder rapidement.
Pour la classe AudioRecord, le site officiel indique `` l'application interroge l'objet AudioRecord à temps '' et `` la taille du tampon en cours de remplissage détermine la durée de l'enregistrement avant de dépasser les données non lues ''. Plus tard, il est suggéré d'utiliser un tampon plus grand lors d'une interrogation moins fréquente. Ils ne montrent jamais réellement un exemple dans le code.
Un exemple que j'ai vu dans un livre utilise la classe AudioRecord pour lire en continu un tampon fraîchement rempli avec du son micro en direct, puis l'application écrit ces données dans un fichier SD. Le pseudo-code ressemble à quelque chose -
set up AudioRecord object with buffer size and recording format info
set up a file and an output stream
myAudioRecord.startRecording();
while(isRecording)
{
// myBuffer is being filled with fresh audio
read audio data into myBuffer
send contents of myBuffer to SD file
}
myAudioRecord.stop();
Comment ce code synchronise sa lecture avec le taux d'enregistrement n'est pas clair - le booléen "isRecording" est-il correctement séquencé et désactivé ailleurs? Il semble que ce code puisse être lu trop fréquemment ou trop rarement, selon le temps que prennent la lecture et l'écriture.
Le document du site indique également que la classe AudioRecord a une classe imbriquée nommée OnRecordPositionUpdateListener qui est définie comme une interface. Les informations suggèrent que, d'une manière ou d'une autre, vous spécifiez la période pendant laquelle vous souhaitez être averti de la progression de l'enregistrement et le nom de votre gestionnaire d'événements, et un appel est automatiquement effectué vers votre gestionnaire d'événements à la fréquence spécifiée. Je pense que la structure, en pseudo-code serait quelque chose comme -
set target of period update message = myListener
set period to be about every 250 ms
other code
myListener()
{
if(record button was recently tapped)
handle message that another 250 ms of fresh audio is available
ie, read it and send it somewhere
)
J'ai besoin de trouver un code spécifique qui me permette de capturer et de traiter l'audio du micro avec un retard de moins d'environ 500 ms. Android propose une autre classe appelée MediaRecorder, mais il ne prend pas en charge la diffusion en continu, et je peux vouloir diffuser du son micro en direct sur un réseau Wi-Fi en temps quasi réel. Où puis-je en trouver des exemples spécifiques?
Après avoir expérimenté beaucoup avec les notifications et un tas d'autres techniques, je me suis installé sur ce code:
private class AudioIn extends Thread {
private boolean stopped = false;
private AudioIn() {
start();
}
@Override
public void run() {
Android.os.Process.setThreadPriority(Android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
AudioRecord recorder = null;
short[][] buffers = new short[256][160];
int ix = 0;
try { // ... initialise
int N = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);
recorder = new AudioRecord(AudioSource.MIC,
8000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
N*10);
recorder.startRecording();
// ... loop
while(!stopped) {
short[] buffer = buffers[ix++ % buffers.length];
N = recorder.read(buffer,0,buffer.length);
//process is what you will do with the data...not defined here
process(buffer);
}
} catch(Throwable x) {
Log.w(TAG,"Error reading voice audio",x);
} finally {
close();
}
}
private void close() {
stopped = true;
}
}
Jusqu'à présent, cela fonctionne assez bien sur une demi-douzaine de téléphones Android sur lesquels je l'ai essayé.
Je me demande si vous pourriez combiner ces réponses de la manière suivante ...
Utilisez setPositionNotificationPeriod (160) avant la boucle while. Cela devrait entraîner le rappel à chaque fois que 160 images sont lues. Au lieu d'appeler le processus (tampon) à l'intérieur du thread qui fait la boucle de lecture, appelez le processus (tampon) à partir du rappel. Utilisez une variable pour garder une trace du dernier tampon de lecture afin de traiter le bon. Dans l'état actuel des choses, vous bloquez la lecture, puis vous ne lisez pas pendant le traitement. Je pense qu'il serait peut-être préférable de les séparer.
Voici le code dont vous avez besoin pour utiliser la période OnRecordPositionUpdateListener et la période de notification.
J'ai remarqué qu'en pratique, il n'envoie pas la notification de manière cohérente au même moment précis, je veux, mais il est suffisamment proche.
À propos de detectAfterEvery
:
La taille de detectEvery doit être suffisamment grande pour contenir uniquement la quantité de données souhaitée. Donc, pour cet exemple, nous avons un taux d'échantillonnage de 44100 Hz, ce qui signifie que nous voulons 44100 échantillons par seconde. En définissant setPositionNotificationPeriod
sur 44100, le code indique Android à rappeler après avoir enregistré 44100 échantillons, soit environ toutes les 1 seconde.
Le code complet est ici :
final int sampleRate = 44100;
int bufferSize =
AudioRecord.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
//aim for 1 second
int detectAfterEvery = (int)((float)sampleRate * 1.0f);
if (detectAfterEvery > bufferSize)
{
Log.w(TAG, "Increasing buffer to hold enough samples " + detectAfterEvery + " was: " + bufferSize);
bufferSize = detectAfterEvery;
}
recorder =
new AudioRecord(AudioSource.MIC, sampleRate,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize);
recorder.setPositionNotificationPeriod(detectAfterEvery);
final short[] audioData = new short[bufferSize];
final int finalBufferSize = bufferSize;
OnRecordPositionUpdateListener positionUpdater = new OnRecordPositionUpdateListener()
{
@Override
public void onPeriodicNotification(AudioRecord recorder)
{
Date d = new Date();
//it should be every 1 second, but it is actually, "about every 1 second"
//like 1073, 919, 1001, 1185, 1204 milliseconds of time.
Log.d(TAG, "periodic notification " + d.toLocaleString() + " mili " + d.getTime());
recorder.read(audioData, 0, finalBufferSize);
//do something amazing with audio data
}
@Override
public void onMarkerReached(AudioRecord recorder)
{
Log.d(TAG, "marker reached");
}
};
recorder.setRecordPositionUpdateListener(positionUpdater);
Log.d(TAG, "start recording, bufferSize: " + bufferSize);
recorder.startRecording();
//remember to still have a read loop otherwise the listener won't trigger
while (continueRecording)
{
recorder.read(audioData, 0, bufferSize);
}
private int freq =8000;
private AudioRecord audioRecord = null;
private Thread Rthread = null;
private AudioManager audioManager=null;
private AudioTrack audioTrack=null;
byte[] buffer = new byte[freq];
//call this method at start button
protected void Start()
{
loopback();
}
protected void loopback() {
Android.os.Process.setThreadPriority(Android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
final int bufferSize = AudioRecord.getMinBufferSize(freq,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, freq,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
MediaRecorder.AudioEncoder.AMR_NB, bufferSize);
audioTrack = new AudioTrack(AudioManager.ROUTE_HEADSET, freq,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
MediaRecorder.AudioEncoder.AMR_NB, bufferSize,
AudioTrack.MODE_STREAM);
audioTrack.setPlaybackRate(freq);
final byte[] buffer = new byte[bufferSize];
audioRecord.startRecording();
Log.i(LOG_TAG, "Audio Recording started");
audioTrack.play();
Log.i(LOG_TAG, "Audio Playing started");
Rthread = new Thread(new Runnable() {
public void run() {
while (true) {
try {
audioRecord.read(buffer, 0, bufferSize);
audioTrack.write(buffer, 0, buffer.length);
} catch (Throwable t) {
Log.e("Error", "Read write failed");
t.printStackTrace();
}
}
}
});
Rthread.start();
}
Il lit l'audio enregistré avec un retard inférieur à 100 ms.