Почему мой звук отстает? - PullRequest
2 голосов
/ 31 марта 2011

Я работаю над системой приложений для обработки звуковых данных.Первое приложение просто читает с разъема микрофона и отправляет данные в следующее приложение.Основной цикл многократно выполняет этот код:

0 : Globals.mySleep(waitTime); // tells the thread to sleep for the proper amount of time for a given data format  
1 : inputLine.read(buffer, 0, bufferSize); // reads sound data from the microphone jack into buffer
2 : if(connections.get(REGISTER) != null) { // if the next application is connected
3 :     DataSlice slice = new DataSlice(buffer, serialIDCounter++, getDeviceName()); // create a slice of data to send, containing the sound data
4 :     try{
5 :         connections.get(REGISTER).sendDataSlice(slice); // send the data to the next application. supposed to block until next application receives the data
6 :         connections.get(REGISTER).flush(); // make sure data gets sent
7 :     } catch (IOException e) {
8 :         // Stream has been broken. Shut Down
9 :         close();
10:     }
11: }

Когда я запускаю систему, она всегда отстает на несколько секунд.Если я ставлю систему на паузу (приложение с графическим интерфейсом сообщает приложению, которое следует за приложением ввода, прекратить прием данных из приложения ввода, поэтому приложение ввода должно блокироваться в строке 5, когда оно приостановлено), подождите, затем воспроизведите снова, система отстает дополнительно на сколько бы долгоЯ только что сделал паузу.Например, если он начинался с 10-секундной задержки, затем приостанавливался на 5 секунд и воспроизводился снова, он затем отставал бы на 15 секунд.

Это происходит, когда я запускаю программу в качестве исполняемой банки.файл.Это не происходит, когда я запускаю его из Eclipse.

Я тестировал это на двух компьютерах, на обоих работает Ubuntu Linux 10.04 LTS.Это происходит на одном, а не на другом.Хотя, с другой стороны, у меня возникает совсем другая проблема, когда я пытаюсь запустить его из Eclipse.Не уверен, что с этим делать.Если вам нужны какие-то технические характеристики на компьютерах, я с удовольствием предоставлю их вам.Просто скажите мне, какие спецификации вы хотите и как их получить.

Может кто-нибудь сказать мне, что может быть причиной задержки?Спасибо.

- РЕДАКТИРОВАТЬ -

