Java-在X位置开始音频播放

时间:2018-10-01 16:47:37

标签: java arrays wav javasound

编辑: 我正在使用.wav文件

我正在尝试找出如何在特定位置开始播放音频(例如:进入音频文件10秒而不是开始播放)。阅读 SourceDataLine 的文档后,我相信可以在以下情况下使用 offset 来实现此目标:

line.write(byte[] b, int offset, int length)

但是每次我尝试使用除0(我相信的默认值)以外的任何值时,我都会得到 java.lang.IndexOutOfBoundsException ,也许它尚未读取x字节位置,所以无法写x字节的位置?我不确定,挠了挠头。

我认为这将是一个足够普遍的请求,但似乎无法在线找到与此相关的任何内容,只能暂停和恢复音频。我可能搜索不正确。

以防万一,这是我目前正在制作音频的方式:

     AudioInputStream stream = AudioSystem.getAudioInputStream("...file...");
     AudioFormat format = stream.getFormat();
     SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, format,((int)stream.getFrameLength()*format.getFrameSize()));
     SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
     int bufferSize = line.getBufferSize();
     byte inBuffer[] = new byte[bufferSize];
     byte outBuffer[] = new byte[bufferSize];
     int numRead, numWritten;

     do {
         numRead = audioStream.read(inBuffer, 0, bufferSize);
            if(numRead <= 0) {
                myAudio.flushStream();
            } else {
                myAudio.writeBytesToStream(inBuffer, numRead);
            }
            do {
                numWritten = myAudio.readBytesFromStream(outBuffer, bufferSize);
                if(numWritten > 0) {
                    line.write(outBuffer, 0, numWritten);
                }
            } while(numWritten > 0);
        } while(numRead > 0);

2 个答案:

答案 0 :(得分:3)

您遇到的问题可能源于您在调整offset而未调整length的事实。如果数组的长度为10个字节,并且您将从偏移量5而不是0开始读取10个字节,则表示从数组末尾读取了5个字节。

我建议先在skip(long)上使用AudioInputStream跳过适当的字节数,然后写入该行。

AudioInputStream stream = AudioSystem.getAudioInputStream("...file...");
AudioFormat format = stream.getFormat();
// find out how many bytes you have to skip, this depends on bytes per frame (a.k.a. frameSize)
int secondsToSkip = 10;
long bytesToSkip = format.getFrameSize() * ((int)format.getFrameRate()) * secondsToSkip;
// now skip until the correct number of bytes have been skipped
int justSkipped = 0;
while (bytesToSkip > 0 && (justSkipped = stream.skip(bytesToSkip)) > 0) {
    bytesToSkip -= justSkipped;
}
// then proceed with writing to your line like you have done before
[...]

请注意,这仅在音频文件未压缩的情况下有效。如果要处理类似.mp3的问题,则首先必须将流转换为PCM(请参见https://stackoverflow.com/a/41850901/942774

答案 1 :(得分:0)

我创建了一个可以编译和运行的示例。您可以从任何时间点播放 .wav 文件。它也应该适用于 mp3 文件,但我还没有测试过。为此调用 mp3ToWav()。

import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;

public class PlayWavAtTimePoint {

    public static void main(String[] args) throws Exception {
        String fileName = args[0];
        int secondsToSkip = (Integer.parseInt(args[1]));

        PlayWavAtTimePoint program = new PlayWavAtTimePoint();
        AudioInputStream is = program.getAudioInputStream(fileName);
        program.skipFromBeginning(is, secondsToSkip);
        program.playSound(is);
    }

    private static void skipFromBeginning(AudioInputStream audioStream, int secondsToSkip) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
        AudioFormat format = audioStream.getFormat();

        // find out how many bytes you have to skip, this depends on bytes per frame (a.k.a. frameSize)
        long bytesToSkip = format.getFrameSize() * ((int)format.getFrameRate()) * secondsToSkip;


        // now skip until the correct number of bytes have been skipped
        long justSkipped = 0;
        while (bytesToSkip > 0 && (justSkipped = audioStream.skip(bytesToSkip)) > 0) {
            bytesToSkip -= justSkipped;
        }
    }


    private static final int BUFFER_SIZE = 128000;


    /**
     * @param filename the name of the file that is going to be played
     */
    public void playSound(String filename) throws IOException, UnsupportedAudioFileException, LineUnavailableException {
        AudioInputStream audioStream = getAudioInputStream(filename);
        playSound(audioStream);
    }

    private AudioInputStream getAudioInputStream(String filename) throws UnsupportedAudioFileException, IOException {
        return AudioSystem.getAudioInputStream(new File(filename));
    }

    public void playSound(AudioInputStream audioStream) throws LineUnavailableException, IOException {
        AudioFormat audioFormat = audioStream.getFormat();
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
        SourceDataLine audioOutput = (SourceDataLine) AudioSystem.getLine(info);
        audioOutput.open(audioFormat);
        audioOutput.start();

        //This seems to be reading the whole file into a buffer before playing ... not efficient.
        //Why not stream it?
        int nBytesRead = 0;

        byte[] abData = new byte[BUFFER_SIZE];
        while (nBytesRead != -1) {
                nBytesRead = audioStream.read(abData, 0, abData.length);
            if (nBytesRead >= 0) {
                audioOutput.write(abData, 0, nBytesRead);
            }
        }

        audioOutput.drain();
        audioOutput.close();
    }

    /**
     * Invoke this function to convert to a playable file.
     */
    public static void mp3ToWav(File mp3Data) throws UnsupportedAudioFileException, IOException {
        // open stream
        AudioInputStream mp3Stream = AudioSystem.getAudioInputStream(mp3Data);
        AudioFormat sourceFormat = mp3Stream.getFormat();
        // create audio format object for the desired stream/audio format
        // this is *not* the same as the file format (wav)
        AudioFormat convertFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
                sourceFormat.getSampleRate(), 16,
                sourceFormat.getChannels(),
                sourceFormat.getChannels() * 2,
                sourceFormat.getSampleRate(),
                false);
        // create stream that delivers the desired format
        AudioInputStream converted = AudioSystem.getAudioInputStream(convertFormat, mp3Stream);
        // write stream into a file with file format wav
        AudioSystem.write(converted, AudioFileFormat.Type.WAVE, new File("/tmp/out.wav"));
    }
}