java - 在Java中同时播放创建的Midi声音

标签 java audio midi javasound javax.sound.midi

我有一个问题,希望在这里得到答案。我想创建许多Midi声音而不将其保存在客户端PC上,因为这应该在Applet中起作用。创建声音部分不是问题,问题是同时播放这些声音。我现在搜索了3天以上的提升服务,但找不到可以帮助我的提升服务。

这是我目前所拥有的:

我的声音(在一个开放项目中找到了它的一部分):

import java.io.*;
import javax.sound.sampled.*;
import java.net.URL;
import javax.sound.*;

public class Sound {

    /**
     *  The sample rate - 44,100 Hz for CD quality audio.
     */
    public static int channels = 2;




    public static final int SAMPLE_RATE = 44100;

    private static final int BYTES_PER_SAMPLE = 4;                // 16-bit audio
    private static final int BITS_PER_SAMPLE = 16;                // 16-bit audio
    private static final double MAX_16_BIT = Short.MAX_VALUE;     // 32,767
    private static final int SAMPLE_BUFFER_SIZE = 4096;


    static SourceDataLine[] line = new SourceDataLine[channels];    // to play the         sound
    private static byte[][] buffer = new byte[2][];         // our internal buffer
    private static int i = 0;             // number of samples currently in internal buffer
    private static int i2 = 0;

    public static Mixer mainmix;

    // static initializer
   public Sound()
   {
       init();
   }

    // open up an audio stram
    static void init() {
        try {

            Mixer.Info[] mixerinfo = AudioSystem.getMixerInfo();    

            mainmix = AudioSystem.getMixer(mixerinfo[0]);

            Line.Info lineinf[] = mainmix.getSourceLineInfo();

            // 44,100 samples per second, 16-bit audio, mono, signed PCM, little Endian
            AudioFormat format = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, 2, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);


            line[0] = (SourceDataLine) mainmix.getLine(info);
            line[1] = (SourceDataLine) mainmix.getLine(info);
            //line[2] = (SourceDataLine) mainmix.getLine(info);

            Control[] linectrl = line[0].getControls();
            Control[] linectrl1 = line[1].getControls();



            //line = (SourceDataLine) AudioSystem.getLine(info);
            line[0].open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);
            line[1].open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);
           // line[2].open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);
            //line[1].open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);



            // the internal buffer is a fraction of the actual buffer size, this choice is arbitrary
            // it gets diveded because we can't expect the buffered data to line up exactly with when
            // the sound card decides to push out its samples.
            buffer[0] = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE];
            buffer[1] = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE];
            //buffer[2] = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE];
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.exit(1);
    }


        // no sound gets made before this call
        line[0].flush();
        line[0].start();
        line[1].flush();
        line[1].start();
}

    /**
     * Close standard audio.
     */
    public final void close() {
        line[0].drain();
        line[0].stop();

       line[1].drain();
       line[1].stop();        
    }

    public final void play(double in) {

        // clip if outside [-1, +1]
        if (in < -1.0) in = -1.0;
        if (in > +1.0) in = +1.0;

        // convert to bytes
        short s = (short) (MAX_16_BIT * in);
        buffer[0][i++] = (byte) s;
        buffer[0][i++] = (byte) (s >> 8);   // little Endian
        buffer[0][i++] = (byte) s;
        buffer[0][i++] = (byte) (s >> 8);   // little Endian


        // send to sound card if buffer is full        
        if (i >= buffer.length) {
            line[0].write(buffer[0], 0, 4);

            i = 0;
        }
    }

    public void play(double[] input) {
        for (int i = 0; i < input.length; i++) {
            play(input[i]);

        }
    }

    public final void play(double in, double an) {


        // clip if outside [-1, +1]
        if (in < -1.0) in = -1.0;
        if (in > +1.0) in = +1.0;

        // convert to bytes
        short s = (short) (MAX_16_BIT * in);
        buffer[1][i++] = (byte) s;
        buffer[1][i++] = (byte) (s >> 8);   // little Endian
        buffer[1][i++] = (byte) s;
        buffer[1][i++] = (byte) (s >> 8);   // little Endian

        // clip if outside [-1, +1]
        if (an < -1.0) an = -1.0;
        if (an > +1.0) an = +1.0;

        // convert to bytes
        short a = (short) (MAX_16_BIT * an);
        buffer[0][i2++] = (byte) a;
        buffer[0][i2++] = (byte) (a >> 8);   // little Endian
        buffer[0][i2++] = (byte) a;
        buffer[0][i2++] = (byte) (a >> 8);   // little Endian

        // send to sound card if buffer is full        
        if (i >= buffer.length) {

            line[1].write(buffer[1], 0, 4);
            line[0].write(buffer[0], 0, 4);


            i2 = 0;
            i = 0;
        }
    }

 public void play(double[] input,double[] input1) {
        for (int i = 0; i < input.length; i++) {
            play(input[i],input1[i]);

        }
    }
}

这是我创建声音的地方:
    public class Note {
    private final int SAMPLE_RATE = 44100;
    private double[] tone;
    public Note(double hz, double duration, double amplitude)
    {
        int N = (int) Math.round(SAMPLE_RATE * duration);
        tone = new double[N+1];
        for (int i = 0; i <= N; i++)
            tone[i] = amplitude * Math.sin(2 * Math.PI * i * hz / SAMPLE_RATE);
    }

    public double[] getTone()
    {
        return tone;
    }

}

这是我的临时主类:
公开课测试{
    public static void main(String[] args) {
        Sound sound = new Sound();

        //Note(hz (freg),seconds (duration),vol(amplitude))
        Note note1 = new Note(50.0,3.0,6);
        Note note2 = new Note(10.0,3.0,6);
        sound.play(note1.getTone(),note2.getTone());
    }

}   

我考虑过线程,但是我不认为如果我有20-30 ++线程,那将不会同时发生,性能也不会那么好。我看到的例子是它们同时播放声音,但是后来他们从文件中加载了声音,而当我在Midi中尝试时,它不起作用。

最佳答案

我看不到您在哪里使用时间戳。使用MIDI,通常可以安排声音以立即播放或在给定时刻播放。我认为要在MIDI中同时播放声音,通常只需安排所有事件在同一时间或时间戳上进行即可。

处理此问题的教程部分在这里:
http://docs.oracle.com/javase/tutorial/sound/MIDI-messages.html

您是否编写了自己的播放代码?您可以修改它以在给定的时间点播放吗?

同样,如果您要编写自己的声音,看来您必须自己将声音混合在一起。这主要是在以PCM形式添加每个回放事件中的数据的过程中。请原谅我没有花时间弄清楚您可以或应该在代码中的哪个位置进行操作。以下是所需步骤的快速摘要:创建一个while循环,循环遍历给定时刻安排的每个事件,然后将字节流组合成每个帧的PCM数据,并将同时播放的帧加在一起(左声道数据与左声道,右声道数据,然后将其反汇编为SourceDataLine所需格式的字节。

关于java - 在Java中同时播放创建的Midi声音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25708657/

相关文章:

java - jpa:分离与 transient 定义

java - java中实现RC5加密

tensorflow - 如何在 TensorFlow 图中读取 Ogg 或 MP3 音频文件?

audio - 如何区分乐器和声音?

javascript - 使用 JavaScript 代码创建音频对象

java - Java 与 C++ 中 OpenCV 的实时性能对比

java - 为什么HashMap中有这么多空映射?

java - 尝试从文件创建矩阵数组,但程序返回两个相同的矩阵?

c# - MIDI 入门

embed - 使用 Chrome 播放 MIDI 文件