2017-08-22 53 views
10

edit: Używam teraz zestawu Jack (Jack Audio Connection Kit). Zobacz odpowiedź poniżej.Wybierz linię wyjściową w java na ośmio kanałową kartę dźwiękową

Mam kartę dźwiękową na moim Raspberry Pi z 8 kanałami wyjściowymi (cztery kanały stereo), kartą Octosound. Chcę tylko wybrać jeden z kanałów, do których ma kierować dźwięk. Z tym kodem wydrukować informacje o karcie dźwiękowej:

mixers = AudioSystem.getMixerInfo(); 
    for (Mixer.Info mixerInfo : mixers) { 
     logger.debug("\n"); 
     logger.debug("Found Mixer: " + mixerInfo); 

     Mixer m = AudioSystem.getMixer(mixerInfo); 

     Line.Info[] sourceLines = m.getSourceLineInfo(); 
     for (Line.Info li : sourceLines) { 
      logger.debug("Found source line: " + li + " " + li.getClass()); 

      if (li instanceof Port.Info) { 
       Port.Info portInfo = (Port.Info) li; 
       logger.debug("port found " + portInfo.getName() + " is source " + portInfo.isSource()); 
       sourceDataLines.add(portInfo); 
      } 

     } 

     Line.Info[] targetLines = m.getTargetLineInfo(); 

     for (Line.Info li : targetLines) { 
      logger.debug("Found target line: " + li + " " + li.getClass()); 
      outputLines.add(li); 

      if (li instanceof Port.Info) { 
       Port.Info portInfo = (Port.Info) li; 
       logger.debug("port found " + portInfo.getName() + " is source " + portInfo.isSource()); 
       outputPorts.add(portInfo); 
      } 
     } 
    } 


private void lineClose(int soundPort) throws LineUnavailableException { 
    Port.Info lineInfo = outputPorts.get(soundPort); 
    Line line = (Port) AudioSystem.getLine(lineInfo); 
    line.close(); 
} 

private void lineOpen(int l) throws LineUnavailableException { 

    for (int i = 0; i < outputPorts.size(); i++) { 
     Port.Info lineInfo = outputPorts.get(i); 
     Line line = (Port) AudioSystem.getLine(lineInfo); 
     if (l == i) { 
      line.open(); 
     } else { 
      line.close(); 
     } 
    } 
} 

To wyjście uzyskać:

Found Mixer: audioinjectoroc [default], version 4.9.41-v7+ 
Found source line: interface SourceDataLine supporting 84 audio formats, and buffers of at least 32 bytes class com.sun.media.sound.DirectAudioDevice$DirectDLI 
Found source line: interface Clip supporting 84 audio formats, and buffers of at least 32 bytes class com.sun.media.sound.DirectAudioDevice$DirectDLI 
Found target line: interface TargetDataLine supporting 84 audio formats, and buffers of at least 32 bytes class com.sun.media.sound.DirectAudioDevice$DirectDLI 

Found Mixer: audioinjectoroc [plughw:0,0], version 4.9.41-v7+ 
Found source line: interface SourceDataLine supporting 96 audio formats, and buffers of at least 32 bytes class com.sun.media.sound.DirectAudioDevice$DirectDLI 
Found source line: interface Clip supporting 96 audio formats, and buffers of at least 32 bytes class com.sun.media.sound.DirectAudioDevice$DirectDLI 
Found target line: interface TargetDataLine supporting 96 audio formats, and buffers of at least 32 bytes class com.sun.media.sound.DirectAudioDevice$DirectDLI 

