在java中为八通道声卡选择输出线

2024-03-30

编辑:我现在使用杰克(杰克音频连接套件)。请参阅下面的答案。

我的 Raspberry Pi 上有一个带有 8 个输出通道(四个立体声通道)的声卡,即 Octosound 卡。我想要做的是选择将声音路由到的通道之一。 使用此代码我打印声卡的信息:

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

这是我得到的输出:

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

现在这是我用来从 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);

我尝试了很多方法,但在所有情况下,所有输出都会发出声音。


我自己找到了解决方案。我现在使用 Jack(Jack 音频连接套件,请参阅here http://jackaudio.org/faq/。 让 Jack 在 Raspberry Pi 上运行有点麻烦。有好资料here https://wiki.linuxaudio.org/wiki/raspberrypi.

I use JnaJack https://github.com/jaudiolibs/jnajackwith 提供了 Java 和 Jack 之间的接口。

您无法在 Raspbian 上开箱即用地运行 Jack。 Debian Wheezy 有补丁,但 Raspbian Jessie 似乎没有。因此,您需要创建一个不使用 DBus 的 Jackd2 版本。解释了如何在没有 DBus 的情况下构建 Jackd2。有一个障碍:您所要做的就是删除引用 DBus 的两行。他们告诉你要修补的其他所有内容现在都已默认在 Raspbian 中修补,或者看起来是这样。您需要替换这些行: 下载源代码后,在 debian/rules 中进行更改

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

我修改了 JnaJack 源代码中的 SimpleAudioClient 示例:

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

我将示例代码中的 SineAudioClient 修改为:

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

因此它在声卡的八个通道中的每个通道中播放正弦波两秒钟。我还没有尝试过输入通道,我读到当输入和输出都被激活时,很难让 Jack 在 Raspbian 上工作。

我在运行我的应用程序之前启动 Jack,启动命令是

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

启动 jack 时的日志应该显示

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

其中“audioinjector”是声卡的名称。如果显示

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

那么你在连接它时就会遇到问题。

您可以使用 QJackCtl 查看 Jack 设置,您可以在 Raspberry Pi 上运行该设置,并通过另一台计算机上的 X 服务器进行访问。我没能在 Pi 上运行 X Windows。

如果你想通过Jack播放wav文件,this http://www.labbookpages.co.uk/audio/javaWavFiles.html是一个很好的例子,展示了如何读取 wav 文件并将其提供给 jack。

编辑:该示例是一个很好的起点,但您需要进行一些更改。最好打开您打算使用的所有端口,调用client.activate(),并在 JackCallback 中将音频文件中的通道路由到声卡中的相应通道。您可以使用qjackctl看看杰克发生了什么。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在java中为八通道声卡选择输出线 的相关文章

  • Google 地图查询返回的 JSON 包含像 \x26 这样的编码字符(如何解码?)

    在 Java 应用程序中 我获取 JSON 来自 Google 地图 其中包含以下字符 x26我想将其转换为其原始字符 据我所知 这是一个 UTF 8 表示法 但我不完全确定 在源 JSON 中 可能会出现各种编码字符 例如 x3c div
  • MP3:一种以毫秒为单位获取任何给定字节位置的位置的方法?

    我创建了一个 servlet 它返回从客户端请求的任何给定字节位置开始的流 来自 MP3 文件 这允许客户端在任何给定字节位置立即开始播放 而无需进行任何本地查找 现在 我有一个滑块可以直观地显示进度 我正在使用当前字节位置来更新滑块 但是
  • Java 创建浮雕(红/蓝图像)

    我正在编写一个 Java 游戏引擎 http victoryengine org http victoryengine org 并且我一直在尝试生成具有深度的 3D 图像 您可以使用那些红色 蓝色眼镜看到 我正在使用 Java2D 进行图形
  • 无法加载 jar 文件的主类

    我使用 Eclipse IDE 开发了一个应用程序 创建应用程序后 我以 jar 格式导出项目 当我尝试运行此 jar 文件时 出现错误 无法加载主类 请帮忙 当您将项目导出为 jar 时 请参阅此所以问题 https stackoverf
  • 有没有好的方法来解析用户代理字符串?

    我有一个Java接收模块User Agent来自最终用户浏览器的字符串的行为需要略有不同 具体取决于浏览器类型 浏览器版本甚至操作系统 例如 FireFox 7 0 Win7 Safari 3 2 iOS9 我明白了User Agent由于
  • 如何对 IntStream 进行逆序排序

    我正在使用 txt 文件读取数字BufferedReader 我想颠倒该流中元素的顺序 以便在收集它们时 它们将从最高到最低排列 我不想在构建数组后进行排序 因为我不知道其中可能有多少元素 我只需要最高的 N 个元素 in new Buff
  • Java 重写 hashCode() 得到 StackOverflowError

    所以我不太熟悉重写 hashCode 并且我似乎在 hashCode 方法中以某种方式进行了一些无限递归 这是我的场景 我有一个 DuplicateCache 类 它是一个缓存对象 用于检查系统中的重复对象 我有一个静态内部类 Duplic
  • Android - 除了普通 SSL 证书之外还验证自签名证书

    我有一个通过 SSL 调用 Web 服务的 Android 应用程序 在生产中 我们将拥有由受信任的 CA 签名的普通 SSL 证书 但是 我们需要能够支持自签名证书 由我们自己的 CA 签名 我已经成功实施了接受自签名证书的建议解决方案
  • 如何将 Spotlight for Help 插入本地化的 macOS 应用程序?

    我正在 macOS 上使用 Swing GUI 框架实现 Java 应用程序 当使用system外观和感觉以及screen菜单栏 Swing 自动插入一个搜索栏 called 聚光灯寻求帮助 https developer apple co
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • 从 html 页面和 javascript 调用 java webservice

    我正在尝试从 javascript 调用 java 实现的 Web 服务 使用 NetBeans IDE 我读过很多关于 jQuery 和 AJAX 的内容 但我似乎无法掌握它 假设我的 Web 服务 WSDL 位于 http localh
  • jmap - 组织和堆操作会给 jvm 带来开销吗?

    正如标题所述 需要多少开销jmap histo and jmap heap分别带到jvm 如果一个内存敏感的 Java 进程处于OutOfMemory 例如 大约 96 的堆已满 并且无法通过 full gc 清除 其中一项操作是否有可能将
  • 在 Selenium WebDriver 上如何从 Span 标签获取文本

    在 Selenium Webdriver 上 如何从 span 标记检索文本并打印 我需要提取文本UPS Overnight Free HTML代码如下 div id customSelect 3 class select wrapper
  • 使用 Java 从 S3 上的文件在 S3 上创建 zip 文件

    我在 S3 上有很多文件 需要对其进行压缩 然后通过 S3 提供压缩文件 目前 我将它们从流压缩到本地文件 然后再次上传该文件 这会占用大量磁盘空间 因为每个文件大约有 3 10MB 而且我必须压缩多达 100 000 个文件 所以一个 z
  • 重写Object类的finalize()方法有什么用?

    据我所知 在java中如果我们想手动调用垃圾收集器 我们可以执行System gc 1 我们在重写的finalize 方法中做了哪些操作 2 如果我们想手动调用JVM垃圾收集器 是否需要重写finalize 方法 我们在重写的 Finali
  • 从一个文本文件中获取数据并将其移动到新的文本文件

    我有一个文件 里面有数据 在我的主要方法中 我读入文件并关闭文件 我调用另一种方法 在原始文件的同一文件夹内创建一个新文件 所以现在我有两个文件 原始文件和通过我调用的方法生成的文件 我需要另一种方法 从原始文件中获取数据并将其写入创建的新
  • Java 编码风格、局部变量与重复方法调用

    我更喜欢使用局部变量而不是多次调用同一方法 I prefer this Vehicle vehicle person getVehicle if vehicle instanceof Car Car car Car vehicle car
  • Java 推断泛型类型

    我正在寻找类似的推断捕获泛型类型的概念 类似于以下方法片段 但不是捕获泛型类型的类 public
  • 使用 eclipse IDE 配置 angularjs

    我想开始使用 AngularJs 和 Java Spring 进行开发 我使用 Eclipse 作为 IDE 我想配置我的 Eclipse 以使这些框架无缝工作 我知道我可能要求太多 但相信我 我已经做了很多研究 你们是我最后的选择 任何帮
  • 尝试使用带有有效购买令牌的 Java Google Play Developer API v3 检索应用内购买信息时出现错误请求(无效值)

    当使用 Java Google Play Developer API 版本 3 并请求有效购买令牌的购买信息时 我收到以下异常 API 调用返回 400 Bad Request 响应以及以下消息 code 400 errors domain

随机推荐