加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

魔兽世界自动钓鱼系统的设计与实现 - 鱼上钩声音事件的提取(1)

(2013-04-17 16:10:33)
标签:

魔兽世界

系统设计与实现

python

分类: 编程实战

前面已经提到过在鱼漂下沉,也就是鱼上钩,这一事件发生的时间点用图像识别比较耗费资源,进而想用在钓鱼时上鱼上钩时会有声音这一点来获取事件发生的时间点(其实是图像识别我也不会,借口而已)。简单说来就是在系统输出的音频流中识别鱼上钩的声音事件发生的时间点。这一时间点就是是下一步提杆操作的基础。首先列出所要解决的所有问题:

  • 获取系统音频流输出
  • 声音事件的描述,也就量声音事件的表达,即特征
  • 声音事件在录音中的识别与在音频流中的识别

获取系统音频流输出

录制系统音频输出方法很简单,我开始想通过选择录音设备(立体声混音),这样就可以不干扰麦克风录音的功能。最终还是放弃了,直接将“立体声混音”这个设备设为默认录音设备,这样随便用一个录音软件都可以录下系统的声音输出了。联想到win7、win8还可以调节单个应用程序的输出,我最开始的最高计划是选择某一应用程序的音频输出,当然,这样就降低了后续处理的难度,没必要。

总之,在这一部分上浪费了大量时间,主要是看见了一篇中文的用python加DirectSound做语言处理的论文,然后就想办法借用Win32API来调用DirectSound,最终能够实现录制特定长度的音频文件(wav)但是却不知道怎么分段录制,而一段一段录制对后面的音频流的实时处理非常重要。造成困难的主要原因还是python的DirectSound的例子太少,文档过于简单,WinPython给出的文档简直没法看!既然都是做系统调用,任意语言的代码应该是类似的,直接参考C#的例子又发现对应起来还是有困难的……另外,SourceForge上面其实是有一个DirectPython的开源项目,但是打不开……没办法,折腾到最后只好放弃DirectSound方案。下面是调用DirectSound的录制wav文件的一段代码,我从py2.6的代码上修改为了py3k的:

# -*- coding: utf-8 -*-
"""
Created on Thu Apr 11 18:00:43 2013

@author: du00
"""

import pywintypes
import struct
import win32event
import win32com.directsound.directsound as ds

def wav_header_pack(wfx, datasize):
   return struct.pack('<4sl4s4slhhllhh4sl', b'RIFF', 36 + datasize,
              b'WAVE', b'fmt ', 16,
              wfx.wFormatTag, wfx.nChannels, wfx.nSamplesPerSec,
              wfx.nAvgBytesPerSec, wfx.nBlockAlign,
              wfx.wBitsPerSample, b'data', datasize);

d = ds.DirectSoundCaptureCreate(None, None)

sdesc = ds.DSCBUFFERDESC()
sdesc.dwBufferBytes = 176400 # 1 seconds
sdesc.lpwfxFormat = pywintypes.WAVEFORMATEX()
sdesc.lpwfxFormat.wFormatTag = pywintypes.WAVE_FORMAT_PCM
sdesc.lpwfxFormat.nChannels = 2
sdesc.lpwfxFormat.nSamplesPerSec = 44100
sdesc.lpwfxFormat.nAvgBytesPerSec = 176400
sdesc.lpwfxFormat.nBlockAlign = 4
sdesc.lpwfxFormat.wBitsPerSample = 16

buffer = d.CreateCaptureBuffer(sdesc)

event = win32event.CreateEvent(None, 0, 0, None)

notify = buffer.QueryInterface(ds.IID_IDirectSoundNotify)
notify.SetNotificationPositions((ds.DSBPN_OFFSETSTOP, event))

buffer.Start(0)
win32event.WaitForSingleObject(event, -1)
# in real life, more, smaller buffers should be retrieved
f = open('recording.wav', 'wb')
f.write(wav_header_pack(sdesc.lpwfxFormat, 176400))
data = buffer.Update(0, 176400)
f.write(data)
f.close
()

 

后来才发现python有封装PortAudio的pyAudio包,真心简单好用,而且还是跨平台的,免除了学习Win32AP的痛苦,网站上提供的例子简单明了,一看就会!录制音频的代码如下:

 

# -*- coding: utf-8 -*-
"""
Created on Fri Apr 12 05:14:56 2013

@author: du00
"""
"""PyAudio example: Record a few seconds of audio and save to a WAVE file."""

import pyaudio
import wave

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 20
WAVE_OUTPUT_FILENAME = "fish_with_bg.wav"

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
               channels=CHANNELS,
               rate=RATE,
               input=True,
               frames_per_buffer=CHUNK)

print("* recording")

frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
   data = stream.read(CHUNK)
   frames.append(data)

print("* done recording")

stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close
()

简单了不少吧!不觉得?http://www/uc/myshow/blog/misc/gif/E___7393ZH00SIGG.gif鱼上钩声音事件的提取(1)" TITLE="魔兽世界自动钓鱼系统的设计与实现 鱼上钩声音事件的提取(1)" />

 

虽然终极目的是在音频播放(音频流)中实时地识别,但是做这些录制工作还是非常有必要的,录制的音频可以从宏观地把握声音事件与背景、其它声音事件的区别,有助于理解声音事件。下一部分的所有工作都是在存在录制的音频文件基础上展开的。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有