Found Mixer: Port audioinjectoroc [hw:0], version 4.9.41-v7+ 
Found source line: ADC1 source port class com.sun.media.sound.PortMixer$PortInfo 
port found ADC1 is source true 
Found source line: ADC2 source port class com.sun.media.sound.PortMixer$PortInfo 
port found ADC2 is source true 
Found source line: ADC3 source port class com.sun.media.sound.PortMixer$PortInfo 
port found ADC3 is source true 
Found target line: DAC1 target port class com.sun.media.sound.PortMixer$PortInfo 
port found DAC1 is source false 
Found target line: DAC2 target port class com.sun.media.sound.PortMixer$PortInfo 
port found DAC2 is source false 
Found target line: DAC3 target port class com.sun.media.sound.PortMixer$PortInfo 
port found DAC3 is source false 
Found target line: DAC4 target port class com.sun.media.sound.PortMixer$PortInfo 
port found DAC4 is source false 

Teraz jest to kod używać do dźwięku wyjściowego z pliku wav:

String path = soundDirectory + soundUrl; 
    InputStream is = new FileInputStream(path); 
    BufferedInputStream bis = new BufferedInputStream(is); 
    AudioInputStream inputStream = AudioSystem.getAudioInputStream(bis); 
    AudioFormat format = inputStream.getFormat(); 

    Mixer.Info mi = mixers[0]; 

    SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getSourceDataLine(format,mi); 
    sourceDataLine.open(format); 
    sourceDataLine.start(); 
    byte[] buf = new byte[1024]; 
    int bytesRead; 
    while ((bytesRead = inputStream.read(buf)) != -1){ 
     sourceDataLine.write(buf, 0, bytesRead); 
    } 
    inputStream.close(); 

    sourceDataLine.drain(); 
    sourceDataLine.stop(); 
    sourceDataLine.close(); 

    lineClose(soundPort); 

Próbowałem wielu rzeczy, ale we wszystkich przypadkach dźwięk pochodzi ze wszystkich rozwiązań.

+1

więc czego próbowałeś? – andy

+1

Oprócz kodu, który napisałem, i wielu jego odmian, obecnie próbuję JnaJack z Jackiem. – Christine

Odpowiedz

3

