问题描述
编辑:我现在正在使用Jack(Jack Audio Connection Kit)。请参阅下面的答案。
edit: I am now using Jack (Jack Audio Connection Kit). See answer below.
我的Raspberry Pi上有一个声卡,有8个输出声道(4个立体声声道),一张Octosound卡。我想要做的是选择一个通道来路由声音。
使用此代码我打印声卡的信息:
I have a soundcard on my Raspberry Pi with 8 output channels (four stereo channels), an Octosound card. What I want to do is select one of the channels to route sound to.With this code I print info of the sound card:
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文件输出声音的代码:
Now this is the code I use to output sound from a wav file:
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);
我尝试了很多东西,但在所有情况下,声音来自所有输出。
I have tried a number of things, but in all cases, sound comes out of all ouputs.
推荐答案
我自己找到了解决方案。我现在使用Jack(Jack音频连接套件,请参见。
这有点麻烦让杰克在Raspberry Pi上运行。有很好的信息。
I found the solution myself. I now use Jack (Jack Audio Connection Kit, see here.It's a bit of a hassle to get Jack running on a Raspberry Pi. There's good info here.
我使用,提供Java和Jack之间的接口。
I use JnaJack with provides an interface between Java and Jack.
你不能在Raspbian上运行Jack.Dcian Wheezy有一个补丁,但Raspbian Jessie似乎没有。所以你需要创建一个不使用DBus的Jackd2版本。它解释了如何在没有DBus的情况下构建Jackd2。有一个障碍:你所要做的就是删除引用DBus的两行。他们告诉你补丁的其他所有内容现在已被修补在Raspbian中默认情况下,或者看起来如此。这些行需要替换:
下载源后,在debian / rules更改
You can't run Jack on Raspbian out of the box. Debian Wheezy had a patch, but Raspbian Jessie doesn't seem to have that. So you need to create a version of Jackd2 that doesn't use DBus. Here it's explained how to build Jackd2 without DBus. There's a snag: all you have to do is remove the two lines that refer to DBus. Everything else that they tell you to patch has now been patched in Raspbian by default, or so it seems. These lines you need to replace:after you downloaded the source, in debian/rules change
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示例:
I modified the SimpleAudioClient example found in the JnaJack source:
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修改为:
I modified the SineAudioClient from the sample code into this:
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上工作。
so it plays a sine wave for two seconds in each of the eight channels of the sound card. I haven't tried input channels (yet), I read that it's hard to get Jack to work on Raspbian when both input and output is activated.
我之前启动了Jack我运行我的应用程序,启动命令是
I start Jack before I run my app, the start command is
/usr/bin/jackd -dalsa -dhw:audioinjectoroc -r48000 -p1024 -n2 -P &
启动杰克时的日志应显示
The log when you start jack should show
creating alsa driver ... hw:audioinjectoroc|-|1024|2|48000|0|0|nomon|swmeter|-|32bit
其中audioinjector是声卡的名称。如果显示
where "audioinjector" is the name of the sound card. If it shows
...hw:audioinjectoroc|hw:audioinjectoroc|1024 ...
然后你就会遇到连接它的问题。
then you'll have issues connecting to it.
您可以使用QJackCtl查看Jack设置,您可以在Raspberry Pi上运行该设置,并从另一台计算机访问X服务器。我没有设法在Pi上运行X Windows。
You can view Jack settings with QJackCtl, which you can run on your Raspberry Pi and access with an X server from another computer. I didn't manage to run X Windows on the Pi.
如果你想通过Jack播放wav文件,这是一个很好的例子,说明如何读取wav文件并将其提供给jack。
If you want to play wav files via Jack, this is a good example of how to read a wav file and feed it to jack.
编辑:示例是一个很好的起点,但您需要进行一些更改。最好打开你打算使用的所有端口,调用 client.activate()
,并在JackCallback路由中将音频文件中的通道路由到声音中的相应通道卡。您可以使用 qjackctl
查看Jack中发生的事情。
the example is a good starting point, but you need to make a few changes. It's best to open all ports that you plan to use, call client.activate()
, and in the JackCallback route the channels from your audio file to the appropriate channels in the sound card. You can use qjackctl
to see what's happening in Jack.
这篇关于在java中为八声道声卡选择输出行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!