2010-01-14 33 views

回答

0

This Sun forum post有一些有趣的代碼來產生sin音。另外,鑑於WAV文件格式不會過於複雜,您可以創建一個代表所需波形的表格,然後將其寫入文件。周圍有幾個例子,例如a raw audio converterhow to write a wav file

+3

太陽論壇鏈接已經死了。 – RealHowTo 2013-09-15 16:49:25

+0

IIRC,討論包括[方法](http://stackoverflow.com/a/7782749/230513)由於[安德魯湯普森](http://stackoverflow.com/users/418556/andrew-thompson),引用[這裏](http://stackoverflow.com/a/2065693/230513)。 – trashgod 2014-08-02 15:34:55

39

使用​​,這裏是一個播放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.midi;示出了一個完整的示例here並且Synthesizing Sound教程被引用爲here

+0

Java包javax.sound.sampled不是一個綜合庫。它只是簡化了已經合成到操作系統本地音頻系統的數字表示的傳遞。 – 2017-02-24 06:16:34

+0

@DouglasDaseeco:謝謝你畫出這個區別;而提問者希望「以編程方式生成音頻文件」,我已經更新了答案,以引用您考慮替代方案的理由。 – trashgod 2017-02-24 10:00:44

+0

感謝您的讚賞。提問者並不十分具體地詢問該庫是否是波形生成庫或告訴OS到播放波形庫。你可能喜歡嘗試一個使用MIDI圖書館的小程序。它以採樣合成器的方式產生音符。對器官或合成聲音的音符不太有用,但更好地產生傳統樂器的聲音。 – 2017-02-24 15:43:56

1

Jcollider是SuperCollider綜合服務器的Java接口。如果你想合成音樂,這將使事情變得更加容易(它從音源發生器抽象到合成器,處理圖形生成等事情,從合成圖中刪除靜音合成器,直到再次需要它們,修補合成器之間的信號動態等)。

7

要做到這一點,最簡單的方法是使用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(); 
    } 
+0

這是一個很好的解決方案,我會用這個很多的東西!乾杯 – Stevo 2015-11-09 00:29:39

4

Java的內置MIDI兼容性

關閉的,現成的Java 8 JRE肯定有你特別要求:內置合成器庫。在Synthesizing Sound中有詳細的描述。

A 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來產生音頻樣本陣列
    • 樣品的載置到字節數組
    • 這樣的字節的提交原則通過一條線向操作系統排列

    標準數字音頻術語用於常量和變量命名。數學在評論中解釋。研究

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

    主題,以增加音樂合成專業

    有幾個主題,如果你想成爲一個數字合成器專家讀了。

    • 數字音頻
    • 信號採樣
    • 奈奎斯特准則
    • 如何採摘上的絃樂器諧波(如吉他)
    • 基本三角學,特別是正弦函數