关于java:Java游戏编程不完全详解4

48次阅读

共计 4870 个字符,预计需要花费 13 分钟才能阅读完成。

前言

代码演示环境:

  • 软件环境:Windows 10
  • 开发工具:Visual Studio Code
  • JDK 版本:OpenJDK 15

声效和音乐

声效基础知识


当咱们玩游戏时, 咱们可能会听到声效,然而不会真正留神它们。因为心愿听到他们,所以声效在游戏中是十分重要的。另外,在游戏中的音乐会动静被批改来配合游戏的剧情的倒退。那么什么是声效(声音)呢?声效是通过媒体振动产生的成果。该媒体是空气和计算机中的扬声器产生的振动—从而收回了声音—传送到咱们耳朵里;而后咱们的耳膜会捕捉这些信号,接着传送给咱们的大脑,从而人类听到了声音。

共振 (vibration) 是通过空气的压缩振动 (fluctuations) 产生的,疾速的振动产生高频声效,让咱们听到低音。每个振动的压缩数量是应用振幅 (amplitude) 来示意的。高振幅会让咱们听到声音大;简而言之,声波 (sound waves) 就是在长久工夫不停批改振幅而已。如下图所示:

数码声效、CD 和计算机的音效格局都是一系列的声波,每秒中的音波振幅叫做音频采样。当然高采样的音波能够更加准确的体现声音,这些采样是应用 16 位来示意 65535 种可能的振幅。许多声音容许多个声道,比方 CD 有两个声道—一个给左扬声器,一个给右扬声器。

Java 声效 API


Java 能够播放 8 位和 16 位的采样,它的范畴从 8000hz 到 48000hz,当然它也能够播放单声道和立体声声效。那么应用什么声音,这须要依据游戏的剧情,比方 16 位单声道,44100Hz 声音。Java 反对三种声频格式文件:AIFF, AU 和 WAV 文件。咱们装载音频文件时应用 AudioSystem 类,该类有几个静态方法,个别咱们应用 getAudioInputStream()办法来关上一个音频文件,能够从本地零碎,或者从互联网关上,而后返回 AudioInputStream 对象。而后应用该对象读取音频采样,或者查问音频款式。

File file = new File(“sound.wav”);
AudioInputStream stream = AudioSystem.getAudioInputStream(file);
AudioFormat format = stream.getFormat();

其中 AudioFormat 类提供了获取声效采样的性能,比方采样率和声道数量。另外它还提供了 frame 尺寸 – 一些字节数量。比方 16 位立体声,它的 frame 大小是 4,或者 2 个字节示意采样值,这样咱们能够很不便的计算出立体声能够占多少内存。比方 16 位三分之二长度的平面音频格式采样所占内存值:44100x 3x 4 字节 = 517KB,如果是单声道,那么采样容量是立体声的一半。

当咱们常识声频采样的大小与格局之后,接下来就是从这些声频文件中读取内容了。接口 Line 是用来发送和接管零碎的音频的 API。咱们能够应用 Line 发送声音采样到 OS 的声音零碎去播放,或者接管 OS 的声音零碎的声音,比方 microphone 声音等。Line 有几个子接口,最次要的子接口是 SourceDataLine,该接口能够让咱们向 OS 中的声音零碎写入声音数据。Line 的实例是通过 AudioSystem 的 getLine()办法获取,咱们能够传送参数 Line.Info 对象来指定返回的 Line 类型。因为 Line.Info 有一个 DataLine.Info 子类,它晓得 Line 类型除了 SourceDataLine 接口之外,还有另外一个 Line 叫做 Clip()接口。该接口能够为咱们做许多事件,比方把采样从 AudioInputStream 流装载到内存中去,并且主动向音频系统输送这些数据去播放。上面是应用 Clipe 来实现声音的播放代码:

// 指定哪种 line 须要被创立
DataLine.Info info = new DataLine.Info(Clip.class,format);
// 创立该 line
Clip clip = (Clip)AudioSystem.getLine(info);
// 从流对象装载采样
clip.open(stream);
// 开始播放音频内容
clip.start();

Clip 接口十分好用,它十分相似于 JDK 1.0 版本中 AudioClip 对象,然而它有一些毛病,比方 Java 声效有限度 Line 的数量,这种限度是在雷同的工夫关上 Line 时呈现,个别最多有 32 个 Line 对象同时存在。也就是说,咱们只能关上无限个 line 对象应用。另外,如果咱们想同时播放多个 Clip 对象,那么 Clip 只能在同一时间播放一个声音,比方咱们想同时播放两到三个爆炸声,然而一个声音只能利用一个爆炸声。因为这种缺点,所以咱们会创立一种解决方案来克服这种问题。

播放声音


