1. 程式人生 > >Java Sound API 學習筆記

Java Sound API 學習筆記

  Java Sound API是javaSE平臺提供底層的(low-level)處理聲音介面。 例外,java也提供了簡單的實用的高層媒體介面(higher-level) - JMF(Java Media Framework)。 Java Sound API 將需要處理的數字音訊分為:simpled-audio和midi, 分別提供Package來處理它們: javax.sound.simpled javax.sound.midi 同時SOUND API還提供了第三方的擴充套件介面: javax.sound.simpled.spi javax.sound.midi.spi *注:spi : service provider interface Sampled Audio 取樣音訊(simpled-audio)不僅包含從模擬訊號取樣來的數字音訊,還包括電腦合成的。 稱作digital-audio更為合適。 為了能夠讓裝置播放取樣聲音,程式需要處理 audio input, output device, audio data buffers。 還有混音處理(mix multiple streams of audio into one stream)。 SOUND API 可以使用3種方式傳輸聲音資料:stream, buffered fashion, in-memory unbuffered fashion。 第三種方式適合資料量不大,能夠一次載入的所有資料的情形。這樣,聲音的響應較快,迴圈和隨機定位也會很簡單。 使用SOUND API播放聲音至少需要3樣東西: lformatted audio data, la mixer, la line. Mixer
