赞 | 2 |
VIP | 143 |
好人卡 | 1 |
积分 | 1 |
经验 | 216792 |
最后登录 | 2019-10-10 |
在线时间 | 24 小时 |
Lv1.梦旅人
- 梦石
- 0
- 星屑
- 61
- 在线时间
- 24 小时
- 注册时间
- 2008-8-5
- 帖子
- 1924
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 紫苏 于 2009-11-20 13:26 编辑
扩展音频模块v1.03.rar
(2.17 MB, 下载次数: 1703)
2009/04/25 v1.01:
2009/05/14 v1.02:
- 修正了范例工程中的一些问题
- 添加了一个保存播放中的 BGM 到存档文件的应用实例(详见范例工程)
2009/10/19 v1.03:
写在前面:
首先,这个脚本不像其它外挂脚本那样一旦插入就能立刻见效。这是一个扩展库(模块)脚本,面向的对象是有点脚本基础(至少知道什么是整数、字符串、函数及其基本用法)的人~
一般游戏使用 RGSS 的 Audio 模块便已足够。但如果你的游戏需要实现以下功能,那就可以调用这个 AudioEx 模块中的函数:
1、淡入音乐的效果
2、在音乐播放结束时的操作
3、获取一首音乐的状态——没准备好、正在播放、暂停、停止
4、改变 MIDI 的播放速度
5、播放时改变音乐节奏(不包括改变 MIDI 的声调)
6、播放时随时移动播放位置的指针
7、同时播放多首音乐
脚本的实现:
AudioEx 调用的是 Windows 的 MCI(Multimedia Control Interface,多媒体控制接口)中的一个函数 mciSendString。MCI 是一个高层 API,它为多媒体设备的访问提供了一种十分方便快捷的方式,只要发送一个简单的命令,就能完成对多媒体设备的一系列复杂操作。这个脚本仅仅用到了 MCI 部分处理音频的命令,实际上 MCI 的功能远远不止于此。
AudioEx 支持 RMXP 常用的音频格式,但不支持 OGG。
在我的电脑上,用 AudioEx 播放 MIDI 的话,同时只能播放一个;而其它格式的音频则可以同时播放多个(可以配合原来的播放模块 Audio 同时播放两个 MIDI 文件)。
AudioEx 没有 RMXP 中改变 MIDI 节奏的功能(希望知道的高手能指点一下),但支持其它格式的播放速度(声音频率)的改变。
关于音乐是否播放结束的判断,mciSendString 本来可以在播放结束时发送一个通知消息通知窗口,但因为在 RMXP 游戏窗口中捕获消息比较麻烦,所以这个脚本没有采用这种方式来判断是否播结束(详见范例工程)。
AudioEx 调用的是 mciSendString 的 unicode 版本 mciSendStringW,否则无法解析包含中文的文件路径。Ruby 的字符串编码是 UTF-8,所以在调用的时候做了相应的转换。
MCI 还支持视频的播放,其针对于视频的功能比起音频来还更多、更繁杂。本站播放 AVI 的脚本就是直接调用了 MCI 来实现的~
常用函数:
这里介绍一些最常用的函数用法。
AudioEx 的函数的第一个参数总是一个字符串,它标识着要操作的设备。不同的音频可以在不同的设备上播放,而唯有通过一个唯一的标识,程序才知道你想要访问哪个设备,就好比公民都有一个唯一的身份证号。这个标识可以在调用的时候任意取,但不要包含双引号等特殊字符。
AudioEx.quickPlay(device, filename, volume, speed, fade)
这个函数可以快速的播放指定的音频文件,仅仅用于第一次打开音频文件(设备)。
device 是设备的标识
filanme 是文件路径(相对/绝对)
volume 是音量,取值范围 0-1000,1000 是默认值,表示最大音量
speed 是播放速度,取值范围 500-1500,1000 是默认值,表示正常速度
fade 是音频淡入的秒数(近似值),默认是 0
例子:
AudioEx.quickPlay("我的音乐1", "Audio/BGM/神奇的九寨.mp3")
AudioEx.quickPlay("我的音乐2", "C:/越剧 天上掉下个林妹妹.mp3", 1000)
AudioEx.quickPlay("我的音乐3", "avril lavigne-girlfriend", 500, 1000)
AudioEx.quickPlay("我的音乐4", "F:/音乐/许嵩 - 断桥残雪.mp3", 1000, 1200, 3) AudioEx.continue(device, fade)
这个函数从暂停状态恢复一个设备的播放。
device 是设备的标识
fade 是音频淡入的秒数(近似值),默认是 1
例子:
AudioEx.continue("我的音乐1")
AudioEx.continue("我的音乐2", 3) AudioEx.fade_out(device, sec, from, to, end_proc)
这个函数淡出一个设备的音量。直接调用的话常用于淡出后关闭音频设备(停止播放)。
device 是设备的标识
sec 是淡出的秒数秒数(近似值),默认是 1
from 是开始淡出的音量(0-1000),默认是 1000
to 是淡出结束时的音量(0-1000),默认是 0
end_proc 是淡出后需要进行的操作,默认是 0:
0 表示关闭设备(释放内存,该设备之后无法通过设备标识访问)
1 表示暂停设备(不释放内存,稍后通过设备标识可以恢复播放)
2 表示停止设备(不释放内存,稍后通过设备标识可以重新播放)
例子:
AudioEx.fade_out("我的音乐1")
AudioEx.fade_out("我的音乐2", 5)
AudioEx.fade_out("我的音乐3", 5, 1000)
AudioEx.fade_out("我的音乐4", 5, 800, 0)
AudioEx.fade_out("我的音乐5", 5, 1000, 200, 1) AudioEx.position(device)
这个函数返回一个设备当前播放(指针)位置的整数,单位是毫秒。
device 是设备的标识
例子:
p "当前播放位置为:" + AudioEx.position("我的音乐1") + "毫秒" AudioEx.length(device)
这个函数返回一个设备音频长度的整数,单位是毫秒。
device 是设备的标识
例子:
len = AudioEx.length("我的音乐1")
print "音乐长度为:" + len / 1000 / 60 + "分" + len / 1000 % 60 + "秒" AudioEx.mode(device)
这个函数返回一个设备当前状态的字符串,可以是以下几种:
"not ready" = 还未准备好播放
"paused" = 暂停中
"playing" = 播放中
"stopped" = 播放停止了(除非调用了 AudioEx.stop,否则 stopped 表示播放结束)
device 是设备的标识
例子:
if AudioEx.mode("我的音乐1") == "stopped"
print "音乐播放结束!"
end AudioEx.timeout(device, time)
这个函数设置一个设备的暂停状态最大持续时间(超时),如果设为 0 则没有超时。
device 是设备的标识
time 是持续时间(毫秒) 自动循环音乐,只要在调用 play 函数的时候传递一个 "repeat" 给它的第二个参数即可:
AudioEx.open("music", "1.mp3")
AudioEx.play("music", "repeat")
完整源代码:- #==============================================================================
- # 本脚本来自www.66RPG.com,使用和转载请保留此信息
- # 作者:紫苏
- #==============================================================================
- $mciSendString = Win32API.new("winmm", "mciSendStringW", ['P', 'P', 'I', 'L'], 'L')
- $mciGetErrorString = Win32API.new("winmm", "mciGetErrorStringW", ['L', 'P', 'I'], 'L')
- $: << Dir.getwd
- #==============================================================================
- # ■ AudioEx
- #------------------------------------------------------------------------------
- # 进行有关音乐,声音处理的扩展音频模块。
- #==============================================================================
- module AudioEx
- #--------------------------------------------------------------------------
- # ● 快速播放(集合打开、播放设备、设置音量、速度、淡入等操作)
- # device : 设备名称(标识)
- # filename : 文件路径
- # volume : 音量(0-1000)
- # speed : 速度(500-1500)
- # fade : 淡入(true | false)
- #--------------------------------------------------------------------------
- def self.quickPlay(device, filename, volume = 1000, speed = 1000, fade = 0)
- open(device, filename)
- play(device)
- setVolume(device, volume)
- setSpeed(device, speed)
- fade_in(device, fade, 0, volume)
- end
- #--------------------------------------------------------------------------
- # ● 暂停一个设备的播放(可淡出)
- # device : 设备名称(标识)
- # fade : 淡出时间(秒)
- #--------------------------------------------------------------------------
- def self.suspend(device, fade = 1)
- fade_out(device, fade, getVolume(device), 0, 1)
- end
- #--------------------------------------------------------------------------
- # ● 恢复一个设备的播放(可淡入)
- # device : 设备名称(标识)
- # fade : 淡入时间(秒)
- #--------------------------------------------------------------------------
- def self.continue(device, fade = 1)
- resume(device)
- fade_in(device, fade, 0, getVolume(device))
- end
- #--------------------------------------------------------------------------
- # ● 发送 open 命令,从文件打开一个设备
- # device : 设备名称(标识)
- # filename : 文件路径
- #--------------------------------------------------------------------------
- def self.open(device, filename)
- error = $mciSendString.call("open \"#{filename}\" type mpegvideo alias #{device} wait".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- set(device, "time format milliseconds")
- # 准备播放
- cue(device)
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 play 命令,播放设备
- # device : 设备名称(标识)
- # flags : 命令标记
- #--------------------------------------------------------------------------
- def self.play(device, flags = "")
- # 如果设备无法播放就抛出异常
- if capability(device, "can play") == "false"
- raise AudioExException, "设备无法播放。"
- end
- error = $mciSendString.call("play #{device} #{flags}".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- #--------------------------------------------------------------------------
- # ● 淡入一个设备(音量从低到高的渐变)
- # device : 设备名称(标识)
- # sec : 淡入的时间(秒,近似值)
- # from : 开始淡入的音量
- # to : 淡入后的音量
- #--------------------------------------------------------------------------
- def self.fade_in(device, sec = 1, from = 0, to = 1000)
- return if sec == 0
- # 开启调节音量的线程
- Thread.new {
- begin
- increment = to / sec / 10
- volume = from
- while volume < to
- setVolume(device, volume)
- volume += increment
- sleep 0.08
- end
- setVolume(device, to)
- rescue Exception => ex
- p "淡出时发生了异常:", ex
- end
- }
- end
- #--------------------------------------------------------------------------
- # ● 淡出一个设备(音量从高到低的渐变)
- # device : 设备名称(标识)
- # sec : 淡出的时间(秒,近似值)
- # from : 开始淡出的音量
- # to : 淡出后的音量
- # end_proc : 淡出结束后的操作
- # ※ 0 = 关闭, 1 = 暂停, 2 = 停止, 其它 = 无操作
- #--------------------------------------------------------------------------
- def self.fade_out(device, sec = 1, from = 1000, to = 0, end_proc = 0)
- # 开启调节音量的线程
- Thread.new {
- begin
- if sec != 0
- volume = from
- decrement = volume / sec / 10
- while volume > to
- setVolume(device, volume)
- volume -= decrement
- sleep 0.08
- end
- setVolume(device, to)
- end
- case end_proc
- when 0: close(device) # 淡出后关闭设备
- when 1: setVolume(device, from) # 将音量调整回淡出之前
- pause(device) # 淡出后暂停设备
- when 2: stop(device) # 淡出后停止设备
- end
- rescue Exception => ex
- p "淡出时发生了异常:", ex
- end
- }
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 pause 命令,暂停设备
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.pause(device)
- error = $mciSendString.call("pause #{device}".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 resume 命令,恢复设备
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.resume(device)
- error = $mciSendString.call("resume #{device}".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- #--------------------------------------------------------------------------
- # ● 向MCI 设备发送 seek 命令,移动当前位置指针
- # device : 设备名称(标识)
- # pos : 位置(毫秒)
- #--------------------------------------------------------------------------
- def self.seek(device, pos)
- error = $mciSendString.call(
- (case pos
- when -1
- "seek #{device} to end"
- when 0
- "seek #{device} to start"
- else
- "seek #{device} to #{pos}"
- end).to_unicode,
- 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- play(device)
- end
- #--------------------------------------------------------------------------
- # ● 设置 MCI 设备左右声道平均音量
- # device : 设备名称(标识)
- # volume : 音量(0-1000)
- #--------------------------------------------------------------------------
- def self.setVolume(device, volume)
- volume = [[volume, 0].max, 1000].min
- setAudio(device, "volume to #{volume}")
- end
- #--------------------------------------------------------------------------
- # ● 获取 MCI 设备左右声道平均音量
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.getVolume(device)
- return status(device, "volume").to_i
- end
- #--------------------------------------------------------------------------
- # ● 设置 MCI 设备播放速度
- # device : 设备名称(标识)
- # speed : 速度(500-1500)
- #--------------------------------------------------------------------------
- def self.setSpeed(device, speed)
- speed = [[speed, 500].max, 1500].min
- set(device, "speed #{speed}")
- end
- #--------------------------------------------------------------------------
- # ● 设置 MCI 设备暂停超时
- # device : 设备名称(标识)
- # time : 超时(毫秒)
- #--------------------------------------------------------------------------
- def self.timeout(device, time)
- set(device, "pause #{time}")
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 stop 命令,停止设备
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.stop(device)
- error = $mciSendString.call("stop #{device}".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 close 命令,关闭设备
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.close(device)
- error = $mciSendString.call("close #{device}".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- #--------------------------------------------------------------------------
- # ● 获取 MCI 设备当前位置(毫秒)
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.position(device)
- return status(device, "position").to_i
- end
- #--------------------------------------------------------------------------
- # ● 获取 MCI 设备媒体长度(毫秒)
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.length(device)
- return status(device, "length").to_i
- end
- #--------------------------------------------------------------------------
- # ● 获取 MCI 设备状态("not ready", "paused", "playing", and "stopped")
- # device : 设备名称(标识)
- #--------------------------------------------------------------------------
- def self.mode(device)
- return status(device, "mode")
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 status 命令,获取设备状态信息
- # device : 设备名称(标识)
- # flags : 命令标记
- #--------------------------------------------------------------------------
- def self.status(device, flags)
- buf = " " * 256
- error = $mciSendString.call("status #{device} #{flags}".to_unicode, buf, 256, 0)
- raise AudioExException.new(error) if error != 0
- buf.strip!
- # 返回 UTF-8 编码格式的设备状态信息
- return buf.to_UTF8
- end
- ###############################################################################
- # #
- # 以下函数在普通情况下不需要直接调用,仅供有兴者参考、试验。 #
- # 使用方法请自行参考 MSDN 有关 MCI Command String(命令字符串)的部分。 #
- # #
- ###############################################################################
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 setaudio 命令,设置音频相关信息
- # device : 设备名称(标识)
- # flags : 命令标记
- #--------------------------------------------------------------------------
- def self.setAudio(device, flags)
- error = $mciSendString.call("setaudio #{device} #{flags}".to_unicode,
- 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 cue 命令,准备播放
- # device : 设备名称(标识)
- # flags : 命令标记
- #--------------------------------------------------------------------------
- def self.cue(device)
- error = $mciSendString.call("cue #{device}".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 capability 命令,获取设备兼容性相关信息
- # device : 设备名称(标识)
- # flags : 命令标记
- #--------------------------------------------------------------------------
- def self.capability(device, flags)
- buf = " " * 256
- error = $mciSendString.call("capability #{device} #{flags}".to_unicode,
- buf, 256, 0)
- raise AudioExException.new(error) if error != 0
- buf.strip!
- return buf.to_UTF8
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 info 命令,获取设备信息
- # device : 设备名称(标识)
- # flags : 命令标记
- #--------------------------------------------------------------------------
- def self.info(device, flags)
- buf = " " * 256
- error = $mciSendString.call("info #{device} #{flags}".to_unicode,
- buf, 256, 0)
- raise AudioExException.new(error) if error != 0
- buf.strip!
- return buf.to_UTF8
- end
- #--------------------------------------------------------------------------
- # ● 向 MCI 设备发送 set 命令,设置设备相关属性
- # device : 设备名称(标识)
- # flags : 命令标记
- #--------------------------------------------------------------------------
- def self.set(device, flags)
- error = $mciSendString.call("set #{device} #{flags}".to_unicode, 0, 0, 0)
- raise AudioExException.new(error) if error != 0
- end
- end
- #==============================================================================
- # ■ AudioExException
- #------------------------------------------------------------------------------
- # 扩展音频模块异常类。
- #==============================================================================
- class AudioExException < Exception
- #--------------------------------------------------------------------------
- # ● 初始化异常类对象
- # code : MCI 错误代码
- #--------------------------------------------------------------------------
- def initialize(code)
- return super if code.class == String
- @msg = " " * 512
- # 返回 MCI 错误信息
- $mciGetErrorString.call(code, @msg, 256)
- @msg = @msg.to_UTF8
- end
- #--------------------------------------------------------------------------
- # ● 返回异常对象的字符串描述
- #--------------------------------------------------------------------------
- def to_s
- # 如果没有传递 MCI 错误代码的话就返回父类的错误信息
- return @msg if @msg
- return super
- end
- end
- #==============================================================================
- # ■ String
- #------------------------------------------------------------------------------
- # 字符串类。可处理任意长度的字节串。(追加编码转换的定义)
- #==============================================================================
- class String
- #--------------------------------------------------------------------------
- # ● 用来编码 Ruby 字符串、解码 unicode 的 两个 Windows API 函数
- #--------------------------------------------------------------------------
- @@MultiByteToWideChar = Win32API.new("kernel32", "MultiByteToWideChar", ['I', 'L', 'P', 'I', 'P', 'I'], 'I')
- @@WideCharToMultiByte = Win32API.new("kernel32", "WideCharToMultiByte", ['I', 'L', 'P', 'I', 'P', 'I', 'P', 'P'], 'I')
- #--------------------------------------------------------------------------
- # ● 返回将 Ruby UTF-8 字符串对象(本身)编码为 unicode 后的字符串
- #--------------------------------------------------------------------------
- def to_unicode
- # 65001: UTF-8 字符集编码(代码页)
- len = @@MultiByteToWideChar.call(65001, 0, self, -1, 0, 0) << 1
- buf =" " * len
- @@MultiByteToWideChar.call(65001, 0, self, -1, buf, len)
- return buf
- end
- #--------------------------------------------------------------------------
- # ● 返回将编码为 unicode 的字符串对象(本身)解码为 UTF-8 后的字符串
- #--------------------------------------------------------------------------
- def to_UTF8
- len = @@WideCharToMultiByte.call(65001, 0, self, -1, 0, 0, 0, 0)
- buf =" " * len
- @@WideCharToMultiByte.call(65001, 0, self, -1, buf, len, 0, 0)
- # 去掉 '\0' 字符串结束符(因为转换之后仅仅用于 Ruby 字符串)
- buf.slice!(-1, 1)
- return buf
- end
- end
- #==============================================================================
- # 本脚本来自www.66RPG.com,使用和转载请保留此信息
- # 作者:紫苏
- #==============================================================================
复制代码 |
|