技术控

    今日:133| 主题:49390
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 用Java做音乐识别软件

[复制链接]
不会发光的星星 发表于 2016-10-4 08:24:46
108 4

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
几天前我看到了这篇文章:  How Shazam Works  。
  这让我对 Shazam 一类的应用程序很感兴趣……更重要的是,用 Java 编写类似的程序会有多困难呢?
  关于 Shazam

  Shazam 是一款用来分析/匹配音乐的应用程序。当你将它安装在手机上并用麦克风采集音源20到30秒,它就能告诉你这是首什么歌。
  我第一次使用时感觉太神奇了。“它是怎么办到的!?”。甚至是今天,用了很久后,我依然觉得它有些神奇。如果我们能编写出可以带来相同感觉的程序会不会更棒呢?这是我在上周末的目标。
  听着……!

  先说重要的,为了获取音乐样品来分析,我们首先需要在 Java 中听取麦克风……!我从没有用 Java 实现过这个,所以我并不清楚会有多难。但结果是这很简单:
  [code]final AudioFormat format = getFormat(); //Fill AudioFormat with the wanted settings
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
final TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();[/code]  现在我们只要像普通 InputStream 那样从 TargetDataLine 中读取数据:
  [code]// In another thread I start:

OutputStream out = new ByteArrayOutputStream();
running = true;

try {
    while (running) {
        int count = line.read(buffer, 0, buffer.length);
        if (count > 0) {
            out.write(buffer, 0, count);
        }
    }
    out.close();
} catch (IOException e) {
    System.err.println("I/O problems: " + e);
    System.exit(-1);
}[/code]  使用这种方式会让打开麦克风和录取所有声音的操作变得非常简单!我现在正在使用的AudioFormat是:
  [code]private AudioFormat getFormat() {
    float sampleRate = 44100;
    int sampleSizeInBits = 8;
    int channels = 1; //mono
    boolean signed = true;
    boolean bigEndian = true;
    return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
}[/code]  所以,现在我们获得了用 ByteArrayOutputStream 包装的录音数据,很好!第一步完成了。
  麦克风数据

  下个问题是分析数据,当我输出在字节数组中获取的数据时,我获得了一串长长的数据列表,像是这样:
  [code]0
0
1
2
4
7
6
3
-1
-2
-4
-2
-5
-7
-8
(etc)[/code]  额……对吗?这是声音吗?
  为了让数据可视化我将输出放入Open Office形成一张线状图表:
   
用Java做音乐识别软件-1 (应用程序,wanted,麦克风,音乐,Java)

  哦是的!这有点像是’声音‘!就像是使用 Windows Sound Recorder 时看到的一样。
   这种数据通常是  时间域 。但这些数据目前对我们来说基本上是无用的……如果你阅读了上面 Shazam 工作方式的文章,你会发现他们使用的是 频谱分析器 而不是直接使用时间域数据。
  所以下个大问题是:我们怎么将现在的数据转换成频谱分析?
  离散傅里叶变化

   为了将数据转换成有用的数据我们需要应用所谓的 离散傅里叶变换 。它能将数据从时域转换成频域。
  仅仅有一个问题,如果将数据转换成频域,每一位与时间有关的信息会变得松散。所以需要知道所有频率的大小,但并不清楚他们什么时候出现。
  我们需要滑动窗口来解决这个问题。取出数据块(在我的例子中是 4096 字节数据)并且转换这部分信息。那么就知道了出现在 4096字节中所有频率的大小。
  实现这些

  我并不担心傅里叶变换,而是在谷歌搜索到了所谓的FFT(快速傅里叶转换)的代码,我调用了这个代码块:
  [code]byte audio[] = out.toByteArray();

final int totalSize = audio.length;

int amountPossible = totalSize/Harvester.CHUNK_SIZE;

//When turning into frequency domain we'll need complex numbers:
Complex[][] results = new Complex[amountPossible][];

//For all the chunks:
for(int times = 0;times < amountPossible; times++) {
    Complex[] complex = new Complex[Harvester.CHUNK_SIZE];
    for(int i = 0;i < Harvester.CHUNK_SIZE;i++) {
        //Put the time domain data into a complex number with imaginary part as 0:
        complex = new Complex(audio[(times*Harvester.CHUNK_SIZE)+i], 0);
    }
    //Perform FFT analysis on the chunk:
    results[times] = FFT.fft(complex);
}

