2017-08-22 16 views
10

chỉnh sửa: Tôi hiện đang sử dụng Jack (Bộ kết nối âm thanh Jack). Xem câu trả lời dưới đây.Chọn dòng đầu ra trong java cho tám card âm thanh kênh

Tôi có một soundcard trên Raspberry Pi của tôi với 8 kênh đầu ra (bốn kênh âm thanh nổi), một thẻ Octosound. Những gì tôi muốn làm là chọn một trong các kênh để định tuyến âm thanh. Với mã này tôi in thông tin của card âm thanh:

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

Đây là sản phẩm tôi nhận được:

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 

Bây giờ đây là đoạn code tôi sử dụng để âm thanh đầu ra từ một tập tin 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); 

Tôi đã thử một số điều, nhưng trong mọi trường hợp, âm thanh xuất hiện trong tất cả các ouput.

+1

vì vậy bạn đã thử những gì? – andy

+1

Ngoài đoạn mã tôi đăng và nhiều biến thể, tôi hiện đang thử JnaJack với Jack. – Christine

Trả lời

3

Tôi đã tự tìm ra giải pháp. bây giờ tôi sử dụng Jack (Jack âm thanh Connection Kit, xem here. Đó là một chút rắc rối để có được Jack chạy trên một Pi mâm xôi. Có thông tin tốt here.

tôi sử dụng JnaJack với cung cấp một giao diện giữa Java và Jack.

Bạn không thể chạy Jack trên Raspbian ra khỏi hộp Debian Wheezy đã có một bản vá, nhưng Raspbian Jessie dường như không có điều đó.Vì vậy, bạn cần phải tạo một phiên bản của Jackd2 mà không sử dụng DBus. Here nó được giải thích làm thế nào để xây dựng Jackd2 mà không có DBus Có một snag: tất cả những gì bạn phải làm là loại bỏ hai dòng liên quan đến DBus. . dòng e bạn cần phải thay thế: sau khi bạn tải về các nguồn, trong debian/quy tắc thay đổi

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/* 

tôi sửa đổi ví dụ SimpleAudioClient tìm thấy trong nguồn 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(); 
    } 
} 

Tôi đổi SineAudioClient từ mã mẫu vào điều này:

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

để nó phát sóng sin trong hai giây trong mỗi tám kênh của card âm thanh. Tôi đã không thử các kênh đầu vào (chưa), tôi đọc rằng thật khó để có được Jack để làm việc trên Raspbian khi cả đầu vào và đầu ra được kích hoạt.

tôi bắt đầu Jack trước khi tôi chạy ứng dụng của tôi, lệnh bắt đầu là

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

Nhật ký khi bạn bắt đầu jack sẽ hiển thị

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

nơi "audioinjector" là tên của card âm thanh . Nếu nó hiển thị

...hw:audioinjectoroc|hw:audioinjectoroc|1024 ... 

thì bạn sẽ gặp sự cố khi kết nối với nó.

Bạn có thể xem cài đặt Jack với QJackCtl, bạn có thể chạy trên Raspberry Pi của mình và truy cập bằng máy chủ X từ máy tính khác. Tôi đã không quản lý để chạy X Windows trên Pi.

Nếu bạn muốn phát tệp wav qua Jack, this là ví dụ tốt về cách đọc tệp wav và nạp dữ liệu đó vào jack.

Chỉnh sửa: ví dụ là điểm bắt đầu tốt, nhưng bạn cần thực hiện một vài thay đổi. Tốt nhất là mở tất cả các cổng mà bạn dự định sử dụng, gọi client.activate() và trong Tuyến đường JackCallback, các kênh từ tệp âm thanh của bạn đến các kênh thích hợp trong thẻ âm thanh. Bạn có thể sử dụng qjackctl để xem điều gì đang xảy ra trong Jack.

Các vấn đề liên quan