Taking control of the soundCard with JAVA

不打扰是莪最后的温柔 提交于 2019-12-08 09:48:36

问题


I'm trying to build a "mixing console" in JAVA. In this mixer, each "slider" would be a line, as if it was a physical mixing console. But, after my search on Goo*** and read the oracle documentation, I didn't really find anything to help me.

First, I list the mixers available (see code at the end) and happily, it list all my soundcards and find the line in and line out. But then, what I have to do ?

My aim is to create "virtual line" that will catch the sound from any line in (microphone) or line out (capture of the sound played in VLC), allowing me to "work on it" and then, reinject it in another line. Of course, I will have to set for each line which is the "in" and which is the "out" Examples of use :

  • 3 physical lines in (microphones) would be catch, then "level adjusted" then reinjected in a virtual line that would be called "chorus" and this virtual line would be reinjected in my main physical line out called "Master level".

  • the song played by VLC (the melody of a song for example), which is usually directly send to the main "out, is catched, like an "interception". Then I decide to setup the level before reinject it in my Master and mix it with the microphone. So, it will allow me to adjust the singer level with the music level.

So, who could help me to start and progress ? Thank you for all your help.

Here is the code that list my mixers :

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Control;
import javax.sound.sampled.Line;
import javax.sound.sampled.Mixer;

public class Mixers {

Mixers() {      
    try {
        //List of the mixers from the system
        Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
        //Characteristics of each mixer
        for (Mixer.Info info : mixerInfo) {
            System.out.println("\n"+info+ " - Description : "+info.getDescription());
            Mixer mix=AudioSystem.getMixer(info);

            //Controles available for this mixer
            for (Control c:mix.getControls()) {
                System.out.println ("\t Control supported by this mixer : "+c);
            }
            //Sources available for this mixer
            for (Line.Info i : mix.getSourceLineInfo()) {
                System.out.println("\t Source line available : "+i);

                //Controls available for this Source
                for (Control c : AudioSystem.getLine(i).getControls()) {
                    System.out.println ("\t\t ==> Controls supported by this source : "+c);
                }
            }
            // TargeLines available for this mixer
            for (Line.Info i : mix.getTargetLineInfo()) {
                System.out.println("\t Target line available : "+i);

                //Controls available for this Target
                for (Control c : AudioSystem.getLine(i).getControls()) {
                    System.out.println ("\t\t Controls supported by this target : "+c);
                }
            }               
        }
    } catch (Exception e) {
        e.printStackTrace();
    }               
}

}

Thanks again to anyone who read my post until the end and give me the start of a solution.

Nicolas


回答1:


If this can be done, I think the next step that you are wanting is to be able to read data from the various mixers and perform operations on a "per frame" basis. The Java Tutorial Capturing Audio refers to reading data from outside lines. The tutorial Using Files and Format Converters has example code that shows how to get to the individual frames. Under the header "Reading Sound Files" is example code for this. The most important point is marked by the following comment:

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

In your case, you would have multiple auiodBytes arrays, one for each line you are inputting.

To mix, the PCM frame data from the different lines can be added together. Volume control between the lines can be done via using a multiplier, e.g., a factor that ranges from 0 (silence) to 1 (full volume).

Care must be taken not to change the multiplier by too much in a single frame, else the discontinuity introduced into the data causes a click. So spread any changes out over multiple frames. Linear changes in the multiplier do not exactly match heard audio levels. I think a 3rd degree exponential distribution is often used for converting between linear values and equivalent multipliers that actually sound like those values. You might want to put in min and max functions to ensure the PCM data never goes outside of the range -1 and 1. Much of this paragraph is about fine points of mixing and subject to differing opinions as to what are the best practices.

There are other posts on stackoverflow that tell how to convert from your byte-stream format to PCM and back. As far as I know, javax.sound.sampled, the source for this code, relies very much on native code and is quite low level. I've been able to do a lot with mixing in this manner, though I rely on my own data sources, not outside lines as you are trying to do. I don't know how those outside lines are managed, especially if you wish to have your computer not play them while you are playing your resulting mix.




回答2:


I am not sure that java sound API can do that. You should probably look at a much lower API , like for example OpenAL (Java has bindings on this) , which provides more control over sound streams



来源:https://stackoverflow.com/questions/34379912/taking-control-of-the-soundcard-with-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!