//Done![/code]  现在我们有了一个 double 数组,它包含了所有 Complex[] 类的数据块。这个数组包含了关于频率的数据。为了将数据可视化我决定实现一个完整的频谱分析器(只是为了保证我获得正确的数据)。我设计了这个程序来显示数据:
  [code]for(int i = 0; i < results.length; i++) {
    int freq = 1;
    for(int line = 1; line < size; line++) {
        // To get the magnitude of the sound at a given frequency slice
        // get the abs() from the complex number.
        // In this case I use Math.log to get a more managable number (used for color)
        double magnitude = Math.log(results[freq].abs()+1);

        // The more blue in the color the more intensity for a given frequency point:
        g2d.setColor(new Color(0,(int)magnitude*10,(int)magnitude*20));
        // Fill:
        g2d.fillRect(i*blockSizeX, (size-line)*blockSizeY,blockSizeX,blockSizeY);

        // I used a improviced logarithmic scale and normal scale:
        if (logModeEnabled && (Math.log10(line) * Math.log10(line)) > 1) {
            freq += (int) (Math.log10(line) * Math.log10(line));
        } else {
            freq++;
        }
    }
}[/code]  介绍一下,Aphex Twin

   似乎有些跑题了,但我还想介绍这位名叫 Aphex Twin(Richard David James)的电子音乐家。他创作疯狂的电子音乐。。。。。。但一些歌曲有着有趣的特点。他大热的作品,比如 Windowlicker 中就有频谱图像。如果将这首歌看成是频谱的图像,它展示了一幅漂亮的螺旋。另一首称为“Mathematical Equation”的歌曲显示了 Twin 的脸!在这里可以发现更多信息:  Bastwood - Aphex Twin’s face 。
  在我的频谱分析器中播放歌曲获得下面结果:

用Java做音乐识别软件-2 (应用程序,wanted,麦克风,音乐,Java)

  不是很完美,但似乎就是Twin 的脸!
  决定关键音乐的点

  Shazam 算法下一步是决定歌曲中一些关键点,将这些点存储为哈希表并且尝试与数据库中超过 8 百万的歌曲匹配。这些操作被快速完成,哈希表的查找速度是 O(1)级的。这就解释了 Shazam 令人惊叹的执行力。
  因为我想要在一个周末让一切运转起来(可悲的是这是我最长的注意力持续时间,随后我需要开始一个新的工程)我尽可能使我的算法简单。惊奇的是它运转起来了。
  我在频谱分析每行中取出一定范围内量级最大的点。在我的例子中范围是:40—80、80—120、120—180、180—300。
  [code]//For every line of data:

for (int freq = LOWER_LIMIT; freq < UPPER_LIMIT-1; freq++) {
    //Get the magnitude:
    double mag = Math.log(results[freq].abs() + 1);

    //Find out which range we are in:
    int index = getIndex(freq);

    //Save the highest magnitude and corresponding frequency:
    if (mag > highscores[index]) {
        highscores[index] = mag;
        recordPoints[index] = freq;
    }
}

//Write the points to a file:
for (int i = 0; i < AMOUNT_OF_POINTS; i++) {
    fw.append(recordPoints + "\t");
}
fw.append("\n");

// ... snip ...

public static final int[] RANGE = new int[] {40,80,120,180, UPPER_LIMIT+1};

//Find out in which range
public static int getIndex(int freq) {
    int i = 0;
    while(RANGE < freq) i++;
        return i;
    }
}[/code]  现在录取一首歌,我们会得到一个数字列表,像这样:
  如果我录取一首歌并将其可视化,看起来像这样:
12下一页
友荐云推荐




上一篇:Blood, Sweat, and Writing Automated Integration Tests for Failure Scenarios
下一篇:使用String的intern方法节省内存
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

鑫磊 发表于 2016-10-22 01:50:47
楼下的不要小看我,我可不是吃素的。
回复 支持 反对

使用道具 举报

你只是我旳專屬 发表于 2016-10-25 14:45:30
帮顶个帖,攒人品,说不定我就会升职加薪、当上总经理、出任CEO、迎娶白富美、走上人生巅峰,嘿嘿,想想还有点小激动。
回复 支持 反对

使用道具 举报

tt222 发表于 2016-11-2 13:10:53
女为悦己者容男为悦己者穷!
回复 支持 反对

使用道具 举报

招积仔 发表于 2016-11-15 23:14:48
不会发光的星星不整容也像雷锋!
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表