Подтвердить что ты не робот

Java audio visualizer - Как записать звук в реальном времени в динамик?

tl; dr Для будущих читателей запись звука в реальном времени невозможна (на данный момент) с Java или С#. Используйте С++, поскольку он предоставляет множество аудио файлов api.


Моя цель - получить текущий звук, воспроизводимый на Windows-машине, и проанализировать звук так же, как графический аудио визуализатор (получить свойство громкости и Hz (базовый и высокий)). Когда я говорю о текущем звуке, я имею в виду, если бы кто-то играл в видео Youtube или Spotify, и эта программа будет читать этот аудиовыход. У меня нет намерения играть в звук, но фиксировать его в режиме реального времени и визуализировать.

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

Я просто не понимаю этого. Я совершенно незнакома, и любая помощь будет оценена.

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

Изменить 2 (снова отредактировано): от эксперимента и осмотра, я написал этот код ниже. Я думаю, что это приводит меня в том направлении, в котором я хочу, но я не знаю, как его закончить.

    Mixer.Info[] mixers = AudioSystem.getMixerInfo();
    for (Mixer.Info mixerInfo : mixers) {
        Mixer mixer = AudioSystem.getMixer(mixerInfo);
        try {
            mixer.open();
            Line.Info[] lines = mixer.getTargetLineInfo();
            for (Line.Info linfo : lines) {

                Line line = AudioSystem.getLine(linfo);

                //here I'm opening the line, but I don't know how to grab data
                line.open();

            }
        } catch (LineUnavailableException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

Я использовал этот источник: Проверка уровня аудиовоспроизведения в строке микшера, но я не хочу проверять все строки, которые воспроизводят громкость, Мне просто нужен микшер по умолчанию для пользователей, получите эту Линию и сможете анализировать данные.

Изменить 3: Я пробовал:

    //creating a format for getting sound
    float sampleRate = 8000;
    int sampleSizeInBits = 16;
    int channels = 2;
    boolean signed = true;
    boolean bigEndian = true;
    AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, channels, 
        signed, bigEndian);

    //creating a line based off of the format
    DataLine.Info info = new DataLine.Info( TargetDataLine.class, format);
    TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);

    //opening and starting that line
    line.open(format);
    line.start();

    while (conditionIsTrue){
        //here, I don't know what to put as the parameters.
        //Had I known, I don't know how I would get to analyze the data
        line.read();
    }

Я думаю, что я на правильном пути, используя код выше, но я не знаю, как извлечь звук и найти bpm, base, highble и т.д.

Редактировать 4: Это было интересно прочитать: Обработка звука с низкой задержкой в ​​режиме реального времени в Java. Это не касается того, какие классы и как реализовать на самом деле, но дает некоторое представление.

Изменить 5: @AndrewThompson Используя этот фрагмент кода, основанный на вашей ссылке, я смог выполнить итерацию по доступным исходным и целевым строкам.

