提问

脑洞的一种提取音乐主旋律的方法

DIY专区  / 倒序浏览   © 著作权归作者本人所有

#楼主# 2018-1-20

跳转到指定楼层

前些日子没事情干,又想到TC那个方波音乐的痛苦与现在新的、不需要方波音乐的灭弧、放音乐的那个效果......顿时觉得有点头大。
1.想法
来看新的灭弧放普通音乐的效果——噗噗噗喀喀喀的。这因为普通音乐的频率里有大量低频成分,而且所送过去的音乐也不是完完全全的主旋律,因而有个简单的想法——提取出音乐的主旋律部分,或许会好些。
按照前面的想法,首先需要去除低频部分。为了做到这一点,我借助了DFT和IDFT来完成,其具体实现也很简单:DFT->删去低频成分->IDFT->结果
其次是主旋律部分的识别,由于主旋律一般是振动幅度较大的那个频率,因此依然可以借助DFT和IDFT来完成:DFT->把振荡幅度低的频率成分调为0->IDFT->结果

为了方便应用程序的处理,我在这里只使用wav文件。原因是这种文件大多存放PCM的线性流数据,方便操作,同样的,为了加快运算速度,我们把DFT和IDFT都用FFT和IFFT代替,那么按照上面的思路,总结就是:
读取wav文件->FFT->删去低频成分->删去振荡幅度低的成分->IFFT->保存wav数据

2.测试
由于上诉过程并不复杂,因此不做代码公开。
以FripSide-LateInAutumn.wav这个文件为例,我们先用Au打开它查看它的频谱,可以得到:

然后,进行FFT之后,删去低频部分,得到了下面的结果:


可以看到低频(最底下)的部分都已经几乎没有了,再进行主旋律筛选,得到的结果是:


可以看到,和第一张图做对比,已经改变太多了。但是图中每一段都有一个“竖线”,是为什么呢?其实是因为在上面的操作过程中导致了波形发生了变化,而FFT每次只是处理一小部分,因而每一个部分的波形会存在跳动,从而导致了这个问题。因此,这个需要注意。
3.编写过程中遇到的问题
在刚开始的时候,我依然使用16bit的wav文件来保存——但是随后发现这样比较麻烦,因此我最终是使用IEEE Float的32位的wav文件的。这种文件的保存非常简单,直接写入浮点数据的数组就可以了。下面是一个简单的实现:
#define WRIT_32_LH(dat,val)        (dat)[3] = (val) >> 24;(dat)[2] = ((val) >> 16) & 0xff;(dat)[1] = ((val) >> 8) & 0xff;(dat)[0] = (val) & 0xff
#define WRIT_16_LH(dat,val)        (dat)[1] = (val) >> 8;(dat)[0] = (val) & 0xff
void writeWavFile(const char* fileName, float *dat, DWORD leng, unsigned short samplerate)
{
        FILE    *f;
        DIGDATA *tmp;
        fopen_s(&f, fileName, "wb");
        if (f == NULL)
                return;
        tmp = (DIGDATA*)malloc(100);
        if (tmp == nullptr)
        {
                printf("打开输出文件失败\r\n");
                fclose(f);
                return;
        }
        fwrite("RIFF____WA【和谐符号,把它删了才是】VEfmt ", 4, 4, f);                        // RIFF文件头
        WRIT_32_LH(tmp + 0, 0x00000010);
        WRIT_16_LH(tmp + 4, WA【和谐符号,把它删了才是】VE_FORMAT_IEEE_FLOAT);        // 流类型
        WRIT_16_LH(tmp + 6, 1);                                                        // 通道数目
        WRIT_32_LH(tmp + 8, samplerate);                                // 采样率
        WRIT_32_LH(tmp + 12, samplerate * 4);                        // 一秒钟的字节数
        WRIT_16_LH(tmp + 16, 4);                                                // 数据区块尺寸
        WRIT_16_LH(tmp + 18, 32);                                                // 量化精度
        fwrite(tmp, 1, 20, f);
        fwrite("data____", 1, 8, f);                                        // data段
        WRIT_32_LH(tmp + 0, leng * 4);
        WRIT_32_LH(tmp + 4, leng * 4 + 44);
        fseek(f, 4, 0);                                                                        // RIFF后面的尺寸
        fwrite(tmp + 4, 1, 4, f);
        fseek(f, 40, 0);                                                                // data后面的尺寸
        fwrite(tmp + 0, 1, 4, f);
        fwrite(dat, 4, leng, f);
        free(tmp);
        fclose(f);
}
之后,便是窗函数的问题了。在这里需要提醒一下,如果FFT用了窗函数,最后把IFFT的结果去除以窗函数将会出问题——对于一些窗函数而言,旁瓣衰减不是不为零的。至于其它,便没有什么注意的了。
顺便吐槽一下论坛编辑器,wav文件,然后提示,有敏感词....

最后,不知道您对我这个脑洞的想法有什么建议或者提示没有
转播转播 分享淘帖
回复

使用道具

成为第一个回答人

B Color Link Quote Code Smilies
Archiver|手机版|小黑屋|MakerTime 创客时代  
Powered by Discuz! X3.3  © 2001-2017 Comsenz Inc.