java是否已经build立了audio_synthesis_库?

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

我想以编程方式生成audio文件,并播放它们。

Java是否已经为此构build了库,还是属于依赖于系统的库?

谢谢!

这个太阳论坛post有一些有趣的代码来产生罪恶的音调。 此外,鉴于WAV文件格式不是太复杂,您可以创build一个代表所需波形的表格,然后将其写入文件。 有一些例子,例如一个原始的audio转换器,以及如何写一个wav文件 。

使用安德鲁的方法 ,这里是一个平衡的规模的例子。

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

这种低层次的方法可能适用于较老的,function较差的平台。 另请考虑javax.sound.midi ; 这里显示了一个完整的例子, 这里引用了Synthesizing Sound教程。

最简单的方法是使用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" (ie, 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(); } 

Java的内置Midifunction

现成的Java 8 JRE绝对具有您特别要求的内容:内置合成器库。 在合成声音中有详细的描述。

一个相当精致的例子提供了一个视觉键盘访问采样音乐合成器。

javax.sound.midi库包含基于MIDI和采样仪器技术的乐器以及能够播放音符的function。 这些声音并不像经典的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(); } } 

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

波形生成

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

  • 控制音高(以每秒周期为单位)
  • 幅度控制(以数字步进)
  • 最小和最大的统计数据,用于指示幅度太高时(导致audio削波音失真)
  • 音调持续时间控制(以秒为单位)
  • 控制任意数量的谐波的相对幅度(其确定音质或波形,这是产生音符音色的几个因素之一)

这个合成器缺less高端波形生成合成器的许多function。

  • 在音符的持续时间内实时修改主谐波和其他谐波的幅度
  • 不播放音符序列(但可以相对容易地修改)
  • 没有任何设备可以对谐波进行相位移动(但这是一个有点不相干的缺点,因为谐波的相位特性不是人耳能够检测到的)

SimpleSynth是一个很好的演示

  • audio合成的原理
  • 谐波的音色效应
  • 使用Java来生成audio样本数组
  • 把样本放到一个字节数组中
  • 通过一行提交这样一个字节数组到OS

标准的数字audio术语被用于恒定和可变的命名。 math解释在评论中。

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

增加音乐合成专长的研究主题

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

  • 数字audio
  • 信号采样
  • 奈奎斯特标准
  • 如何在弦乐器(如吉他)上拾取谐波
  • 基本的三angular函数,特别是正弦函数

你看了JSyn吗? 我不认为Java核心库可以做你想做的。

请参阅Java Sound API 。

再看一下,我也find了Jass 。

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