Mixer.Info[] mixers = AudioSystem.getMixerInfo();
    for (Mixer.Info mixerInfo : mixers) {
        Mixer mixer = AudioSystem.getMixer(mixerInfo);
        try {
            mixer.open();
            Line.Info[] sourceLines = mixer.getSourceLineInfo();
            Line.Info[] targetLine = mixer.getTargetLineInfo();
            for (Line.Info sourceLinfo : sourceLines) {
                System.out.println(sourceLinfo );
            }
            for (Line.Info targetLinefo : targetLine) {
                System.out.println(targetLinefo);
            }

        } catch (LineUnavailableException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

Результат выглядит следующим образом:

interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
HEADPHONE target port
SPEAKER target port

Затем я создал метод, который получает уровни звука всех строк, которые выглядят следующим образом:

private static void getVolumeOfAllLines() {
    Mixer.Info[] mixers = AudioSystem.getMixerInfo();
    for (Mixer.Info mixerInfo : mixers) {
        Mixer mixer = AudioSystem.getMixer(mixerInfo);
        try {
            mixer.open();
            Line.Info[] lines = mixer.getSourceLineInfo();
            for (Line.Info linfo : lines) {
                DataLine line = (DataLine)AudioSystem.getLine(linfo);
                if(line != null)
                    System.out.println(line.getLevel());
            }
        } catch (LineUnavailableException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

- позволяет найти текущую строку, воспроизводящую звук, указывая на более высокий уровень громкости. Это возвращает:

-1.0
-1.0
-1.0
-1.0
-1.0
-1.0

Нет прогресса.


Новый код:

    private static void debug(){
    Mixer.Info[] mixers = AudioSystem.getMixerInfo();
    for (Mixer.Info mixerInfo : mixers) {
        Mixer mixer = AudioSystem.getMixer(mixerInfo);
        try {
            mixer.open();
            Line.Info[] lines = mixer.getTargetLineInfo();


            AudioFormat format = new AudioFormat(
                    AudioFormat.Encoding.PCM_SIGNED,
                    44100,
                    16, 2, 4,
                    44100, false);

            AudioFormat[] tdl = AudioSystem.getTargetFormats(AudioFormat.Encoding.PCM_SIGNED, format);

            for (Line.Info linfo : lines) {

                //Line line = AudioSystem.getLine(linfo);


                TargetDataLine line = null;
                DataLine.Info info = new DataLine.Info(TargetDataLine.class,
                        format); // format is an AudioFormat object
                if (!AudioSystem.isLineSupported(info))
                {
                    System.out.println("line not supported:" + line );
                }

                try
                {
                    line = (TargetDataLine) AudioSystem.getLine(info); //error
                    line.open(format);
                    System.out.println("line opened:" + line);

                    line.start();

                    byte[] buffer = new byte[1024];
                    int ii = 0;
                    int numBytesRead = 0;
                    while (ii++ < 100) {
                        // Read the next chunk of data from the TargetDataLine.
                        numBytesRead =  line.read(buffer, 0, buffer.length);

                        System.out.println("\nnumBytesRead:" + numBytesRead);
                        if (numBytesRead == 0) continue;
                        // following is a quickie test to see if content is only 0 vals
                        // present in the data that was read.

                        for (int i = 0; i < 16; i++)
                        {
                            if (buffer[i] != 0)
                                System.out.print(".");
                            else
                                System.out.print("0");
                        }
                    }

                } catch (LineUnavailableException ex) {
                    ex.printStackTrace();
                    //...
                }
            }
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
}
4b9b3361

Ответ 1

В учебниках Java есть хороший пример, который поможет вам извлечь данные PCM из строки. В учебнике под названием "Использование файлов и форматировщиков" есть пример кода в разделе "Чтение звуковых файлов" . Соответствующая часть является примером "фрагмента" и помечена кодом:

  // Here, do something useful with the audio data that 
  // now in the audioBytes array...

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


Добавляем некоторый код в ответ на комментарии.

В отличие от того, что вы не смогли применить к TargetDataLine, следующее, извлеченное из учебника, позволило мне передать строку в TargetDataLine.

    AudioFormat format = new AudioFormat(
        AudioFormat.Encoding.PCM_SIGNED, 
        44100,
        16, 2, 4, 
        44100, false);

    TargetDataLine line = null;
    DataLine.Info info = new DataLine.Info(TargetDataLine.class, 
        format); // format is an AudioFormat object
    if (!AudioSystem.isLineSupported(info)) 
    {
        System.out.println("line not supported:" + line );
    }

    try 
    {
        line = (TargetDataLine) AudioSystem.getLine(info);
        line.open(format);
        System.out.println("line opened:" + line);

        line.start();

        byte[] buffer = new byte[1024];
        int ii = 0;
        int numBytesRead = 0;
        while (ii++ < 100) {
       // Read the next chunk of data from the TargetDataLine.
            numBytesRead =  line.read(buffer, 0, buffer.length);

            System.out.println("\nnumBytesRead:" + numBytesRead);
               if (numBytesRead == 0) continue;
     // following is a quickie test to see if content is only 0 vals
    // present in the data that was read.

               for (int i = 0; i < 16; i++)
               {
                   if (buffer[i] != 0)
                       System.out.print(".");
                   else
                       System.out.print("0");
               }
            }

        } catch (LineUnavailableException ex) {
            ex.printStackTrace();
        //... 
    }
}

Но я просто хватаю строку, используя схему формата качества CD, я не пытался выяснить, какая строка имеет звук на воспроизводимом канале YouTube.


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