上面咱们创立一个简略的声音播放器,次要应用 AudioInputStream 类把音频文件读到字节数组中,而后应用 Line 对象来自动播放。因为 ByteArrayInputStream 类封装了字节数组,所以,咱们能够同时播放多个雷同音频的复本。getSamples(AudioInputStream)办法从 AudioInputStream 流中读采样数据,而后保留到字节数组中,最初应用 play()办法从 InputStream 流对象中读取数据到缓存,而后写到 SourceDataLine 对象中让它播放。

因为 Java 声效 API 中有 bug,所以让 Java 过程不会本人退出,通常状况下,JVM 只运行精灵线程,然而当咱们应用 Java 声效时,非精灵线程在台后进行中运行,所以咱们必须呼叫 System.exit(0)完结 Java 声效过程。

SimpleSoundPlayer 类

package com.funfree.arklis.sounds;
import java.io.*;
import javax.sound.sampled.*;

/**
    性能:书写一个的类,用来封装声音从文件系统关上,而后进行播放
    */

public class SimpleSoundPlayer  {
    private AudioFormat format;
    private byte[] samples;// 保留声音采样

    /**
        Opens a sound from a file.
    */
    public SimpleSoundPlayer(String filename) {
        try {
            // 关上一个音频流
            AudioInputStream stream =
                AudioSystem.getAudioInputStream(new File(filename));

            format = stream.getFormat();

            // 获得采样
            samples = getSamples(stream);
        }
        catch (UnsupportedAudioFileException ex) {ex.printStackTrace();
        }
        catch (IOException ex) {ex.printStackTrace();
        }
    }


    /**
        Gets the samples of this sound as a byte array.
    */
    public byte[] getSamples() {return samples;}


    /**
        从 AudioInputStream 获取采样,而后保留为字节数组。-- 这里的数组会被封装 ByteArrayInputStream 类中,以便 Line 能够同时播放多个音频文件。*/
    private byte[] getSamples(AudioInputStream audioStream) {
        // 获取读取字节数
        int length = (int)(audioStream.getFrameLength() *
            format.getFrameSize());

        // 读取整个流
        byte[] samples = new byte[length];
        DataInputStream is = new DataInputStream(audioStream);
        try {is.readFully(samples);
        }
        catch (IOException ex) {ex.printStackTrace();
        }

        // 返回样本
        return samples;
    }


    /**
        播放流
    */
    public void play(InputStream source) {
        // 每 100 毫秒的音频采样
        int bufferSize = format.getFrameSize() *
            Math.round(format.getSampleRate() / 10);
        byte[] buffer = new byte[bufferSize];

        // 创立 line 对象来执行声音的播放
        SourceDataLine line;
        try {
            DataLine.Info info =
                new DataLine.Info(SourceDataLine.class, format);
            line = (SourceDataLine)AudioSystem.getLine(info);
            line.open(format, bufferSize);
        }catch (LineUnavailableException ex) {ex.printStackTrace();
            return;
        }

        // 开始自动播放
        line.start();

        // 拷贝数据到 line 对象中
        try {
            int numBytesRead = 0;
            while (numBytesRead != -1) {
                numBytesRead =
                    source.read(buffer, 0, buffer.length);
                if (numBytesRead != -1) {line.write(buffer, 0, numBytesRead);
                }
            }
        }catch (IOException ex) {ex.printStackTrace();
        }

        // 期待所有的数据播放结束,而后敞开 line 对象。line.drain();
        line.close();}

}

如果须要循环播出,那么批改一下下面类就能够实现该性能。

LoopingByteInputStream 类

package com.funfree.arklis.engine;
import static java.lang.System.*;
import java.io.*;

/**
    性能:封装 ByteArrayInputStream 类,用来循环播放音频文件。当进行循环播放时
          呼叫 close()办法
    */
public class LoopingByteInputStream extends ByteArrayInputStream{
    private boolean closed;
    
    public LoopingByteInputStream(byte[] buffer){super(buffer);
        closed = false;
    }
    
    /**
        读取长度为 length 的数组。如果读完数组内容,那么把下标设置为开始处,如果敞开状态,那么返回 -1.
        */
    public int read(byte[] buffer, int offset, int length){if(closed){return -1;}
        int totalBytesRead = 0;
        while(totalBytesRead < length){
            int numBytesRead = super.read(buffer,offset + totalBytesRead,
                length - totalBytesRead);
            if(numBytesRead > 0){totalBytesRead += numBytesRead;}else{reset();
            }
        }
        return totalBytesRead;
    }
    
    /**
        敞开流
        */
    public void close()throws IOException{super.close();
        closed = true;
    }
}

声效过滤器是简略的音频处理器,用来现有的声音样本,这种过滤器个别用来实时处理声音。所以谓声效过滤器就是常说的是数字信号处理器 (digital signal processor)—用于前期声效的解决,比方吉他增加回响成果。
图片起源:http://www.cungun.com/ 游戏

正文完
 0