2016-08-09 110 views
13

tl; dr Für zukünftige Leser ist die Aufnahme von Audio in Echtzeit nicht möglich (für jetzt) ​​mit Java oder C#. Verwenden Sie C++, da es eine Fülle von Audio-APIs bietet.Java audio visualizer- So erfassen Sie Echtzeit-Soundausgabe zum Lautsprecher?


Mein Ziel ist der aktuelle Sound abgespielt auf einem Windows-Rechner zu bekommen, und analysiert den Klang wie ein Grafik-Audio-Visualizer ist (die Lautstärke Eigenschaft und Hz (Basis und Höhen bekommen)). Wenn ich aktuellen Sound sage, meine ich, wenn man einen Youtube-Video oder Spotify-Song spielt, und dieses Programm würde diesen Audio-Output lesen. Ich habe KEINE Absicht, Sound zu spielen, sondern ihn in Echtzeit zu erfassen und zu visualisieren.

In dem Versuch, dies zu tun, ich lese, wie zu build an audio waveform display und es berührt, wie eine Audiodatei in ein Array von Bytes (a Line) zu konvertieren. Das hilft nicht, weil es nicht den aktuellen Sound bekommt. Ich lese auch auf, wie zu capture audio als auch, und this java accessing sound tutorial, keiner von denen meine Frage beantworten, weil sie beide eine Song-Datei zum Laden benötigen.

Ich verstehe das überhaupt nicht. Ich bin total ahnungslos und jede Hilfe wäre willkommen.

Edit: Ich habe ein wenig mehr umgesehen, und die second answer from this source führen mich zu dem Schluss, dass: Ich konnte alle Audiogeräte finden, sehen, welche einen Ton produziert. Ich weiß nicht, was ich danach machen soll.

Edit 2 (noch einmal bearbeitet): Aus dem Experimentieren und herumschauen habe ich diesen Code unten geschrieben. Ich denke, das bringt mich in die Richtung, in die ich will, aber ich weiß nicht, wie ich es beenden soll.

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(); 
     } 

benutzte ich diese Quelle: Checking The Level of Audio-Playback in a mixers line, aber ich bin nicht auf der Suche für alle Linien zu überprüfen, die Lautstärke spielen, ich muss nur die Benutzer Standard-Mixer, dass die Linie erhalten, und in der Lage, die Daten zu analysieren .

Bearbeiten 3: Ich habe versucht:

//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(); 
    } 

Ich glaube, ich auf dem richtigen Weg bin über den Code verwenden, aber ich weiß nicht, wie man den Ton zu extrahieren und die bpm, Base zu finden, Treble usw.

Edit 4: Dies war eine interessante Lektüre: Real-time low latency audio processing in Java. Dies berührt nicht, welche Klassen und wie dies tatsächlich implementiert wird, aber es gibt einige Einblicke.

Edit 5: @AndrewThompson Mit diesem Stück Code von Ihrem Link ausgehend konnte ich über die verfügbaren Quell- und Ziellinien iterieren.

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(); 
     } 
    } 

Die Ausgabe sieht wie folgt aus:

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 

Ich habe dann ein Verfahren geschaffen, das die Klangpegel aller Linien, die wie folgt aussieht bekommt:

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(); 
     } 
    } 
} 

-in attemps um die aktuelle Zeile zu finden, die den Ton spielt und eine höhere Lautstärke anzeigt. Dies gibt:

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

keine Fortschritte.


Neuer Code:

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(); 
     } 
    } 
} 
+0

Ich bin mir nicht sicher, dass es immer möglich ist. * Einige * Computer haben einen "Stereo Mix" -Eingang, der wie ein Mikrofon wirkt, das den Ausgang aufzeichnet. – immibis

+0

Wenn Sie einige Computer sagen, könnten Sie das näher erläutern? (Wie in nur Windows Maschinen, Mac, etc) – Eric

+0

Ich habe es auf einigen Windows-Maschinen gesehen. – immibis

Antwort

4

Es ist ein gutes Beispiel aus den Java-Tutorials, die Ihnen die PCM-Daten aus einer Leitung extrahieren helfen. Im Tutorial mit dem Titel Using Files and Format Converters gibt es ein Codebeispiel unter der Rubrik "Sounddateien lesen". Der relevante Teil der „Schnipsel“ Beispiel ist, und wird durch den Code gekennzeichnet:

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

An diesem Punkt hve Sie Zugriff auf die einzelnen Bytes der Linie und kann sie in PCM nehmen und entsprechend der Montage Format der Sounddatei. Es gibt mehrere andere Stackoverflow-Fragen, die sich mit den Besonderheiten des Hinwegs von und nach Bytes zu PCM beschäftigen.


Füge einen Code als Antwort auf Kommentare hinzu.

Da ich nicht in TargetDataLine umwandeln konnte, erlaubte mir Folgendes, das aus dem Tutorial extrahiert wurde, eine Zeile nach TargetDataLine zu konvertieren.

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(); 
     //... 
    } 
} 

Aber ich bin nur eine Linie mit einem CD-Qualität Format Schema greifen, ich habe, um herauszufinden, nicht versucht, die Linie, die den Ton aus dem YouTube-Kanal hat, den abgespielt wird.

OP und ich gingen zum Chat und weiter zu hacken, aber waren nicht in der Lage, zu einer Lösung durchzuarbeiten. Es scheint, dass viele andere sich das angesehen und aufgegeben haben. Ich hoffe, dass das Kopfgeld verlockend ist - das ist ein interessantes Thema.

+0

Das Problem dabei ist "In beiden Fällen müssen Sie Zugriff auf die Daten in der Audiodatei erhalten". Ich möchte Echtzeit-Audio extrahieren, das von einer Leitung gesendet wird. Die Methoden in den Java-Lernprogrammen behandeln bereits vorhandene Sounddateien. – Eric

+0

Ich bin verdächtig auf die Methode .getLevel().Es gab Fälle, in denen der Versuch, etwas mit der Ebene einer aktiven Leitung zu tun, fehlschlug, so dass ich immer auf der Byte/DSP-Ebene arbeite, wie in meinem Beitrag beschrieben. Ich beschäftige mich hauptsächlich mit Live-Generierung über Synthesizer, also arbeite ich mit Echtzeit-Zeilen nicht existierende Dateien. Ich habe mit dem eingehenden Live-Sound nicht viel getan. In den Tutorials wird jedoch beschrieben, wie die Zeilen (und die Verwendung von TargetDataLine) zu identifizieren sind, und mit der genannten Routine kann geprüft werden, ob tatsächlich Daten fließen und nicht nur eine Folge von Nullen und in Echtzeit. –

+0

Nach dem Lesen der Tutorials ermöglicht TargetDataLine() die Aufnahme eingehender Audiodaten von den Aufnahme-Ports. Was ich nicht herausfinden kann ist, wie man einen Line-In-Port zuweist und eine TargetDataLine erstellt, die ein Port steuern kann. – Eric