文章目录
一、 一次读一个文件,文件是单通道的windows文件
(一)目标文件信息:
一秒左右的标准wav文件,信息如下:
File size : 84744 bytes
Canonical format : true
Audio format : 1
Number of channels: 1
Sampling rate : 44100 Hz
Sample size : 16 bits
Number of samples : 42364
Sound size : 84728 bytes
Sound duration : 0s
(二)kuiper处理原理
source:读取wav文件,读到返回文件数组,并开始读取下一个文件。没读取到就等3秒再读
function:对接收到的数组fft,每接收一个就fft一次,返回fft后的数组
sink:得到一个数组就以json格式追加写入到目标文件中,这里是追加,而没有把每个数组分别存储到不同的文件,后面也可以改成这样
总结:读取wav,fft,写入txt
(三)实操流程
1. 解析wav
安装了一个可以读取wav文件的库,修改以后可以读取标准wav文件,返回文件本身的信息和文件包含的信息。前者包括通道数,bits等,后者是一系列的数组。
2. sources
source读取wav文件,读到返回文件数组,并开始读取下一个文件。没读取到就等3秒再读
核心代码
go func(exeCtx api.StreamContext) {
for{
select{
case s.filepath = <-c:
fmt.Println(s.filepath)
row_arr:=Main1(s.filepath)
println("len(row_arr)=",len(row_arr))
for i:=0;i<len(row_arr);i++{//把读取到的数组返回,每次返回fft_len
ret:=row_arr[i]
s.pattern["value"]=ret
consumer<-api.NewDefaultSourceTuple(s.pattern,nil)
}
case <-exeCtx.Done():
return
}
}
}(exeCtx)
go func() {
//此线程不断查询新的文件,有就把文件名传入通道c,并查询下一个文件,没有就等待3s
id:=0
filename := getfilepath(id)
for{
_, err := os.Stat(filename)
if(err==nil){//如果存在目标文件,即未读取的文件
time.Sleep(3000)
c<-filename
id++
filename = getfilepath(id)
fmt.Println(filename)
}else{
time.Sleep(3000)//等待3秒
//fmt.Println("waiting file")
}
}
}()
3. function
function简单,主要是fft
核心代码
func (f *row2fft) Exec(args []interface{}) (interface{}, bool) {
m :=args[0].([]float64)
a:=fftabs(m)
fmt.Printf("变换前第3个%v,变换后第3个%v\n",m[2],a[2])
return a,true
}
4. sinks
把传来的[]byte ,以json格式追加写入txt文件。
核心代码:
func (m *wavSink) Collect(ctx api.StreamContext, item interface{}) error {
logger := ctx.GetLogger()
if v, ok := item.([]byte); ok {
var str_content = string(v)
fd_content:=strings.Join([]string{str_content,"\n"},"")
buf:=[]byte(fd_content)
m.fd.Write(buf)
println("received")
println(len(fd_content))
logger.Debug("wavSink sink receive data")
} else {
logger.Debug("wavSink sink receive non byte data")
println("item isnot []byte")
}
println("collect func finished")
return nil
}
5. kuiper-stream
bin/cli create stream streamReadWav '(value array(float)) with (datasource="topicReadWav",TYPE="readWav")'
//流名称 (sources中map的键:value 类型) with (主题, sources插件名)
6. kuiper-rule
{
"id": "ruleWavSink",
"sql": "SELECT row2fft(value) from streamReadWav",
"actions": [
{
"log":{},
"wavSink": {}
}
]
}
(四)结果:
成功,对比使用go语言直接读取并fft得到的数组,一样。
得到的txt数组大小 800k。主要是浮点数比较长,存储起来比较费空间。
二、一个文件分多次读, 一次读1024个数据,文件是单通道的windews文件
(一)和上一个实验的区别
function相同,sink没有,直接查询的,所以可以认为这两个都相同,只有source不同
source
只有下面的地方不同
go func(exeCtx api.StreamContext) {
for{
select{
case s.filepath = <-c:
num :=1
fmt.Println(s.filepath)
row_arr:=Main1(s.filepath)
println("len(row_arr)=",len(row_arr))
ret_arr1:=make([]interface{},s.fft_len)
for i:=0;i<len(row_arr)-s.fft_len;i=i+s.fft_len{//把读取到的数组返回,每次返回fft_len
for j:=0;j<s.fft_len;j++{
ret_arr1[j]=row_arr[i+j]
}
s.pattern["value"]=ret_arr1
fmt.Printf("source第%v次返回的数组第3个数字%v\n",num,ret_arr1[2])
num++
//等待小数组放进去
consumer<-api.NewDefaultSourceTuple(s.pattern,nil)
}
case <-exeCtx.Done():
return
}
}
}(exeCtx)
想要source读取一个文件,得到一个大数组,把大数组切割,每1024位切割出来,返回到通道;
同时function每接收到一个数组,就fft,在kuiper查询处理结果。
(二)结果
发生了错误如下:
- source每读取1024个字节就传给consumer通道
source的源码为:
。。。
fmt.Printf("source第%v次返回的数组第3个数字%v\n",num,ret_arr1[2])
consumer<-api.NewDefaultSourceTuple(s.pattern,nil)
- function每接收到一个数组就进行fft:
源码为
func (f *row2fft) Exec(args []interface{}) (interface{}, bool) {
m :=args[0].([]float64)
a:=fftabs(m)
fmt.Printf("变换前第3个%v,变换后第3个%v\n",m[2],a[2])
return a,true
}
- 结果为:
文件长度42364,每次1024,舍弃后面的部分,可以得到41个长1024的数组。
souece源码中fmt.Printf("source第%v次返回的数组第3个数字%v\n",num,ret_arr1[2])
语句按顺序执行了,依次打印输出0,-6,29.。。
和预期的相同,说明前面部分没有错误。
function打印出的顺序有误,而且有的数组重复打印了,有的没打印,多次运行打印的东西还不相同;不过总共打印次数居然和预期一样是41,说明刚好接收到了41个数组。
三、反思
虽然source一次读取整个一个文档并返回可以成功,但是source一次返回1024个就会导致function乱序。不知道是什么原因。
我不由得反思,source一次读取一个文档并返回,如果文档较多,比如几十个,function处理的顺序会变吗,顺序变化还影响不大,假如也出现 有的处理了多次,有的不处理,那就很麻烦了。
四、读取demo文件,一次读取整个文件
(一)demo文件解析
1. demo文件包含的信息
和普通wav文件类似,但是demo和普通的wav文件有些不同:
- 一是通道,demo是3通道,xlsx文件写的是2-5通道,不知道实际应用中是固定3通道还是2-5通道
- 二是demo包含一些新的信息,我找的wav解析包解析不出来这些信息,如果这些信息重要,还得自己写解析函数,如果不重要或者说所有的wav文件信息相同,就没必要解析了,直接用notepad打开wav文件可以看到这些信息
这些信息包括:
44-299 256字节的 UTF-8 设备名称
300(13CH)-307 8字节 UINT64 时间戳
308-371 64字节的 UTF-8通道名称1:VIB1_1
372(174H)-435 64字节的 UTF-8 单元名称1: m/s^2
436(1B4)-439 浮点放大1 :@u<== 00 40 75 3C ==1,014,317,056
440-443 浮动偏移1 : 0
2. 使用go程序解析demo
使用go程序,对整个demo文件解析,选取其中的一个通道,读取到的数据如下:
这个图横坐标太多了,多以全红。绘制通道1、原信号的前1024位
进行fft变换,得到的结果如下。
感觉fft变换后的图片中没有包含很多信息,而且比较乱,大概在4 700 000HZ、6 000 000HZ、 500 000HZ频率下有较多数据。
如果只选择1024位进行fft,结果如下:
感觉信息更少
另外两个通道的图类似。
解决了的问题
kuiper server报错:
interface conversion: interface {} is []float64, not []interface {}
因为source的open方法需要返回一个[]interface {}的接口类型,而我返回的是[]float64,所以报错。
解决方法
创建一个[]interface {},把[]float64中的元素一一赋值给[]interface {}的元素。
sink判断输入类型
虽然function返回流 []float64,但是sink断言输入类型位 []float64 时会报错,要改为断言为 []byte。然后用string(bu),转为字符串。
但是怎么由[]byte转为数组?不知道
还要解决的问题
难题
- 为什么function只能一次接收一个文件的数组,而一次接收1024个数据就会乱序,遗漏和重复
- 这样如果wav文件很多,function处理起来会不会也乱序,遗漏和重复呢
待解决
- sink可以写入不同的文件。
- 尝试一次读取多个文件(意义不大)
- function一次处理1024个数据
- 3通道/2-5通道 时如何处理
- 如果新区信息是必要的,怎么获得
扩展:
- fft是否精度有问题(不会)
- 是否需要相频,时域虚数等信息
总之,三个插件,function基本不用改了,另外两个可以修改。当然,多通道时可能也要改function