Я передаю микрофонный вход с C-сервера через сокет. Я знаю, что поток работает, потому что он работает с клиентом C, и я получаю правильные значения на своем клиенте Android.
Я передаю поток 1024 floatarray. Один float равен 4 байтам. Итак, я получил входящий поток с 4096 байтами на кадр. Я получаю числа с плавающей запятой из этих байтов, и я знаю, что это числа с плавающей запятой, которые я отправил, так что эта часть должна работать.
Теперь я хочу передать этот поток прямо на динамики телефона с помощью AudioTrack. Я попытался ввести полученные байты напрямую: просто шум. Я попытался вернуть его в массив байтов, все то же самое. Я попытался преобразовать это число с плавающей запятой в число Short (потому что AudioTrack принимает байты или число Short). Я мог бы получить что-то, что могло бы быть входом моего микрофона (стуки), но очень неуклюжим и очень запаздывающим. Я бы понял, если бы был лаг между кадрами, но у меня даже одного чистого звука не получается. Однако я могу четко выводить звук греха, который я создаю локально, и помещать в этот короткий массив. Теперь мне интересно, есть ли у меня какие-то проблемы в моем коде, которые кто-нибудь из вас может увидеть, потому что я их не вижу.
Что я делаю: я помещаю 4 байта в массив байтов. Я получаю поплавок из него. Как только я получил один кадр в своем массиве с плавающей запятой (я контролирую это с помощью логического значения, нехорошо, но это должно работать), я помещал его в свой короткий массив и позволял аудиотреку воспроизводить его. Это двойное приведение может быть медленным, но я делаю это, потому что это самое близкое к воспроизведению фактического ввода.
Редактировать: я проверил порядок байтов, сравнив числа с плавающей запятой, они имеют правильные значения от -1 до 1, и это те же самые значения, которые я отправляю. Поскольку я не меняю порядок следования байтов при приведении к float, я не понимаю, почему перенаправление массива 4096 байт напрямую в AudioTrack также не работает. Может быть что-то не так с многопоточностью, но я не понимаю, что это может быть.
Редактировать 2: я обнаружил небольшую проблему — я сбросил j на 1023. Но отсутствие числа с плавающей запятой не должно было быть проблемой. Что я сделал кроме этого, так это поместил метод, который брал поток из сокета, в другой поток вместо того, чтобы вызывать его в асинхронной задаче. Это заставило его работать, теперь я могу понимать звуки микрофона. Тем не менее, качество очень плохое - может быть причина в коде? Также я получил задержку около 10 секунд. Только около половины секунды вызвано WLAN, поэтому мне интересно, может ли это быть ошибка кодов. Любые дальнейшие мысли приветствуются.
Редактировать 3: я поэкспериментировал с кодом и реализовал несколько идей GreenApps в комментариях. С новой структурой потоков я столкнулся с проблемой отсутствия звука. Вроде вообще. Я не понимаю, как это вообще возможно, поэтому я переключился обратно. Другие вещи, которые я пытался сделать потоки более легкими, не имели никакого эффекта. У меня задержка и очень плохое качество (я могу распознать стуки, но не могу понять голоса). Я подумал, что с моими преобразованиями может быть что-то не так, поэтому я поместил байты, которые я получаю из сокета, прямо в AudioTrack - ничего, кроме уродливого пульсирующего статического шума. Теперь я еще больше запутался, так как именно этот поток все еще работает с клиентом C. Я сообщу, если найду решение, но все же любая помощь приветствуется.
Редактировать 4 Я должен добавить, что я могу воспроизводить входные данные микрофона из другого приложения для Android, где я отправляю эти входные данные непосредственно в виде байтов (я бы исключил материал приведения с плавающей запятой и поместил бы байты, которые я получаю, непосредственно в audioTrack в моем код игрока).
Также мне пришло в голову, что проблема может заключаться в том, что указанный массив с плавающей запятой, передаваемый сервером C, поступает с 64-битной машины, а телефон 32-битный. Может ли это быть какой-то проблемой, даже если я просто передаю числа с плавающей запятой как 4 байта? Или, еще одна моя мысль: базовый числовой формат байтов, которые я получаю, - это число с плавающей запятой. Какой формат ожидает AudioTrack? Даже если ввести только байты - нужно ли мне преобразовать это число с плавающей запятой в целое число и вернуть его обратно в байты или что-то в этом роде?
новый код:
public class PCMSocket {
AudioTrack audioTrack;
boolean doStop = false;
int musicLength = 4096;
byte[] music;
Socket socket;
short[] buffer = new short[4096];
float[] fmusic = new float[1024];
WriteToAudio writeThread;
ReadFromSocket readThread;
public PCMSocket()
{
}
public void start()
{
doStop = false;
readThread = new ReadFromSocket();
readThread.start();
}
public class ReadFromSocket extends Thread
{
public void run()
{
doStop=true;
InetSocketAddress address = new InetSocketAddress("xxx.xxx.xxx.x", 8000);
socket = new Socket();
int timeout = 6000;
try {
socket.connect(address, timeout);
} catch (IOException e2) {
e2.printStackTrace();
}
musicLength = 1024;
InputStream is = null;
try {
is = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
try{
int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT );
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT, minSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
} catch (Throwable t)
{
t.printStackTrace();
doStop = true;
}
writeThread = new WriteToAudio();
readThread.start();
int i = 0;
int j=0;
try {
if(dis.available()>0)Log.d("PCMSocket", "receiving");
music = new byte[4];
while (dis.available() > 0)
{
music[i]=0;
music[i] = dis.readByte();
if(i==3)
{
int asInt = 0;
asInt = ((music[0] & 0xFF) << 0)
| ((music[1] & 0xFF) << 8)
| ((music[2] & 0xFF) << 16)
| ((music[3] & 0xFF) << 24);
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
fmusic[j]=asFloat;
}
i++;
j++;
if(i==4)
{
music = new byte[4];
i=0;
}
if(j==1024)
{
j=0;
if(doStop)doStop=false;
}
}
} catch (IOException e) {
e.printStackTrace();
}
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
public class WriteToAudio extends Thread
{
public void run()
{
while(true){
while(!doStop)
{
try{
writeSamples(fmusic);
}catch(Exception e)
{
e.printStackTrace();
}
doStop = true;
}
}
}
};
public void writeSamples(float[] samples)
{
fillBuffer( samples );
audioTrack.write( buffer, 0, samples.length );
}
private void fillBuffer( float[] samples )
{
if( buffer.length < samples.length )
buffer = new short[samples.length];
for( int i = 0; i < samples.length; i++ )
{
buffer[i] = (short)(samples[i] * Short.MAX_VALUE);
}
}
}
старый код:
public class PCMSocket {
AudioTrack audioTrack;
WriteToAudio thread;
boolean doStop = false;
int musicLength = 4096;
byte[] music;
Socket socket;
short[] buffer = new short[4096];
float[] fmusic = new float[1024];
public PCMSocket()
{
}
public void start()
{
doStop = false;
new GetStream().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private class GetStream extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... values) {
PCMSocket.this.getSocket();
return null;
}
@Override
protected void onPreExecute() {
}
@Override
protected void onPostExecute(Void result)
{
return;
}
@Override
protected void onProgressUpdate(Void... values) {
}
}
private void getSocket()
{
doStop=true;
InetSocketAddress address = new InetSocketAddress("xxx.xxx.xxx.x", 8000);
socket = new Socket();
int timeout = 6000;
try {
socket.connect(address, timeout);
} catch (IOException e2) {
e2.printStackTrace();
}
musicLength = 1024;
InputStream is = null;
try {
is = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
try{
int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT );
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT, minSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
} catch (Throwable t)
{
t.printStackTrace();
doStop = true;
}
thread = new WriteToAudio();
thread.start();
int i = 0;
int j=0;
try {
if(dis.available()>0)Log.d("PCMSocket", "receiving");
music = new byte[4];
while (dis.available() > 0)
{
music[i]=0;
music[i] = dis.readByte();
if(i==3)
{
int asInt = 0;
asInt = ((music[0] & 0xFF) << 0)
| ((music[1] & 0xFF) << 8)
| ((music[2] & 0xFF) << 16)
| ((music[3] & 0xFF) << 24);
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
fmusic[j]=asFloat;
}
i++;
j++;
if(i==4)
{
music = new byte[4];
i=0;
}
if(j==1023)
{
j=0;
if(doStop)doStop=false;
}
}
} catch (IOException e) {
e.printStackTrace();
}
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public class WriteToAudio extends Thread
{
public void run()
{
while(true){
while(!doStop)
{
try{
writeSamples(fmusic);
}catch(Exception e)
{
e.printStackTrace();
}
doStop = true;
}
}
}
};
public void writeSamples(float[] samples)
{
fillBuffer( samples );
audioTrack.write( buffer, 0, samples.length );
}
private void fillBuffer( float[] samples )
{
if( buffer.length < samples.length )
buffer = new short[samples.length*4];
for( int i = 0; i < samples.length; i++ )
{
buffer[i] = (short)(samples[i] * Short.MAX_VALUE);
}
}
}
audioTrack.play()
до того, как вы что-то получили. Не могли бы вы начать это после получения первых или нескольких образцов? Также вы создаете и запускаете второй поток в первом. Я бы это убрал. Также создание плеера вне потока. - person greenapps   schedule 03.06.2014That multithreading also looks suspicious, though.
как сказал иммибис. Второй поток полностью потребляет одно ядро или иным образом сильно влияет на скорость. Вы можете просто скопировать в буфер, где вы установилиdoStop=false
, а затем только запустить поток для копирования в плеер. Или нет темы и просто скопировать/написать туда же. - person greenapps   schedule 03.06.2014