Sam znalazłem rozwiązanie. Teraz używam Jack (Jack Kit połączenie audio, patrz here. To trochę kłopotów dostać Jack działa na Raspberry Pi. Nie ma dobrych informacji here.

używam JnaJack z zapewnia interfejs pomiędzy Java i Jacka.

Nie można uruchomić Jacka na Raspbian po wyjęciu z pudełka Debian Wheezy miał łatkę, ale Raspbian Jessie nie wydaje się tego mieć, więc musisz stworzyć wersję Jackd2, która nie używa DBus. Here Wyjaśniono, jak budować Jackd2 bez DBus.Nie ma szkopuł: wszystko, co musisz zrobić, to usunąć dwa wiersze, które odnoszą się do DBus.Co wszystko, co każą ci łatać zostało domyślnie poprawione w Raspbian, lub tak się wydaje Thes Linie e trzeba wymienić: po pobraniu źródeł, w debian/rules zmienić

waf-configure-options += $(if $(filter linux,$(DEB_HOST_ARCH_OS)),--alsa --dbus) 
became 
waf-configure-options += $(if $(filter linux,$(DEB_HOST_ARCH_OS)),--alsa) 

dh_install -pjackd2 debian/tmp/usr/share/dbus-1/* 
became 
#dh_install -pjackd2 debian/tmp/usr/share/dbus-1/* 

zmodyfikowałem przykład SimpleAudioClient znaleźć w źródle JnaJack:

public class SimpleAudioClient { 

    private boolean autoconnect = true; 
    private JackClient client; 
    private Processor processor; 
    private Callback callback; 
    private ShutDownHook shutDownHook; 
    private JackPort[] inputPorts; 
    private JackPort[] outputPorts; 
    private FloatBuffer[] inputBuffers; 
    private FloatBuffer[] outputBuffers; 
    private float samplerate; 
    private int buffersize; 
    private volatile boolean active; 

    private Logger logger = Logger.getLogger(getClass().getSimpleName()); 
    private int channelNumber = 0; 

    public SimpleAudioClient() throws JackException { 

     Jack jack = Jack.getInstance(); 
     logger.debug("Jack instance " + jack.toString()); 
     EnumSet<JackOptions> options = EnumSet.of(JackOptions.JackNoStartServer); 
     EnumSet<JackStatus> status = EnumSet.noneOf(JackStatus.class); 
     try { 
      client = jack.openClient("jna_jack", options, status); 
     } catch (JackException ex) { 
      System.out.println("ERROR : Status : " + status); 
      throw ex; 
     } 

     String[] inputs = new String[0]; 
     inputPorts = new JackPort[inputs.length]; 
     EnumSet<JackPortFlags> flags = EnumSet.of(JackPortFlags.JackPortIsInput); 
     for (int i = 0; i < inputs.length; i++) { 
      //inputPorts[i] = client.registerPort(inputs[i], JackPortType.AUDIO, flags); 
     } 

     String[] outputs = new String[]{"playback_1", "playback_2", "playback_3", "playback_4", "playback_5", "playback_6", "playback_7", "playback_8"}; 
     outputPorts = new JackPort[outputs.length]; 
     flags = EnumSet.of(JackPortFlags.JackPortIsOutput); 
     for (int i = 0; i < outputs.length; i++) { 
      outputPorts[i] = client.registerPort(outputs[i], JackPortType.AUDIO, flags); 
     } 

     processor = new SineAudioSource(); 

     this.inputBuffers = new FloatBuffer[inputPorts.length]; 
     this.outputBuffers = new FloatBuffer[outputPorts.length]; 
     this.callback = new Callback(); 
     this.shutDownHook = new ShutDownHook(); 
     client.onShutdown(shutDownHook); 

     for (JackPort port : inputPorts) { 
      logger.debug("input port " + port.getType() + " " + port.getName()); 
     } 

     for (JackPort port : outputPorts) { 
      logger.debug("output port " + port.getType() + " " + port.getName()); 
     } 
    } 

    public void activate(int channelNr) throws JackException { 

     this.channelNumber = channelNr; 

     try { 
      samplerate = client.getSampleRate(); 
      System.out.println("Sample rate = " + samplerate); 
      buffersize = client.getBufferSize(); 
      System.out.println("Buffersize = " + buffersize); 
      processor.setup(samplerate, buffersize); 
      active = true; 
      client.setProcessCallback(callback); 
      client.activate(); 
      if (autoconnect) { 
       doAutoconnect(); 
      } 
     } catch (Exception ex) { 
      active = false; 
      throw new JackException("Could not activate Jack client"); 
     } 
    } 

    private void doAutoconnect() throws JackException { 
     Jack jack = Jack.getInstance(); 
     String[] physical = jack.getPorts(client, null, JackPortType.AUDIO, 
       EnumSet.of(JackPortFlags.JackPortIsInput, JackPortFlags.JackPortIsPhysical)); 
     int count = Math.min(outputPorts.length, physical.length); 
     for (int i = 0; i < count; i++) { 
      logger.debug("output port " + outputPorts[i].getName()); 
      jack.connect(client, outputPorts[i].getName(), physical[i]); 
     } 
     physical = jack.getPorts(client, null, JackPortType.AUDIO, 
       EnumSet.of(JackPortFlags.JackPortIsOutput, JackPortFlags.JackPortIsPhysical)); 
     count = Math.min(inputPorts.length, physical.length); 
     for (int i = 0; i < count; i++) { 
      logger.debug("input port " + inputPorts[i].getName()); 
      //jack.connect(client, physical[i], inputPorts[i].getName()); 
     } 
    } 

    public void shutdown() { 
     active = false; 
     client.deactivate(); 
     client.close(); 
    } 

    private void processBuffers(int nframes) { 
     for (int i = 0; i < inputPorts.length; i++) { 
      inputBuffers[i] = inputPorts[i].getFloatBuffer(); 
     } 
     for (int i = 0; i < outputPorts.length; i++) { 
      outputBuffers[i] = outputPorts[i].getFloatBuffer(); 
     } 
     processor.process(channelNumber, inputBuffers, outputBuffers); 
    } 

    private class Callback implements JackProcessCallback { 

     public boolean process(JackClient client,final int nframes) { 

      if (!active) { 
       return false; 
      } else { 
       try { 
        processBuffers(nframes); 
        return true; 
       } catch (Exception ex) { 
        System.out.println("ERROR : " + ex); 
        active = false; 
        return false; 
       } 

      } 
     } 
    } 

    private class ShutDownHook implements JackShutdownCallback { 

     public void clientShutdown(JackClient client) { 
      active = false; 
      processor.shutdown(); 
     } 
    } 

    public static interface Processor { 

     public void setup(float samplerate, int buffersize); 

     public void process(int channelNumber, FloatBuffer[] inputs, FloatBuffer[] outputs); 

     public void shutdown(); 
    } 

    /** 
    * Create a SimpleAudioClient. 
    * 
    * @return client 
    * @throws org.jaudiolibs.jnajack.JackException 
    */ 
    public static SimpleAudioClient create(
    ) throws JackException { 

     return new SimpleAudioClient(); 
    } 
} 

