[警告:那些不能忍受EDM / dubstep的人,哦,关于这个博客,我对您有不好的消息]
Dubstep的歌曲通常被批评为听起来非常计算机生成,并且往往过于激进/“数字化”,以致于很多人无法欣赏。人们开玩笑说他们听起来好像有人在增加现代噪音的过程中增加了低音和鼓声并不少见。
对于某些曲目,这比其他更真实。毕竟,这是一种类型,具有更激进的解释和更轻松的解释。
但这让我思考,将机器可读数据实际嵌入到dubstep轨道中要花多大的精力,同时还要确保人类也能欣赏到声音……
让我们来跟踪一下,这是Skrillex的构建和完善-正确的方法:
如果您可以忍受,那是个好消息!您可能会满意以下所有内容。
上面是嵌入的歌曲的频谱图,因此您可以更好地了解歌曲中所有频段的状况。
如图所示,有很多频谱可以隐藏数据,但是它存在于高频段,音频压缩可能会将其删除。 那么,如果我们在低音线的频段上看起来较低,该怎么办? 这是0-100hz的声音(您有更多机会在头戴式/耳塞式手机上收听): 如果我们回到原始位置并移除100hz频段及以下频段,听起来像是: $ sox orig.wav onlyhigher.wav sinc -b 0 0.2k-22k $ cp onlyhigher.wav tmp.wav $#做第二轮以确保下带确实为空$ sox tmp.wav onlyhigher.wav sinc -b 0 0.2k-22k $ rm tmp.wav 注意:如果您调高音量以收听最后一个音量,则需要立即还原。 如果您像我一样,并且数学背景很差,那么每次您看到这样的事情: 不幸的是,这几乎完全是DSP世界。 但是,考虑到我在此过程中遇到的困难,我将尝试描述没有任何花哨符号的情况。
首先,这基本上是最基本的形式。向扬声器发出信号,告知扬声器应处于什么位置。范围从1(始终)到-1(始终)。在这些位置上移动会排空空气并发出声音!
我的想法是,如果像这样将波形移动到上端:
这意味着我们现在可以观察到两种状态!最好的部分是(至少我的)人耳看不到这种区别。
包mainimport(" encoding / binary"" flag"" log"" os")func main(){ins:= flag.String( " input",/#34.in/f64.data"、"")outs:= flag.String(" out",& #34; ./ out.f64.data&#34 ;,"")flag.Parse()inf,err:= os.OpenFile(* ins,os.O_RDONLY,0644)如果err!= nil {log.Fatalf("无法打开输出文件%s&#34 ;, err.Error())} outf,err:= os.OpenFile(* outs,os.O_CREATE | os.O_WRONLY | os。 O_TRUNC,0644)如果err!= nil {log.Fatalf("无法打开输出文件%s&#34 ;, err.Error())} Symbolrate:= 11000 SamplesUntilChange:= Symbolrate UpperFlip:= false位:对于{var raws float64 err == 0,如果err!= nil {log.Printf(" Leave%s&#34 ;, err.Error())== binary.Read(inf,binary.LittleEndian,& raws) )break} //首先,获得一个上空翻转范数:=(原始+ 1)/ 2 SamplesUntilChange--如果SamplesUntilChange == 0 {UpperFlip =!UpperFlip SamplesUntilChange = Symbolrate bits ++}如果!UpperFlip {normie =范数* -1}如果SamplesUntilChange< 1000 {dest:= normie * -1 normie = lerp(dest,normie,float64(SamplesUntilChange)/1000.0)} binary.Write(outf,binary.LittleEndian,normie)} log.Printf("用%d结尾位/%d个字节&#34 ;,位,位/ 8)} func lerp(a,b,n float64)float64 {return(1-n)* a + n * b}
在这一点上,我们可以重新组合输出的低音线文件和较高的频率,并查看它是否可以播放:
$ sox orig.wav onlylower.wav sinc 0k-0.1k $ sox orig.wav onlyhigher.wav sinc 0.1k-22k $ ffmpeg -i onlylower.wav -f f64le -ar 44100 -ac 1 -y in.f64.data $ ./ASK-dubstep-输入in.f64.data $ ffmpeg -f f64le -ar 44100 -ac 1 -i out.f64.data -y编码-bass.wav $ sox -m编码-bass.wav onlyhigher.wav编码-bassline.wav
我可能是错的,但据我所知,这是一种幅移键控。
为了使其编码我们自己的数据,我们可以使用一个简单的库来帮助我们以一种易于使用的好方法读取位,然后将它们包含在音频中:
包mainimport(" encoding / binary"" flag"" log"" os"" strings"" github .com / dgryski / go-bitstream")func main(){ins:= flag.String(" input&#34 ;," ./ in.f64.data&#34 ;,&# 34;")outs:= flag.String(" out&#34 ;," ./ out.f64.data&#34 ;,"")编码目标:= flag.String(" data&#34 ;," Hello World!&#34 ;,"")flag.Parse()inf,err:= os.OpenFile(* ins ,os.O_RDONLY,0644)如果err!= nil {log.Fatalf("无法打开输出文件%s&#34 ;, err.Error())} outf,err:= os.OpenFile(* outs ,os.O_CREATE | os.O_WRONLY | os.O_TRUNC,0644)如果err!= nil {log.Fatalf("无法打开输出文件%s&#34 ;, err.Error())} sr:= strings.NewReader(* encodetarget)bitreader:= bitstream.NewReader(sr)Symbolrate:= 5500 SamplesUntilChange:= Symbolrate UpperFlip:= false bits:= 0 nextbit:= false for {var raws float64 err:= binary.Read(inf, binary.LittleEndian,& raws)如果err!= nil {log.Printf("离开%s&#34 ;, err.Error())break} //首先,获得上翻转范数:=(原始+ 1)/ 2 SamplesUntilChange--如果SamplesUntilChange == 0 {UpperFlip = nextbit b,_:= bitreader.ReadBit()nextbit = bool(b)SamplesUntilChange = Symbolrate bits ++}如果!UpperFlip {normie = normie * -1}如果SamplesUntilChange< 1000&& UpperFlip!= nextbit {dest:= normie * -1 normie = lerp(dest,normie,float64(SamplesUntilChange)/1000.0)} binary.Write(outf,binary.LittleEndian,normie)} log.Printf("完成带有%d位/%d字节&#34 ;,位,位/ 8)} func lerp(a,b,n float64)float64 {return(1-n)* a + n * b}
包mainimport(" encoding / binary"" flag"" fmt"" log"" os"位流" github.com/dgryski/go-bitstream")func main(){ins:= flag.String(" input&#34 ;," ./ in.f64.data&#34 ;,& #34;")symbolrate:= flag.Int(< srate&#34 ;, 5500,"示例中的符号率")flag.Parse()inf,err:= os。 OpenFile(* ins,os.O_RDONLY,0644)如果err!= nil {log.Fatalf("无法打开输出文件%s&#34 ;, err.Error())} bw:= bitstream.NewWriter( os.Stdout)bw.WriteBit(bitstream.Bit(false))SamplesUntilChange:= *符号位:= 1 negscore:= 0对于{var raws float64 err:= binary.Read(inf,binary.LittleEndian,& raws)如果err!= nil {fmt.Print(" \ n")log.Printf("离开%s&#34 ;, err.Error())break} isNeg:= raws< 0如果isNeg {negscore ++} SamplesUntilChange-如果SamplesUntilChange == 0 {rsp:= negscore< (*符号/ 2),如果位!= 1 {bw.WriteBit(bitstream.Bit(rsp))} negscore = 0 SamplesUntilChange = *符号位++}} log.Printf("用%d位/%d结尾字节,位,位/ 8)}
该解码器的工作原理是计算“帧”中有多少个样本为负数,并以此为基础声明是1位还是0位。
最后,下面是一个现场演示,演示了如何使用另一首歌曲(“平滑-无处”):
我一如既往地在github上推送了代码(尽管它实际上并不是处于可用状态):https://github.com/benjojo/dubstep-data
如果您喜欢这个,您将很高兴得知我将在接下来的10周内前往纽约的Recurse中心!意味着您可以关注我的Twitter或RSS,以跟上我将要做的其他愚蠢的事情!