調音臺 technically the Mixer itself is also a kind of Line Line 音訊資料管道。 Clip extends Line 將需要播放的音訊資料裝載進來。 preloads audio data from a sound file into clips A Clip is a data line into which audio data can be loaded prior to playback. Because the data is pre-loaded rather than streamed, the clip's duration is known before playback, and you can choose any
starting position in the media. Clips can be looped, meaning that upon playback, all the data between two specified loop points will repeat a specified number of times, or indefinitely. SourceDataLine extends Line accept real-time stream of audio data feed audio to the Mixer A SourceDataLine receives audio data for playback. It provides methods for writing data to the
source data line's buffer for playback, and for determining how much data the line is prepared to receive without blocking. TargetDataLine A TargetDataLine receives audio data from a mixer. Commonly, the mixer has captured audio data from a port such as a microphone; it might process or mix this captured audio before placing the data in the target data line's buffer. The TargetDataLine interface provides methods for reading the data from the target data line's buffer and for determining how much data is currently available for reading. Port extends Line simple Line Line 介面的繼承關係圖 AudioSystem AudioSystem提供音訊資源的訪問服務。 通過AudioSystem,可以知道什麼樣的資源可以被識別。 可從AudioSystem獲得的資源: lMixers, AudioSystem類可以提供一張已經安裝的Mixer列表 lLines lFormat conversions lFiles and streams Mixer的獲得 Mixer.Info AudioSystem.getMixerInfo():Mixer.Info 可以獲得一張Mixer資訊列表。 每個Mixer.Info包含如下關鍵資訊: lName lVersion lVendor lDescription 我機器上的Mixer列表,WinXP,JDK_1.4.2 [INFO 0] INFO.NAME:Java Sound Audio Engine INFO.VERSION:1.0 INFO.VERDOR:Sun Microsystems INFO.DESCRIPTION:Software mixer and synthesizer [INFO 1] INFO.NAME:Microsoft ?ù??????÷ INFO.VERSION:Unknown Version INFO.VERDOR:Unknown Vendor INFO.DESCRIPTION:No details available [INFO 2] INFO.NAME:Realtek AC97 Audio INFO.VERSION:Unknown Version INFO.VERDOR:Unknown Vendor INFO.DESCRIPTION:No details available [INFO 3] INFO.NAME:Realtek AC97 Audio INFO.VERSION:5.10 INFO.VERDOR:Unknown Vendor INFO.DESCRIPTION:Unknown Description 獲取Mixer AudioSystem.getMixer( MixerInfo ):Mixer 如果只從Mixer.Info提供的資訊無法確定需要的Mixer, 不妨創建出所有的Mixer,使用時檢查它們的能力,使用合適那個。 例如,你可能需要一個Mixer,能夠將混音好的資料同時寫入一定數目的目標資料管道(TargetDataLine. 使用Mixer.getMaxLines(Line.Info info):int來了解Mixer的輸出能力。info就是指定的TargetDataLine 獲得指定型別Line 2種方法獲得Line l直接由AudioSystem獲得,AudioSystem.getLine( Line.Info ):Line lMixer獲得 AudioSystem直接獲得Line static Line AudioSystem.getLine( Line.Info ) 不同於Mixer.InfoLine.Info不是文字資訊,而是Line的類資訊。 Line.Info是抽象類,所以使用它的子類DataLine.Info,Port.Info 下面是通過Line.Info獲得Line的示例: TargetDataLine line; DataLine.Info info = new DataLine.Info(TargetDataLine.class,     format); // format is an AudioFormat object if (!AudioSystem.isLineSupported(info)) {     // Handle the error. } // Obtain and open the line. try {     line = (TargetDataLine) AudioSystem.getLine(info);     line.open(format); } catch (LineUnavailableException ex) {     // Handle the error.     //... } Port.Info定義一系列靜態的Port.Info物件,MICROPHONE,SPEAKER,etc. Mixer獲得Line getSourceDataLine() getTargetDataLine() getLine() AudioSystem物件模型 AudioPermission 音訊資源訪問許可。 利用JAVA-SOUND-API播放聲音 可以使用2Line來播放聲音,Clip,SourceDataLine Clip一次載入需要播放的聲音資源,而SourceDataLine 以流的方式傳輸聲音資料。 使用Clip 當使用getLine()獲得Clip後,還要保證其他的程式在你播放前不能獲取它,呼叫open()方法獨佔它: void open( AudioInputStream stream ); void open( AudioFormat, byte[] data, int offset, int bufferSize ); Clip預設從音訊的開頭開始播放,除非使用setFramePosition(),setMicroSecondPosition()設定其他位置。 Clip.start()播放,Clip.stop()暫停。 getLevel(),獲得聲音高度。 isActive(),Clip是否正在播放。 使用SourceDataLine open(AudioFormat),開啟source dataLine,但不指定資料,使用預設的buffer size open(AudioFormat, bufferSize),指定buffer size. 合理設定buffer size,保證開始載入的延時能夠被接受,又儘量減少IO訪問。 open()之後,呼叫start()容許SourceDataLine一有資料就開始播放,使用write()不停的輸入資料。 void write( byte[] b, int offset, int length ); SourceDataLine開始播放後,向Mixer傳輸資料,當Mixer開始向target傳輸資料時,SourceDataLine產生一個 START 事件。 這是SourceDataLine被認為是活動的(active) isRunning()表明Line是否start()了。 isActive()表明Line是否開始播放。 write()方法向buffer size 中寫入資料,如果buffer已滿,還剩下一些資料,該方法會阻塞;否則,是不阻塞的。 可以使用DataLine.available()知道buffer size還能寫入多少資料。 事實上,可以另開執行緒去使用write(),而不用考慮阻塞的問題。 drain()方法在所有資料播放完後返回。 所有在寫完資料後,呼叫drain(),到它返回時再是否Line line.write(b, offset, numBytesToWrite); //this is the final invocation of write line.drain(); line.stop(); line.close(); line = null; flush()清空buffer中的剩餘資料,Linestop時才能呼叫。 有如下情形,Line會產生 STOP 事件: l呼叫drain() l呼叫stop() l呼叫flush() l輸出完舊的資料,而新的資料未到時。 STOP事件意味著isActive()返回false. start()呼叫之後,isRunning()都會返回true,知道stop()被呼叫。它不是依據 STOP 事件產生返回值的。 isActive()是依據START STOP 事件的。 監視Line的狀態 使用LineListener響應Line的事件。 void Line.addLineListener( LineListener ); 當呼叫open(),close(),start(),stop()會產生OPEN,CLOSE,START,STOP事件。 多個Line 同步播放 有些Mixer提供方便的同步功能,對一組Lines使用open(),close(),start(),stop(),保證它們的同步。 可以使用如下方法,檢查Mixer是否支援同步功能: boolean isSynchronizationSupported( Line[] lines, boolean maintainSync ) 第二個引數表明同步精度,是取樣同步,還是隻是start(),stop()保持同步,並不維護播放過程同步。 AudioFormat 音訊取樣的格式,不是音訊檔案的格式。 l編碼技術,一般都是PCM( pulse code modulation ) l聲道數目(1,單聲道;2,雙聲道;等等) l取樣頻率 sample rate l樣本的位數 number of bits per sample l幀速率 Frame rate lFrame Size in bytes lByte Order( big-endian or little-endian ) AudioFileFormat: 音訊檔案格式。 The file type( WAV,AIFF,etc ) The file length in bytes The length, in frames, of the audio data contained in the file An AudioFormat that specifies data format of the audio data in the file AudioInputStream extends InputStream 無須考慮檔案的格式,就能操作Samples 讀取音訊檔案 AudioSystem提供2中方法讀取音訊檔案: l根據音訊檔案中音訊資料的格式資訊 l使用一個指定了音訊資料格式的流 使用如下方法獲得音訊檔案中音訊資料的格式資訊: static AudioFileFormat getAudioFileFormat(File); static AudioFileFormat getAudioFileFormat(InputStream); static AudioFileFormat getAudioFileFormat(URL); 利用如下方法獲得第二種方法提到的音訊資料流: static AudioInputStream getAudioInputStream(File) static AudioInputStream getAudioInputStream(InputStream) static AudioInputStream getAudioInputStream(URL) 讀取音訊檔案資料的步驟: 1)獲得AudioInputStream物件 2)建立一個位元組陣列,存放一次讀入的資料塊 3)不斷地從audio流中讀入資料,播放或處理資料。 示例程式碼如下: int totalFramesRead = 0; File fileIn = new File(somePathName); // somePathName is a pre-existing string whose value was // based on a user selection. try {     AudioInputStream audioInputStream =         AudioSystem.getAudioInputStream(fileIn);     int bytesPerFrame =         audioInputStream.getFormat().getFrameSize();     // Set an arbitrary buffer size of 1024 frames.     int numBytes = 1024 * bytesPerFrame;     byte[] audioBytes = new byte[numBytes];     try {         int numBytesRead = 0;         int numFramesRead = 0;         // Try to read numBytes bytes from the file.         while ((numBytesRead =             audioInputStream.read(audioBytes)) != -1) {         // Calculate the number of frames actually read.         numFramesRead = numBytesRead / bytesPerFrame;         totalFramesRead += numFramesRead;         // Here, do something useful         // with the audio data that's         // now in the audioBytes array...     }     } catch (Exception ex) {         // Handle the error...     } } catch (Exception e) {     // Handle the error... } 寫音訊檔案 通過下列方法知道AudioSystem支援哪些音訊檔案格式寫入: static boolean isFileTypeSupported( AudioFileFormat.Type,AudioInputStream ); static AudioFileFormat.Type[] getAudioFileTypes(); static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream); 利用AudioSystem.write()方法向檔案寫入指定格式的音訊資料: static int write( AudioInputStream, AudioFileFormat.Type, File ); 檔案或資料格式轉換 “Java Sound Programmer Guide” – chapter 7 Audio File Format Convertion Audio Data Format Convertion PCM PCM 脈衝編碼調製是Pulse Code Modulation的縮寫。 PCM通過抽樣、量化、編碼三個步驟將連續變化的模擬訊號轉換為數字編碼。 PCM是數字音訊中最佳的保真水準,近似看成“無損”編碼。 PCM編碼的優點是音質好,缺點是資料量大。 JAVA SOUND API對於其它編碼格式,在播放前都會轉換成PCM格式。 DAC:digital-to-analog converter,數模轉換器 Decibel:分貝。pl.decibels PAN:聲象,該通道訊號在左右音箱之間的立體聲位置。 GAIN:增益 REVERB:數字混響。 Acoustics:聲學 資源 《Java Sound programmer guide》