Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/211.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Android:音频曲目(流模式)在写入之间切换_Java_Android_Multithreading_Audio_Audiotrack - Fatal编程技术网

Java Android:音频曲目(流模式)在写入之间切换

Java Android:音频曲目(流模式)在写入之间切换,java,android,multithreading,audio,audiotrack,Java,Android,Multithreading,Audio,Audiotrack,我的主要目标是能够在局域网中将音频从一个设备流到另一个设备。我计划通过将mp3文件读入一个字节[](我已经开始工作)并将其作为udp数据包发送到第二台设备并在那里播放(我告诉您,以防这已经是错误的方法)。我目前一直在玩字节数组。我使用mp3中的解码器(path、startMs、durationMs)功能读取文件。目前,我能够听到音频,但在每次滴答声(我阅读文件的部分)后,我都会听到一些ms的“无”音,这会导致不良的听力体验。我认为这与缓冲区大小有关,并试着对其进行一些调整,但这并没有真正改变什么

我的主要目标是能够在局域网中将音频从一个设备流到另一个设备。我计划通过将mp3文件读入一个字节[](我已经开始工作)并将其作为udp数据包发送到第二台设备并在那里播放(我告诉您,以防这已经是错误的方法)。我目前一直在玩字节数组。我使用mp3中的
解码器(path、startMs、durationMs)
功能读取文件。目前,我能够听到音频,但在每次滴答声(我阅读文件的部分)后,我都会听到一些ms的“无”音,这会导致不良的听力体验。我认为这与缓冲区大小有关,并试着对其进行一些调整,但这并没有真正改变什么,也没有添加
音轨。我还考虑将每个for()-循环放在不同的线程中,但这根本不起作用(这是有道理的)。我也尝试过先读取文件,然后将字节[]放入Arraylist,因为这可能是由于文件读取速度慢而导致的问题,但仍然是相同的体验。了解
Log.e(“DEBUG”,“Length”+data.Length”)也可能有所帮助
只在每个勾号上显示,这意味着每次勾号上都只会发生写入操作(这可能就是问题所在)。我怎样才能摆脱歌曲中的这些空白部分

以下是单击按钮时执行的代码:

song.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Thread thrd = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                int tick = 1000;
                                int max = 9000;
                                int sampleRate = 44100;
                                int bufSize = AudioTrack.getMinBufferSize(sampleRate*4, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
                                byte[] data = decode(path, 0, tick);
                                AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,
                                        44100, AudioFormat.CHANNEL_OUT_STEREO,
                                        AudioFormat.ENCODING_PCM_16BIT, bufSize,
                                        AudioTrack.MODE_STREAM, AudioTrack.WRITE_NON_BLOCKING);
                                track.play();
                                track.write(data, 0, data.length);
                                Log.e("DEBUG", "Length " + data.length);
                                for(int i = tick; i < max; i+=tick) {
                                    data = decode(path, i, tick);
                                    track.write(data, 0, data.length);
                                    Log.e("DEBUG", "Length " + data.length);
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    thrd.start();
                }
            });

我认为decode函数不是问题所在,因为返回的字节[]似乎相当好。也许阅读过程可以像以后一样优化,当我真的流式播放音频并总是阅读大约10毫秒的部分时,总是打开和关闭文件可能是一个问题。

导致这一问题的根本原因是:您使用的
decode()
函数不是专门为其设计的。即使出现“代码>引号())/代码>将允许您以随机访问方式解码MP3流的任何部分,但实际上,返回的音频的前几毫秒始终是静默的,无论是从歌曲的开头开始,还是从中间开始。这种沉默造成了“鸿沟”。显然,
decode()
函数更多的是用于在随机位置重新开始播放,例如由于用户“seek”

decode()
的行为是这样的,因为为了解码第N个压缩数据块,解码器需要块N-1和块N。对应于块N的解压缩数据将是良好的,但块N-1的数据将具有这种“淡入”声音。这是.mp3解码器的一个通用功能,我知道AAC也有这种情况。同时,解码块N+1、N+2、N+3等没有问题,因为在每种情况下,解码器已经具有前一块

一种解决方案是更改
decode()
函数:

private Decoder decoder;
private float totalMs;
private Bitstream bitstream;
private InputStream inputStream;

//call this once, when it is time to start a new song:
private void startNewSong(String path) throws IOException
{
    decoder = new Decoder();
    totalMs = 0;
    File file = new File(path);
    inputStream = new BufferedInputStream(new FileInputStream(file), 8 * 1024);
    bitstream = new Bitstream(inputStream);
}

