在安卓系统中使用AudioTrack来播放WAV文件

26 人关注

我正在使用安卓系统,试图让我的AudioTrack应用程序播放一个Windows .wav文件(Tada.wav)。 坦率地说,这不应该这么难,但我听到了很多奇怪的东西。 该文件保存在我的手机的迷你SD卡上,读取内容似乎没有问题,但当我播放该文件时(参数我只确定是正确的),我得到几秒钟的白噪声,然后声音似乎解决了,只是可能是正确的。

我已经成功地在电话上录制并播放了我自己的声音 -- 我根据这个例子中的指示创建了一个.pcm文件。

http://emeadev.blogspot.com/2009/09/raw-audio-manipulation-in-android.html

(没有反向屏蔽)...

谁有一些建议或意识到网络上有一个在Android上播放.wav文件的例子?

android
wav
audio-streaming
Rich
Rich
发布于 2010-10-13
7 个回答
Rich
Rich
发布于 2019-08-04
已采纳
0 人赞同

我偶然发现了答案(坦率地说,是通过尝试&^@!我不认为会成功),如果有人感兴趣的话...... 在我的原始代码中(源自原帖链接中的例子),数据是这样从文件中读取的。

 InputStream is = new FileInputStream       (file);
    BufferedInputStream bis = new BufferedInputStream   (is, 8000);
    DataInputStream dis = new DataInputStream       (bis);      //  Create a DataInputStream to read the audio data from the saved file
int i = 0;                                                          //  Read the file into the "music" array
while (dis.available() > 0)
        music[i] = dis.readShort();                                     //  This assignment does not reverse the order
    dis.close();                                                        //  Close the input stream

在这个版本中,music[]是SHORTS的数组。 所以,readShort()方法在这里似乎是有意义的,因为数据是16位PCM......。 然而,在Android上,这似乎是个问题。 我把这段代码改为如下。

     music=new byte[(int) file.length()];//size & length of the file
    InputStream             is  = new FileInputStream       (file);
    BufferedInputStream     bis = new BufferedInputStream   (is, 8000);
    DataInputStream         dis = new DataInputStream       (bis);      //  Create a DataInputStream to read the audio data from the saved file
int i = 0;                                                          //  Read the file into the "music" array
while (dis.available() > 0)
        music[i] = dis.readByte();                                      //  This assignment does not reverse the order
    dis.close();                                                        //  Close the input stream

在这个版本中,music[]是一个BYTES数组。 我仍然告诉AudioTrack它是16位PCM数据,而我的Android似乎对这样配置的AudioTrack中写入字节数组没有问题...... 无论如何,它最终听起来是正确的,所以如果有人想在他们的Android上播放Windows的声音,出于某种原因,这就是解决方案。 啊,Endianness......

gary
这应该是公认的答案,因为它回答了这个问题。)
Rich
我可以发誓,我在这里留下了 "谢谢你,加里",但现在不在这里了...... 谢谢你,加里!!
篇文章 的主题似乎是:在阅读WAV格式时,使用Java的short和byte之间的差异。
如何知道music[]的长度来进行初始化?
Rich
基本上,就是 "足够大"。 如果你知道你要发送的文件,就使用一个刚好大于其大小的值,否则你或多或少要从你的屁股里拿出一个数字。
R Earle Harris
R Earle Harris
发布于 2019-08-04
0 人赞同

我发现这个问题有很多冗长的答案。我的最终解决方案,考虑到所有的剪切和粘贴几乎都是我的,归结为:。

