读取wav并fft

19 篇文章 0 订阅

一、 一次读一个文件,文件是单通道的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查询处理结果。

(二)结果

发生了错误如下:

  1. source每读取1024个字节就传给consumer通道
    source的源码为:
。。。
fmt.Printf("source第%v次返回的数组第3个数字%v\n",num,ret_arr1[2])
consumer<-api.NewDefaultSourceTuple(s.pattern,nil)
  1. 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
}
  1. 结果为:
    文件长度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文件有些不同:

  1. 一是通道,demo是3通道,xlsx文件写的是2-5通道,不知道实际应用中是固定3通道还是2-5通道
  2. 二是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转为数组?不知道

还要解决的问题

难题

  1. 为什么function只能一次接收一个文件的数组,而一次接收1024个数据就会乱序,遗漏和重复
  2. 这样如果wav文件很多,function处理起来会不会也乱序,遗漏和重复呢

待解决

  1. sink可以写入不同的文件。
  2. 尝试一次读取多个文件(意义不大)
  3. function一次处理1024个数据
  4. 3通道/2-5通道 时如何处理
  5. 如果新区信息是必要的,怎么获得

扩展:

  1. fft是否精度有问题(不会)
  2. 是否需要相频,时域虚数等信息

总之,三个插件,function基本不用改了,另外两个可以修改。当然,多通道时可能也要改function

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值