【问题标题】:Does java have built in libraries for audio _synthesis_?java是否有内置的音频_synthesis_库?
【发布时间】:2011-01-05 01:32:24
【问题描述】:

注意:我不想“读取音频文件 foo.bar 并播放它。”

我想以编程方式即时生成音频文件并播放它们。

Java 是否为此内置了库,还是属于系统相关库?

谢谢!

【问题讨论】:

    标签: java audio


    【解决方案1】:

    Java 的内置 Midi 功能

    现成的 Java 8 JRE 肯定有您特别要求的东西:一个内置的合成器库。在Synthesizing Sound 中有详细描述。

    quite refined example 提供对采样音乐合成器的可视键盘访问。

    javax.sound.midi 库包含基于 MIDI 和采样乐器技术的乐器以及在其上演奏音符的能力。声音不像经典的 Kurzweil 乐器系列那样真实,但如果您希望在单个乐器的多个音高范围内进行自己的采样并计算出相当无缝过渡的细节,该框架支持这种复杂程度范围之间。

    快速查看使用情况的简单示例

    这是一个简单的示例类。

    import javax.sound.midi.MidiSystem;
    import javax.sound.midi.Synthesizer;
    import javax.sound.midi.MidiChannel;
    
    public class PlayMidiNote
    {
        private static void sleep(int iUSecs)
        {
            try
            {
                Thread.sleep(iUSecs);
            }
            catch (InterruptedException e)
            {
            }
        }
    
        public static void main(String[] args) throws Exception
        {
            if (args.length < 3 && args.length > 4)
            {
                System.out.println("usage: java PlayNote
                        <8.bit.midi.note.number> <8.bit.velocity>
                        <usec.duration> [<midi.channel>]");
                System.exit(1);
            }
    
            int iMidiKey = Math.min(127, Math.max(0,
                    Integer.parseInt(args[0])));
            int iVelocity = Math.min(127, Math.max(0,
                    Integer.parseInt(args[1])));
            int iUSecsDuration = Math.max(0,
                    Integer.parseInt(args[2]));
            int iChannel = args.length > 3
                ? Math.min(15, Math.max(0,
                        Integer.parseInt(args[3])))
                : 0;
    
            Synthesizer synth = MidiSystem.getSynthesizer();
            synth.open();
    
            MidiChannel[] channels = synth.getChannels();
            MidiChannel channel = channels[iChannel];
    
            channel.noteOn(iMidiKey, iVelocity);
            sleep(iUSecsDuration);
            channel.noteOff(iMidiKey);
    
            synth.close();
        }
    }
    

    使用多线程或 javax.sound.midi.Sequencer 的实现(如 GitHub.com 上提供的那些)将提供实际制作音乐所需的结构。

    波形生成

    如果您希望生成自己的波形而不是使用样本,那么您的问题的答案是“不”,但是您可以使用我为此问题编写的音调合成器。它有几个特点。

    • 音高控制(以每秒周期为单位)
    • 幅度控制(数字步进)
    • 最小和最大统计数据表明振幅何时过高(这会导致音频削波导致音调失真)
    • 控制音调持续时间(以秒为单位)
    • 控制任意数量的谐波的相对幅度(这决定了音质或波形,这是创建音符音色的几个因素之一)

    这种合成器缺少高端波形生成合成器的许多功能。

    • 在音符持续时间内实时修改主谐波和其他谐波的幅度
    • 不播放音符序列(但可以相对轻松地进行修改)
    • 也无法对谐波进行相移(但这是一个无关紧要的缺点,因为谐波的相位特性不是人耳能够检测到的)

    SimpleSynth 是一个很好的演示

    • 音频合成原理
    • 和声的音色效果
    • 使用Java生成音频样本数组
    • 将样本放入字节数组中
    • 这样的字节数组通过一行提交给操作系统

    标准数字音频术语用于常量和变量命名。数学在 cmets 中进行了解释。

    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.AudioFormat;
    import javax.sound.sampled.SourceDataLine;
    
    class SimpleSynth
    {
        private static int SAMPLE_BITS = 16;
        private static int CHANNELS = 1;
        private static boolean SIGNED_TRUE = true;
        private static boolean BIG_ENDIAN_FALSE = false;
        private static float CDROM_SAMPLE_FREQ = 44100;
    
        private SourceDataLine line;
    
        private double dDuration;
        private double dCyclesPerSec;
        private double dAmplitude;
        private double[] adHarmonics;
    
        private double dMin;
        private double dMax;
    
        public SimpleSynth(String[] asArguments) throws Exception
        {
            dDuration = Double.parseDouble(asArguments[0]);
            dCyclesPerSec = Double.parseDouble(asArguments[1]);
            dAmplitude = Double.parseDouble(asArguments[2]);
            adHarmonics = new double[asArguments.length - 3];
            for (int i = 0; i < adHarmonics.length; ++ i)
                adHarmonics[i] = Double.parseDouble(
                        asArguments[i + 3]);
    
            AudioFormat format = new AudioFormat(
                    CDROM_SAMPLE_FREQ, SAMPLE_BITS,
                    CHANNELS, SIGNED_TRUE, BIG_ENDIAN_FALSE);
            line = AudioSystem.getSourceDataLine(format);
            line.open();
            line.start();
        }
    
        public void closeLine()
        {
            line.drain();
            line.stop();
        }
    
        public void playSound()
        {
            // allocate and prepare byte buffer and its index
            int iBytes = (int) (2.0 * (0.5 + dDuration)
                    * CDROM_SAMPLE_FREQ);
            byte[] ab = new byte[iBytes];
            int i = 0;
    
            // iterate through sample radian values
            // for the specified duration
            short i16;
            double dSample;
            double dRadiansPerSample = 2.0 * Math.PI
                    * dCyclesPerSec / CDROM_SAMPLE_FREQ;
            double dDurationInRadians = 2.0 * Math.PI
                    * dCyclesPerSec * dDuration;
            dMin = 0.0;
            dMax = 0.0;
            for (double d = 0.0;
                    d < dDurationInRadians;
                    d += dRadiansPerSample)
            {
                // add principle and the dot product of harmonics
                // and their amplitudes relative to the principle
                dSample = Math.sin(d);
                for (int h = 0; h < adHarmonics.length; ++ h)
                    dSample += adHarmonics[h]
                            * Math.sin((h + 2) * d);
    
                // factor in amplitude
                dSample *= dAmplitude;
    
                // adjust statistics
                if (dMin > dSample)
                    dMin = dSample;
                if (dMax < dSample)
                    dMax = dSample;
    
                // store in byte buffer
                i16 = (short) (dSample);
                ab[i ++] = (byte) (i16);
                ab[i ++] = (byte) (i16 >> 8);
            }
    
            // send the byte array to the audio line
            line.write(ab, 0, i);
        }
    
        public void printStats()
        {
            System.out.println("sample range was ["
                    + dMin + ", " + dMax + "]"
                    + " in range of [-32768, 32767]");
    
            if (dMin < -32768.0 || dMax > 32767.0)
                System.out.println("sound is clipping"
                        + "(exceeding its range),"
                        + " so use a lower amplitude");
        }
    
        public static void main(String[] asArguments)
                throws Exception
        {
            if (asArguments.length < 3)
            {
                System.err.println("usage: java SimpleSynth"
                        + " <duration>"
                        + " <tone.cycles.per.sec>"
                        + " <amplitude>"
                        + " [<relative.amplitude.harmonic.2>"
                        + " [...]]");
                System.err.println("pure tone:"
                        + " java SimpleSynth 1 440 32767");
                System.err.println("oboe-like:"
                        + " java SimpleSynth 1 440 15000  0 1 0 .9");
                System.err.println("complex:"
                        + " java SimpleSynth 1 440 800 .3"
                        + " .5 .4 .2 .9 .7 5 .1 .9 12 0 3"
                        + " .1 5.2 2.5 .5 1 7 6");
    
                System.exit(0);
            }
    
            SimpleSynth synth = new SimpleSynth(asArguments);
            synth.playSound();
            synth.closeLine();
            synth.printStats();
    
            System.exit(0);
        }
    }
    

    增强音乐合成专业知识的研究主题

    如果您想成为数字合成器专家,可以阅读一些主题。

    • 数字音频
    • 信号采样
    • 奈奎斯特标准
    • 如何在弦乐器(如吉他)上弹奏泛音
    • 基本三角函数,特别是正弦函数

    【讨论】:

      【解决方案2】:

      使用Andrew's approach,这是一个播放equal tempered scale 的示例。

      import javax.sound.sampled.AudioFormat;
      import javax.sound.sampled.AudioSystem;
      import javax.sound.sampled.LineUnavailableException;
      import javax.sound.sampled.SourceDataLine;
      
      public class Tone {
      
          public static void main(String[] args) throws LineUnavailableException {
              final AudioFormat af =
                  new AudioFormat(Note.SAMPLE_RATE, 8, 1, true, true);
              SourceDataLine line = AudioSystem.getSourceDataLine(af);
              line.open(af, Note.SAMPLE_RATE);
              line.start();
              for  (Note n : Note.values()) {
                  play(line, n, 500);
                  play(line, Note.REST, 10);
              }
              line.drain();
              line.close();
          }
      
          private static void play(SourceDataLine line, Note note, int ms) {
              ms = Math.min(ms, Note.SECONDS * 1000);
              int length = Note.SAMPLE_RATE * ms / 1000;
              int count = line.write(note.data(), 0, length);
          }
      }
      
      enum Note {
      
          REST, A4, A4$, B4, C4, C4$, D4, D4$, E4, F4, F4$, G4, G4$, A5;
          public static final int SAMPLE_RATE = 16 * 1024; // ~16KHz
          public static final int SECONDS = 2;
          private byte[] sin = new byte[SECONDS * SAMPLE_RATE];
      
          Note() {
              int n = this.ordinal();
              if (n > 0) {
                  double exp = ((double) n - 1) / 12d;
                  double f = 440d * Math.pow(2d, exp);
                  for (int i = 0; i < sin.length; i++) {
                      double period = (double)SAMPLE_RATE / f;
                      double angle = 2.0 * Math.PI * i / period;
                      sin[i] = (byte)(Math.sin(angle) * 127f);
                  }
              }
          }
      
          public byte[] data() {
              return sin;
          }
      }
      

      这种低级方法可能适用于较旧、功能较弱的平台。也可以考虑javax.sound.midihere 显示了一个完整的示例,Synthesizing Sound 教程被引用 here

      【讨论】:

      • @DouglasDaseeco:感谢您做出这样的区分;虽然提问者想要“以编程方式即时生成音频文件”,但我已经更新了答案以引用您考虑替代方案的理由。
      【解决方案3】:

      最简单的方法是使用 java 内置的 MIDI 库:

      int channel = 0; // 0 is a piano, 9 is percussion, other channels are for other instruments
      
          int volume = 80; // between 0 et 127
          int duration = 200; // in milliseconds
      
          try {
              Synthesizer synth = MidiSystem.getSynthesizer();
              synth.open();
              MidiChannel[] channels = synth.getChannels();
      
              // --------------------------------------
              // Play a few notes.
              // The two arguments to the noteOn() method are:
              // "MIDI note number" (pitch of the note),
              // and "velocity" (i.e., volume, or intensity).
              // Each of these arguments is between 0 and 127.
              channels[channel].noteOn( 60, volume ); // C note
              Thread.sleep( duration );
              channels[channel].noteOff( 60 );
              channels[channel].noteOn( 62, volume ); // D note
              Thread.sleep( duration );
              channels[channel].noteOff( 62 );
              channels[channel].noteOn( 64, volume ); // E note
              Thread.sleep( duration );
              channels[channel].noteOff( 64 );
      
              Thread.sleep( 500 );
      
              // --------------------------------------
              // Play a C major chord.
              channels[channel].noteOn( 60, volume ); // C
              channels[channel].noteOn( 64, volume ); // E
              channels[channel].noteOn( 67, volume ); // G
              Thread.sleep( 3000 );
              channels[channel].allNotesOff();
              Thread.sleep( 500 );
      
      
      
              synth.close();
          }
          catch (Exception e) {
              e.printStackTrace();
          }
      

      【讨论】:

      • 这是一个很好的解决方案,我会经常搞砸这个!干杯
      【解决方案4】:

      Jcollider 是 SuperCollider 综合服务器的 Java 接口。如果您想合成音乐,这将使事情变得更容易(它从音调发生器抽象到合成器,处理诸如图形生成之类的事情,从合成图中删除静音合成器直到再次需要它们,在合成器之间修补信号动态等)。

      【讨论】:

        【解决方案5】:

        This Sun forum post 有一些有趣的代码用于生成正弦音。此外,鉴于 WAV 文件格式并不太复杂,您可以创建一个表示所需波形的表,然后将其写入文件。周围有几个例子,例如a raw audio converterhow to write a wav file

        【讨论】:

        【解决方案6】:

        Java Sound API

        多看了一下,我也找到了Jass

        【讨论】:

          【解决方案7】:

          你看过JSyn吗?我不认为 Java Core 库可以做你想做的事。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-11-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-08-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多