public boolean play() {
    int i = 0;
    byte[] music = null;
    InputStream is = mContext.getResources().openRawResource(R.raw.noise);
    at = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
        AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
        minBufferSize, AudioTrack.MODE_STREAM);
    try{
        music = new byte[512];
        at.play();
        while((i = is.read(music)) != -1)
            at.write(music, 0, i);
    } catch (IOException e) {
        e.printStackTrace();
    at.stop();
    at.release();
    return STOPPED;

STOPPED只是作为重置暂停/播放按钮的信号送回一个 "真"。 而在类的初始化器中。

public Mp3Track(Context context) {
    mContext = context;
    minBufferSize = AudioTrack.getMinBufferSize(44100,
        AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);

Context只是来自调用活动的 "this"。 你可以在SD卡上使用一个FileInputStream,等等。我的文件是在res/raw

谢谢你,在我试过的所有答案中,这个答案的结果最好。虽然我在轨道启动时仍会有一点音频咔哒声....。
我在开始和结束时都有咔哒声,而且播放时超过了文件的末尾,继续播放甚至不在任何音频文件中的音频。很奇怪!
音频以慢动作播放。可能是什么问题。
Aaron C
Aaron C
发布于 2019-08-04
0 人赞同

你在把文件的其他数据转入缓冲区之前,是否跳过了文件的前44字节? 前44个字节是WAVE的头,如果你想播放它们,它们听起来就像随机的噪音。

另外,你确定你创建的AudioTrack的属性与你要播放的WAVE相同(采样率、比特率、通道数等)? 实际上,Windows在文件属性页面中很好地提供了这些信息。

Rich
我已经尝试了我能想到的跳过标题和不跳过标题的所有组合 -- 没有什么运气。 白噪声的持续时间远远超过44个比特的价值。 Tada.wav是一个168 kByte的文件,所以 "播放 "标题应该几乎不会被发现,对吗?
正确,文件头会在很短的时间内产生垃圾。 你确定你创建的AudioTrack的属性与你要播放的WAVE相同(采样率、比特率、通道数等)?
Rich
我有一个十六进制编辑器,我肯定认为我从头文件中正确地得到了数字。 我有点惊讶,我还没能在互联网上找到某种wav文件的分析器。 我被难住了,Windows媒体播放器当然可以播放这个文件......
如果你想理智地检查你的数值,Window的文件属性页面实际上能很好地提供这些信息。
请记住,wav的头并不总是44字节。对于32位的浮动数据头是58字节。
Sixro
Sixro
发布于 2019-08-04
0 人赞同

正如 Aaron C 所说,你必须跳过最初的44个字节,或者(我更喜欢)读取前44个字节,这些字节是WAVE的头。这样你就能知道WAVE包含多少个通道、每个样本的比特数、长度等等。

在这里 你可以找到一个很好的WAVE头解析器/写入器的实现。

yano
yano
发布于 2019-08-04
0 人赞同

请不要让糟糕的解析代码长期存在。WAV解析是很容易实现的 http://soundfile.sapp.org/doc/WaveFormat/ 你会感谢你自己,因为你能够解析诸如采样率、比特深度和通道数的东西。

另外,x86和ARM(至少默认情况下)都是小恩典,所以原生恩典的WAV文件应该不需要任何洗牌就可以了。

如果你能具体说明你认为哪些解析代码是糟糕的,以及为什么,你的回答会更有用。
Sabin
Sabin
发布于 2019-08-04
0 人赞同

只要确认你在 AudioTrack 构造函数中是否有AudioTrack AudioTrack.MODE_STREAM 而不是 AudioTrack.MODE_STATIC

AudioTrack at = new AudioTrack(
  AudioManager.STREAM_MUSIC,
  sampleRate,
  AudioFormat.CHANNEL_IN_STEREO,
  AudioFormat.ENCODING_PCM_16BIT,
  // buffer length in bytes
  outputBufferSize,
  AudioTrack.MODE_STREAM
Mike Yang
Mike Yang
发布于 2019-08-04
0 人赞同

wav文件样本。
http://www.mauvecloud.net/sounds/pcm1644m.wav

样品代码。

public class AudioTrackPlayer {
    Context mContext;
    int minBufferSize;
    AudioTrack at;
    boolean STOPPED;
    public AudioTrackPlayer(Context context) {
        Log.d("------","init");
        mContext = context;
        minBufferSize = AudioTrack.getMinBufferSize(44100,
                AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
    public boolean play() {
        Log.d("------","play");
        int i = 0;
        byte[] music = null;
        InputStream is = mContext.getResources().openRawResource(R.raw.pcm1644m);
        at = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
                AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
                minBufferSize, AudioTrack.MODE_STREAM);
        try {
            music = new byte[512];
            at.play();
            while ((i = is.read(music)) != -1)
                at.write(music, 0, i);
        } catch (IOException e) {
            e.printStackTrace();
        at.stop();