环形缓冲区无法读取正确的数据

时间:2017-10-11 07:55:36

标签: java buffer

我开始研究一个项目,以不同​​的频率生成声音。在那个目标中,我使用了3个对象:

  • 用于创建正弦信号并生成编号(使用简单整数ID)的声音数据包的发生器,以44100 Hz采样并使用“可观察”模式。
  • “环形缓冲区”,它是发生器的“观察者”。它会将数据包存储在用作缓冲区的数组(不是列表)中
  • 读取器将声音数据读入环形缓冲区

Main类中,我在创建缓冲区和创建阅读器之间创建了一个中断(Thread.sleep)。它允许我在讲座开始之前填充一些环形缓冲区(我也希望看到会发生什么......)

问题在于环形缓冲区(代码如下)。如果休息时间足够短,无法在讲座开始时填满环形缓冲区,那么一切似乎都或多或少都可以。 但是,如果我的缓冲区被填满(以及许多其他无法解释的情况),我的声音有很多寄生虫。如果我分析读取数据包ID的结果,这就是我得到的:

Buffer.load - packet ID : 63 - buffer pos. = 62
Buffer.load - packet ID : 64 - buffer pos. = 63
Buffer.load - packet ID : 65 - buffer pos. = 64
Buffer.read - packet ID : 65 - buffer pos. = 2
Buffer.read - packet ID : 65 - buffer pos. = 3
Buffer.read - packet ID : 65 - buffer pos. = 4
Buffer.read - packet ID : 65 - buffer pos. = 5

读取方法始终发送相同的数据包ID,无论读取器的位置如何。但是,加载方法似乎正常工作,并在以下位置加载以下ID。

public class Buffer2 implements Observer {

    private Object[] buffer;
    /*
     * important note about the buffer : 
     * It is an array and not a list.
     * So, it will be impossible to shift on the left or on the right the elements
     * (a method like deleteFirst or similar doesn't exsits for arrays)
     * in case of lecture (then suppression) or adding of en element.
     * So, I'll use three variables : one to know the lecture position in the buffer
     * one for the writing position
     * a last one to know the volume data into the buffer.
     */
    private int inBuffer, bufferSize, first, last;

    public Buffer2 (int bufferSize) {
        buffer = new Object[bufferSize];
        inBuffer=0;
        last=0;
        this.bufferSize=bufferSize;
    }

    public synchronized byte[] read () {
        while (inBuffer==0) {
            try {
                wait();
                System.out.println("Buffer.read = null");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // See the method "load()" for the explanation of the next line
        first=(first+1)%bufferSize;
        inBuffer--;
        /*
         * This method awake the threads which were sleeping because of the load() method
         * when the buffer is full 
         */
        notifyAll();
        Object[] temp = (Object[]) buffer[first];
        System.out.println("Buffer.read - packet ID : "+(int)temp[0]+" - buffer pos. = "+first);
        return (byte[]) temp[1];
    }

    public synchronized void load (Object[] data) {
        while (inBuffer==bufferSize) {
            try {
                System.out.println("Buffer.load : full !");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        /*
         * Method to calculate the position to add the new value : 
         * Ex : buffer size = 10 and we just read the 3rd position
         * (3+1)%10 = 4 => the new value will be added in the 4th position of the array
         * Ex2 : if the position was 9 :
         * (9+1)%10=0 => the value would be added at the position 0 of the array.
         */
        last = (last+1)%bufferSize;
        inBuffer++;
        System.out.println("Buffer.load - packet ID : "+(int)data[0]+" - buffer pos. = "+last);
        buffer[last]=data;
        /*
         * Awake for all waiting threads locked
         * because the buffer was empty
         */
        notifyAll();
    }

    @Override
    public void update(Observable observable, Object obj) {
        if (observable instanceof Generator) {
            load((Object[]) obj);           
        }       
    }
}

有什么问题?谢谢你的所有答案。

经过几次测试和检查,我发现了一些事情:

  • update()方法接收不同的值,并使用这些值调用load()方法。所以,它似乎按要求工作。
  • 在使用指令buffer[last]=data;更改缓冲区之前,我检查了数据是否来自update方法:是的,它们是。所以,它有效。
  • 在更改缓冲区之后,我检查整个缓冲区中的数据(对于测试,我只使用了具有10个值的缓冲区)。这就是问题所在:所有的值都是相同的,即使它们是在数组的前面引入的。换句话说,当我在数组中加载一个新值时,所有的值都会被修改并等于最后一个值。

感谢Thomas提供了更准确地识别问题的技巧,但是,我仍然无法解决它。

谁会帮助我更多?

谢谢大家。

1 个答案:

答案 0 :(得分:0)

我终于找到并修复了问题,帮助了托马斯的提示。实际上,当我通过update()方法收到我的数据时,我有一个副作用。 收到的数据包是Object[2],其中的2个对象是标题的另一个Object[]和数据本身的byte[]

因此,我必须读取此数据包的每个数组的每个值,以便将它们写入“一次性”对象(Object[] packet = new Object[2])中。

因此,update()方法的代码是:

    @Override
    public synchronized void update(Observable observable, Object obj) {
        if (observable instanceof Generator) {
            // A packet contain a header (header) and datas (data)
            Object[] packet = new Object[2];

                /*
             * The header contain 3 datas : 
             * [0] ID
             * [1] The number of byes in the array of bytes
             * [2] the AudioFormat
             */
            Object[] header = new Object[3];
            header[0]= ((Object[]) ((Object[]) obj)[0])[0];
            header[1]= ((Object[]) ((Object[]) obj)[0])[1];
            header[2]= ((Object[]) ((Object[]) obj)[0])[2];
            // I reconstruct the hehader in the position [0] in the packet
            packet[0]=header;


            byte[] data = new byte[(int) header[1]*4];
            for (int i=0; i< data.length ; i++) {
                /*
                 * Reading each byte in the [i] position of the bytes[]
                 * This array of bytes is in the position [1] of the array which is the Object[] obj
                 */
                data[i] =  ((byte[]) ((Object[]) obj)[1])[i];
            }
            // I reconstruct the data in the position [1] of the packet
            packet[1]=data;
            // I feed the load() method with the packet
            load(packet);
        }       
    }

我希望我的回答可以帮助别人。祝你有个美好的一天。