По предложению Эндрю я создал то, что я считаю SSCCE :

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.sound.sampled.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main implements MouseListener{
    // Class that reads a signal from Line-in source and sends that signal
    // to either a recorder module or the signal-viewing pipeline
    public class PlayThread extends Thread {

        byte[] buffer = new byte[bufferSize];
        boolean playing = false;
        boolean connected = false;

        PlayThread() {}

        public void run() {
            while(true) {
                try {
                    sleep(waitTime);
                    inputLine.read(buffer, 0, bufferSize);
                    if(connected) {
                        while(!playing)
                            sleep(100);
                        int max = 0;
                        for(int i = 0; i < buffer.length; i++) {
                            if(Math.abs(buffer[i]) > max)
                                max = Math.abs(buffer[i]);
                        }
                        System.out.println("Max: " + max);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void setPlaying(boolean playing) {
            this.playing = playing;
        }

        public void setConnected(boolean connected) {
            this.connected = connected;
        }
    }

    TargetDataLine inputLine;
    AudioFormat format;
    float sampleRate;
    int sampleSizeBits;
    int channels;
    int waitTime;
    int bufferSize;
    int slicesPerSecond;
    int windowSize = 512;
    PlayThread pThread;

    JFrame gui = new JFrame("Sound Lag");
    JPanel panel = new JPanel();
    JButton play = new JButton("Play"), pause = new JButton("Pause"),
            connect = new JButton("Connect"), disconnect = new JButton("Disconnect");

    Main() {
        sampleRate = 44100;
        sampleSizeBits = 16;
        channels = 2;
        bufferSize = (sampleSizeBits/8)*channels*windowSize;
        slicesPerSecond = (int) ((sampleRate/(float)channels)/(float)windowSize);
        waitTime = (int)((((1000f/sampleRate)/(float)sampleSizeBits)/2f)*8f*(float)bufferSize);

        play.addMouseListener(this);
        pause.addMouseListener(this);
        connect.addMouseListener(this);
        disconnect.addMouseListener(this);

        panel.add(play);
        panel.add(pause);
        panel.add(connect);
        panel.add(disconnect);
        gui.add(panel);
        gui.setVisible(true);
        gui.pack();
        gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void read() {
        // Open line from line-in
        format = new AudioFormat(sampleRate, sampleSizeBits, channels, true, true);

        // Obtain and open the lines.
        inputLine = getTargetDataLine();

        pThread = new PlayThread();
        pThread.start();
    }

    private TargetDataLine getTargetDataLine() {
        try {
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
            for (Mixer.Info mi : AudioSystem.getMixerInfo()) {
                TargetDataLine dataline = null;
                try {
                    Mixer mixer = AudioSystem.getMixer(mi);
                    dataline = (TargetDataLine)mixer.getLine(info);
                    dataline.open(format);
                    dataline.start();
                    return dataline;
                }
                catch (Exception e) {}
                if (dataline != null)
                    try {
                        dataline.close();
                    }
                    catch (Exception e) {}
            }
        }
        catch (Exception e) {}
        return null;
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.read();
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
        if(arg0.getSource() == play) {
            System.out.println("Playing");
            pThread.setPlaying(true);
        }
        else if(arg0.getSource() == pause) {
            System.out.println("Paused");
            pThread.setPlaying(false);
        }
        else if(arg0.getSource() == connect) {
            System.out.println("Connected");
            pThread.setConnected(true);
        }
        else if(arg0.getSource() == disconnect) {
            System.out.println("Disconnected");
            pThread.setConnected(false);
        }
    }

    @Override public void mouseEntered(MouseEvent arg0) {}
    @Override public void mouseExited(MouseEvent arg0) {}
    @Override public void mousePressed(MouseEvent arg0) {}
    @Override public void mouseReleased(MouseEvent arg0) {}
}

Этот кодсоздает окно с четырьмя кнопками: воспроизведение, пауза, подключение и отключение.Если вы нажмете кнопку воспроизведения, программа будет как в режиме воспроизведения.Если вы щелкнете по кнопке «Подключиться», приложение для ввода звука будет подключено к следующему модулю.
Для проверки выполните следующее:
Подключите звуковое устройство к гнезду микрофона (но ничего не воспроизводите).
Создайте исполняемый файл JAR из этого кода.
Запустите файл с терминала.
Нажмите «play».
Нажмите «connect».

В этот момент вы должны увидеть группу меньших чисел, идущих по терминалу.

На вашем звуковом устройстве начните воспроизводить звуки.

Вы должны немедленно начать видеть большецифры в терминале.

Остановите воспроизведение звуков на звуковом устройстве (следует вернуться к меньшим номерам в терминале).
Нажмите «Пауза».
Подождите 5 секунд.
Нажмите «Воспроизвести»,

Начните воспроизведение звука с аудиоустройства.

Вот здесь и появляется ошибка. Если я запускаю этот код в Eclipse, я снова получаю большие числа.Если я просто запускаю файл jar, задержка составляет 5 секунд, тогда я получаю большие цифры.

Есть новые мысли?

Ответы [ 3 ]

2 голосов
/ 01 апреля 2011

Это исправлено. Всякий раз, когда я хочу запустить звуковой поток (когда я нажимаю кнопку воспроизведения), я закрываю текущий поток и открываю новый.

Я не осознавал, что TargetDataLine на самом деле содержит буфер звуковых данных, который просто выбирается при каждом вызове метода чтения.

Похоже, что когда я запускал приложение из Eclipse, оно использовало тип TargetDataLine другого типа, чем когда я запускал его как исполняемый файл JAR. Об этом свидетельствует разница в размере между буферами. Хотя разница в размере составляла всего 2, поэтому я думаю, что проблема была не в размере буфера, а в чем-то еще, касающемся извлеченных TargetDataLines.

Как ни странно, удаление Globals.mySleep (waitTime) сработало при исправлении SSCCE, но не реальной программы, которую он должен был представлять.

Я пытался как слить, так и промыть Линию, вместо того, чтобы заменить ее, но ни один из них, похоже, не работал, хотя, возможно, я использовал их неправильно.

Итак, проблема заключалась в том, что буфер DataLine заполнялся, и, пока программа не воспроизводилась, буфер не очищался, поэтому, когда он все же начал воспроизведение, он продолжал извлекать данные из буфера с обычной скоростью воспроизведения, вызывая его отставание.

Решение: Когда программа начнет воспроизведение, замените DataLine.

- EDIT -

Дальнейшие наблюдения показывают, что когда я бежал из Eclipse, он, похоже, использовал другой JRE, чем когда я бегал как файл jar. Я установил java-программу по умолчанию на java-6-sun вместо java-6-openjdk, и она отлично работает из файла jar.

Кроме того, я попытался запустить метод замены DataLine на другом компьютере. На этом компьютере у меня был неприятный перерыв в сигнале. Казалось, что потянуть новую DataLine займет больше времени, поэтому я решил, что это не сработает. Теперь я просто все время читаю из DataLine. Я просто никуда не посылаю сигнал, если система приостановлена.

1 голос
/ 31 марта 2011

Globals.mySleep (waitTime);// заставляет поток спать в течение необходимого количества времени для данного формата данных

I подозреваемый 'правильный' waitTime здесь равен '0'.

Если вы хотите чего-то большего, чем подозрения, я рекомендую опубликовать SSCCE (без номеров строк).

1 голос
/ 31 марта 2011

Я считаю, что лучше всего делать в таких ситуациях, когда ваш код работает медленно, но вы не знаете, зачем использовать профилировщик, http://www.quest.com/jprobe/software_download.aspx вы можете получить бесплатный след этого профилировщика Java, и он будет построчно сообщайте вам, сколько времени потрачено и сколько раз оно выполнено, и вы сможете точно определить, что именно замедляет ваш код, с помощью этого.

Надеюсь, это поможет, Имон

...