Python 在数据流中查找一系列模式
(这是用Python编写的,代码会很棒,但我主要对算法感兴趣。) 我正在监视一个音频流(PyAudio)并寻找一系列5个POP(见底部的可视化)。我正在对流进行读取()并获取刚刚读取的块的RMS值(类似于)。我的问题是,我不是在寻找一个单一的事件,而是一系列事件(pop),它们具有一些特征,但不像我希望的那么布尔。检测这五种持久性有机污染物最直接(最有效)的方法是什么 RMS函数为我提供了如下流:Python 在数据流中查找一系列模式,python,algorithm,audio,stream,Python,Algorithm,Audio,Stream,(这是用Python编写的,代码会很棒,但我主要对算法感兴趣。) 我正在监视一个音频流(PyAudio)并寻找一系列5个POP(见底部的可视化)。我正在对流进行读取()并获取刚刚读取的块的RMS值(类似于)。我的问题是,我不是在寻找一个单一的事件,而是一系列事件(pop),它们具有一些特征,但不像我希望的那么布尔。检测这五种持久性有机污染物最直接(最有效)的方法是什么 RMS函数为我提供了如下流: 0.000580998485254, 0.00045098391298, 0.0075143644
0.000580998485254, 0.00045098391298, 0.00751436443973, 0.002733730043, 0.00160775708652, 0.000847808804511
如果我为您搜索(类似的流),它看起来会更有用:
你可以在第3项中看到弹出,大概是在第4项中它安静下来的时候,也许尾端是在第5项的一小部分
我想连续检测其中的5个
我天真的做法是:
a) 定义pop是什么:块的RMS超过.002。至少2个区块,但不超过4个区块。以沉默开始,以沉默结束
此外,我很想定义什么是沉默(忽略不太响亮但不太沉默的块,但我不确定这比将“pop”视为布尔值更有意义)
b) 然后有一个状态机,它跟踪一组变量,并有一组if语句。比如:
while True:
is_pop = isRMSAmplitudeLoudEnoughToBeAPop(stream.read())
if is_pop:
if state == 'pop':
#continuation of a pop (or maybe this continuation means
#that it's too long to be a pop
if num_pop_blocks <= MAX_POP_RECORDS:
num_pop_blocks += 1
else:
# too long to be a pop
state = 'waiting'
num_sequential_pops = 0
else if state == 'silence':
#possible beginning of a pop
state = 'pop'
num_pop_blocks += 1
num_silence_blocks = 0
else:
#silence
if state = 'pop':
#we just transitioned from pop to silence
num_sequential_pops += 1
if num_sequential_pops == 5:
# we did it
state = 'waiting'
num_sequential_pops = 0
num_silence_blocks = 0
fivePopsCallback()
else if state = 'silence':
if num_silence_blocks >= MAX_SILENCE_BLOCKS:
#now we're just waiting
state = 'waiting'
num_silence_blocks = 0
num_sequential_pops = 0
为True时:
is_pop=isrmSamplicationNoughtObeapop(stream.read())
如果是流行音乐:
如果state==“pop”:
#流行音乐的延续(或者这个延续意味着
#它太长了,不能成为流行音乐
如果num_pop_blocks=MAX_SILENCE_blocks:
#现在我们只是在等待
状态='等待'
num_silence_blocks=0
num_sequential_pops=0
这段代码一点也不完整(可能有一两个bug),但说明了我的思路。它肯定比我希望的要复杂,这就是我征求建议的原因
您可能需要计算最后p个点的值,其中p~=4,并将结果与原始输入数据一起绘制 然后,你可以使用平滑平均值的最大值作为一个pop。定义一个最大间隔,在该间隔内可以看到五个pop,这可能是你的目标 调整p以获得最佳拟合
如果还没有Python模块,我不会感到惊讶,但我还没有看过。在我看来,这是一种幼稚的方法,有一个正在进行的循环和一些变量需要维护并转换到新状态。不过,在完成之后,我想到,我应该探索hotword检测,因为5 conse可爱的点击基本上是一个热门词。它们有一个模式,我必须寻找 无论如何,这是我的代码:
POP_MIN_MS = 50
POP_MAX_MS = 150
POP_GAP_MIN_MS = 50
POP_GAP_MAX_MS = 200
POP_BORDER_MIN_MS = 500
assert POP_BORDER_MIN_MS > POP_GAP_MAX_MS
POP_RMS_THRESHOLD_MIN = 100
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100 # Sampling Rate -- frames per second
INPUT_BLOCK_TIME_MS = 50
INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME_MS/1000)
POP_MIN_BLOCKS = POP_MIN_MS / INPUT_BLOCK_TIME_MS
POP_MAX_BLOCKS = POP_MAX_MS / INPUT_BLOCK_TIME_MS
POP_GAP_MIN_BLOCKS = POP_GAP_MIN_MS / INPUT_BLOCK_TIME_MS
POP_GAP_MAX_BLOCKS = POP_GAP_MAX_MS / INPUT_BLOCK_TIME_MS
POP_BORDER_MIN_BLOCKS = POP_BORDER_MIN_MS / INPUT_BLOCK_TIME_MS
def listen(self):
pops = 0
sequential_loud_blocks = 0
sequential_notloud_blocks = 0
stream = self.pa.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=INPUT_FRAMES_PER_BLOCK
)
states = {
'PENDING': 1,
'POPPING': 2,
'ENDING': 3,
}
state = states['PENDING']
while True:
amp = audioop.rms(stream.read(INPUT_FRAMES_PER_BLOCK), 2)
is_loud = (amp >= POP_RMS_THRESHOLD_MIN)
if state == states['PENDING']:
if is_loud:
# Only switch to POPPING if it's been quiet for at least the border
# period. Otherwise stay in PENDING.
if sequential_notloud_blocks >= POP_BORDER_MIN_BLOCKS:
state = states['POPPING']
sequential_loud_blocks = 1
# If it's now loud then reset the # of notloud blocks
sequential_notloud_blocks = 0
else:
sequential_notloud_blocks += 1
elif state == states['POPPING']:
if is_loud:
sequential_loud_blocks += 1
# TODO: Is this necessary?
sequential_notloud_blocks = 0
if sequential_loud_blocks > POP_MAX_BLOCKS:
# it's been loud for too long; this isn't a pop
state = states['PENDING']
pops = 0
#print "loud too long"
# since it has been loud and remains loud then no reason to reset
# the notloud_blocks count
else:
# not loud
if sequential_loud_blocks:
# just transitioned from loud. was that a pop?
# we know it wasn't too long, or we would have transitioned to
# PENDING during the pop
if sequential_loud_blocks < POP_MIN_BLOCKS:
# wasn't long enough
# go to PENDING
state = states['PENDING']
pops = 0
#print "not loud long enough"
else:
# just right
pops += 1
logging.debug("POP #%s", pops)
sequential_loud_blocks = 0
sequential_notloud_blocks += 1
else:
# it has been quiet. and it's still quiet
sequential_notloud_blocks += 1
if sequential_notloud_blocks > POP_GAP_MAX_BLOCKS:
# it was quiet for too long
# we're no longer popping, but we don't know if this is the
# border at the end
state = states['ENDING']
elif state == states['ENDING']:
if is_loud:
# a loud block before the required border gap. reset
# since there wasn't a gap, this couldn't be a valid pop anyways
# so just go back to PENDING and let it monitor for the border
sequential_loud_blocks = 1
sequential_notloud_blocks = 0
pops = 0
state = states['PENDING']
else:
sequential_notloud_blocks += 1
# Is the border time (500 ms right now) enough of a delay?
if sequential_notloud_blocks >= POP_BORDER_MIN_BLOCKS:
# that's a bingo!
if pops == 5:
stream.stop_stream()
# assume that starting now the channel is not silent
start_time = time.time()
print ">>>>> 5 POPS"
elapsed = time.time() - start_time
#time.time() may return fractions of a second, which is ideal
stream.start_stream()
# do whateve we need to do
state = states['PENDING']
pops = 0
POP_MIN_MS=50
POP_MAX_MS=150
POP_GAP_MIN_MS=50
波普_间隙_最大值_MS=200
POP_BORDER_MIN_MS=500
断言POP_BORDER_MIN_MS>POP_GAP_MAX_MS
POP\u RMS\u阈值\u最小值=100
格式=pyaudio.paInt16
通道=2
速率=44100#采样速率--每秒帧数
输入\块\时间\毫秒=50
每块输入帧=int(速率*输入块时间=MS/1000)
POP_MIN_BLOCKS=POP_MIN_MS/输入_BLOCK_TIME_MS
POP_MAX_BLOCKS=POP_MAX_MS/输入_BLOCK_TIME_MS
POP_GAP_MIN_BLOCKS=POP_GAP_MIN_MS/输入_BLOCK_TIME_MS
POP_GAP_MAX_BLOCKS=POP_GAP_MAX_MS/输入_BLOCK_TIME_MS
POP_BORDER_MIN_BLOCKS=POP_BORDER_MIN_MS/输入_BLOCK_TIME_MS
def监听(self):
pops=0
顺序块=0
顺序块=0
流=self.pa.open(
格式=格式,
频道=频道,
比率=比率,
输入=真,
每个缓冲区的帧数=每个块的输入帧数
)
国家={
“待定”:1,
"砰砰":2,,
"结局":3,,
}
状态=状态['PENDING']
尽管如此:
amp=audioop.rms(流读(每个块输入帧),2)
是否响亮=(amp>=砰砰声\u RMS\u阈值\u MIN)
如果state==states['PENDING']:
如果声音很大:
#只有在至少在边境保持安静的情况下才切换到弹出
#期间。否则待决。
如果连续的\u notloud\u块>=POP\u BORDER\u MIN\u块:
状态=状态['POPPING']
顺序块=1
#如果现在是响亮的,则重置notloud块的#
顺序块=0
其他:
顺序块+=1
elif state==状态['POPPING']:
如果声音很大:
顺序块+=1
#托多:这有必要吗?
顺序块=0
如果顺序块>弹出块>最大块:
#声音太大太久了,这不是流行音乐
状态=状态['PENDING']
pops=0
#打印“太长”
#因为它一直很响并且一直很响,所以没有理由重置
#notloud_积木计数
其他:
#不大声
如果连续_loud_块:
#刚从大声转换过来,那是流行音乐吗?
#我们知道时间不会太长,否则我们会过渡到
#在pop期间挂起
如果连续的\u loud\u块弹出块>最大块:
#安静了太久
#我们不再蹦蹦跳跳了,但我们不知道这是不是
#尽头的边界
状态=状态[“结束”]
elif state==状态['ENDING']:
如果是_
POP_MIN_MS = 50
POP_MAX_MS = 150
POP_GAP_MIN_MS = 50
POP_GAP_MAX_MS = 200
POP_BORDER_MIN_MS = 500
assert POP_BORDER_MIN_MS > POP_GAP_MAX_MS
POP_RMS_THRESHOLD_MIN = 100
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100 # Sampling Rate -- frames per second
INPUT_BLOCK_TIME_MS = 50
INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME_MS/1000)
POP_MIN_BLOCKS = POP_MIN_MS / INPUT_BLOCK_TIME_MS
POP_MAX_BLOCKS = POP_MAX_MS / INPUT_BLOCK_TIME_MS
POP_GAP_MIN_BLOCKS = POP_GAP_MIN_MS / INPUT_BLOCK_TIME_MS
POP_GAP_MAX_BLOCKS = POP_GAP_MAX_MS / INPUT_BLOCK_TIME_MS
POP_BORDER_MIN_BLOCKS = POP_BORDER_MIN_MS / INPUT_BLOCK_TIME_MS
def listen(self):
pops = 0
sequential_loud_blocks = 0
sequential_notloud_blocks = 0
stream = self.pa.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=INPUT_FRAMES_PER_BLOCK
)
states = {
'PENDING': 1,
'POPPING': 2,
'ENDING': 3,
}
state = states['PENDING']
while True:
amp = audioop.rms(stream.read(INPUT_FRAMES_PER_BLOCK), 2)
is_loud = (amp >= POP_RMS_THRESHOLD_MIN)
if state == states['PENDING']:
if is_loud:
# Only switch to POPPING if it's been quiet for at least the border
# period. Otherwise stay in PENDING.
if sequential_notloud_blocks >= POP_BORDER_MIN_BLOCKS:
state = states['POPPING']
sequential_loud_blocks = 1
# If it's now loud then reset the # of notloud blocks
sequential_notloud_blocks = 0
else:
sequential_notloud_blocks += 1
elif state == states['POPPING']:
if is_loud:
sequential_loud_blocks += 1
# TODO: Is this necessary?
sequential_notloud_blocks = 0
if sequential_loud_blocks > POP_MAX_BLOCKS:
# it's been loud for too long; this isn't a pop
state = states['PENDING']
pops = 0
#print "loud too long"
# since it has been loud and remains loud then no reason to reset
# the notloud_blocks count
else:
# not loud
if sequential_loud_blocks:
# just transitioned from loud. was that a pop?
# we know it wasn't too long, or we would have transitioned to
# PENDING during the pop
if sequential_loud_blocks < POP_MIN_BLOCKS:
# wasn't long enough
# go to PENDING
state = states['PENDING']
pops = 0
#print "not loud long enough"
else:
# just right
pops += 1
logging.debug("POP #%s", pops)
sequential_loud_blocks = 0
sequential_notloud_blocks += 1
else:
# it has been quiet. and it's still quiet
sequential_notloud_blocks += 1
if sequential_notloud_blocks > POP_GAP_MAX_BLOCKS:
# it was quiet for too long
# we're no longer popping, but we don't know if this is the
# border at the end
state = states['ENDING']
elif state == states['ENDING']:
if is_loud:
# a loud block before the required border gap. reset
# since there wasn't a gap, this couldn't be a valid pop anyways
# so just go back to PENDING and let it monitor for the border
sequential_loud_blocks = 1
sequential_notloud_blocks = 0
pops = 0
state = states['PENDING']
else:
sequential_notloud_blocks += 1
# Is the border time (500 ms right now) enough of a delay?
if sequential_notloud_blocks >= POP_BORDER_MIN_BLOCKS:
# that's a bingo!
if pops == 5:
stream.stop_stream()
# assume that starting now the channel is not silent
start_time = time.time()
print ">>>>> 5 POPS"
elapsed = time.time() - start_time
#time.time() may return fractions of a second, which is ideal
stream.start_stream()
# do whateve we need to do
state = states['PENDING']
pops = 0