private byte[] decode(String path, int startMs, int maxMs)
        throws IOException {
    ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);


    try {
        boolean done = false;
        while (! done) {
            Header frameHeader = bitstream.readFrame();
            if (frameHeader == null) {
                done = true;
                inputStream.close();   //Note this change. Now, the song is done. You can also clean up the decoder here.
            } else {
                totalMs += frameHeader.ms_per_frame();

                SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);

                if (output.getSampleFrequency() != 44100
                        || output.getChannelCount() != 2) {
                    Log.w("ERROR", "mono or non-44100 MP3 not supported");
                }

                short[] pcm = output.getBuffer();
                for (short s : pcm) {
                    outStream.write(s & 0xff);
                    outStream.write((s >> 8) & 0xff);
                }

                if (totalMs >= (startMs + maxMs)) {
                    done = true;
                }
            }
            bitstream.closeFrame();
        }
    } catch (BitstreamException e) {
        throw new IOException("Bitstream error: " + e);
    } catch (DecoderException e) {
        Log.w("ERROR", "Decoder error", e);
    }
    return outStream.toByteArray();
}
这段代码有点粗糙,需要改进。但是一般的方法是,每次使用一个新的方法来解码更多的歌曲,而不是随机访问,
decode()
;再多读一点文件,再向解码器发送几个块。由于解码器(和
位流
)状态在每次调用
decode()
之间保持不变,因此无需搜索块N-1

UDP和流媒体


UDP方法的有效性取决于很多因素。您可能需要寻找其他问题来解决这个问题。UDP可以方便地广播到给定子网上的多个设备,但它不能帮助您确保按顺序接收数据包,或者根本不能。您可能需要TCP。还考虑是否要传输已编码的mp3块(那些由代码> >比特流.Read Frror()/代码>返回的)或解压缩音频块。还要考虑如何处理网络延迟、断开连接和缓冲。这里有许多困难的设计决策,每个选择都有利弊。祝你好运。

请添加你的
decode()
函数的实现。@greeble31谢谢你的反馈,decode()现在包括在内了!注意你正在使用的
AudioTrack
构造器;您的最后一个参数实际上是不匹配的。你所有的写操作都被阻塞了。为了使您选择的线程方案能够工作,它们必须是。您假设
write()
准确返回请求的字节数,因此您不希望有任何“短写入”。顺便说一下,您的写入需要是阻塞的,而不是非非阻塞的。这就是我第二条评论的意思。或者,每次你从
decode()
得到一个结果时,你可以将它转换成一个方波,用这个模式替换数组的内容,端到端重复,覆盖整个数组:
[127,127,127,127,127,127,127,0,0,0,0,0]
。这将产生一个连续的、高调的音调,没有任何间隙。如果在你将音频转换成9秒长的方波后,仍然存在间隙,那么我肯定错了。您可以使用
for
循环来完成它:
如果(((i/16)%2)==0)数据[i]=127;else数据[i]=0
private Decoder decoder;
private float totalMs;
private Bitstream bitstream;
private InputStream inputStream;

//call this once, when it is time to start a new song:
private void startNewSong(String path) throws IOException
{
    decoder = new Decoder();
    totalMs = 0;
    File file = new File(path);
    inputStream = new BufferedInputStream(new FileInputStream(file), 8 * 1024);
    bitstream = new Bitstream(inputStream);
}

private byte[] decode(String path, int startMs, int maxMs)
        throws IOException {
    ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);


    try {
        boolean done = false;
        while (! done) {
            Header frameHeader = bitstream.readFrame();
            if (frameHeader == null) {
                done = true;
                inputStream.close();   //Note this change. Now, the song is done. You can also clean up the decoder here.
            } else {
                totalMs += frameHeader.ms_per_frame();

                SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);

                if (output.getSampleFrequency() != 44100
                        || output.getChannelCount() != 2) {
                    Log.w("ERROR", "mono or non-44100 MP3 not supported");
                }

                short[] pcm = output.getBuffer();
                for (short s : pcm) {
                    outStream.write(s & 0xff);
                    outStream.write((s >> 8) & 0xff);
                }

                if (totalMs >= (startMs + maxMs)) {
                    done = true;
                }
            }
            bitstream.closeFrame();
        }
    } catch (BitstreamException e) {
        throw new IOException("Bitstream error: " + e);
    } catch (DecoderException e) {
        Log.w("ERROR", "Decoder error", e);
    }
    return outStream.toByteArray();
}