zmodyfikowałem SineAudioClient od przykładowy kod do tego:

public class SineAudioSource implements SimpleAudioClient.Processor { 

    private final static int TABLE_SIZE = 200; 
    private int left_phase = 0; 
    private int right_phase = 0; 
    private float[] data; 

    public void setup(float samplerate, int buffersize) { 
     data = new float[TABLE_SIZE]; 
     for (int i = 0; i < TABLE_SIZE; i++) { 
      data[i] = (float) (0.2 * Math.sin(((double) i/(double) TABLE_SIZE) * Math.PI * 2.0)); 
     } 
    } 

    public void process(int channelNumber, FloatBuffer[] inputs, FloatBuffer[] outputs) { 

     FloatBuffer left = outputs[channelNumber]; 
     int size = left.capacity(); 
     for (int i = 0; i < size; i++) { 
      left.put(i, data[left_phase]); 
      left_phase += 2; 
      right_phase += 3; 
      if (left_phase >= TABLE_SIZE) { 
       left_phase -= TABLE_SIZE; 
      } 
     } 
    } 

    public void shutdown() { 
     System.out.println("Sine Audio Source shutdown"); 
    } 
} 

, więc odtwarza falę sinusoidalną przez dwie sekundy w każdym z ośmiu kanałów karty dźwiękowej. Nie próbowałem kanałów wejściowych (jeszcze), czytałem, że trudno jest Jackowi pracować nad Raspbian, gdy aktywowane jest zarówno wejście, jak i wyjście.

zacznę Jack zanim uruchomię moją aplikację, komenda start jest

/usr/bin/jackd -dalsa -dhw:audioinjectoroc -r48000 -p1024 -n2 -P & 

Log po uruchomieniu jack powinien pokazać

creating alsa driver ... hw:audioinjectoroc|-|1024|2|48000|0|0|nomon|swmeter|-|32bit 

gdzie "audioinjector" to nazwa karty dźwiękowej . Jeśli wyświetli się

...hw:audioinjectoroc|hw:audioinjectoroc|1024 ... 
, wtedy będziesz mieć problemy z łączeniem się z tym.

Możesz zobaczyć ustawienia Jacka z QJackCtl, które możesz uruchomić na swoim Raspberry Pi i uzyskać dostęp do serwera X z innego komputera. Nie udało mi się uruchomić X Windows na Pi.

Jeśli chcesz odtwarzać pliki WAV przez Jacka, this jest dobrym przykładem, jak odczytać plik WAV i przekazać go do gniazda.

Edytuj: przykład to dobry punkt wyjścia, ale musisz wprowadzić kilka zmian. Najlepiej otworzyć wszystkie porty, z których zamierzasz korzystać, zadzwoń pod numer client.activate(), a następnie w JackCallback przekaż kanały z pliku audio do odpowiednich kanałów na karcie dźwiękowej. Możesz użyć numeru qjackctl, aby zobaczyć, co dzieje się w Jacku.