如何查找丢失的音频片段

时间:2015-08-11 06:51:26

标签: java algorithm audio

我正在编写自己的音频格式作为游戏控制台项目的一部分。该项目的一部分要求我编写一个模拟器,以便我确切知道如何在硬件中实现它的功能。我目前正在编写DSP部分,但我在编写解码算法时遇到了麻烦。在我走得更远之前,我将解释我的格式。

DST(Dingo Sound Track)音频格式

音频格式仅记录每个样本的数据片段:自上次样本以来的幅度和帧数。我会解释一下。转换音频文件(例如WAV)时,它会将当前样本与前一个样本进行比较。如果它检测到当前样本相对于前一个样本切换幅度方向,则它记录前一个样本和自上一个记录以来的帧数。它一直持续到文件结束。这是一个进一步解释的图表:

enter image description here

我需要做什么

我需要我的“DSP”来确定每个样本之间的数据,尽可能准确地使用给定的信息。我不认为这是我的编码算法,因为当我在Audacity中播放文件时,我可以编写原始歌曲。但是当我尝试使用我的解码算法进行播放时,我会得到分散的点击。我可以直接使用几个mod来播放WAV文件,几乎没有质量下降,所以我知道它绝对是算法,而不是DSP的其余部分。

守则

所以现在我得到了所有的基本信息,这是我的代码(只有重要的部分)。

编码算法:

                FileInputStream s = null;
                BufferedWriter bw;
                    try {
                        int bytes;
                        int previous = 0;
                        int unsigned;
                        int frames = 0;
                        int size;
                        int cursor = 0;
                        boolean dir = true;
                        int bytes2;
                        int previous2 = 0;
                        int unsigned2;
                        int frames2 = 0;
                        boolean dir2 = true;
                        s = new FileInputStream(selectedFile);
                        size = (int)s.getChannel().size();
                        File f = new File(Directory.getPath() + "\\" + (selectedFile.getName().replace(".wav", ".dts")));
                        System.out.println(f.getPath());
                        if(!f.exists()){
                            f.createNewFile();
                        }
                        bw = new BufferedWriter(new FileWriter(f));
                        try (BufferedInputStream b = new BufferedInputStream(s)) {
                            byte[] data = new byte[128];
                            b.skip(44);
                            System.out.println("Loading...");
                            while ((bytes = b.read(data)) > 0) {
                              // do something
                              for(int i=1; i<bytes; i += 4) {
                                  unsigned = data[i] & 0xFF;
                                  if (dir) {
                                      if (unsigned < previous) {
                                          bw.write(previous);
                                          bw.write(frames);
                                          dir = !dir;
                                          frames = 0;
                                      }else{
                                          frames ++;
                                      }
                                  } else {
                                      if (unsigned > previous) {
                                          bw.write(previous);
                                          bw.write(frames);
                                          dir = !dir;
                                          frames = 0;
                                      }else{
                                          frames ++;
                                      }
                                  }
                                  previous = unsigned;
                                  cursor ++;
                                  unsigned2 = data[i + 2] & 0xFF;
                                  if (dir2) {
                                      if (unsigned2 < previous2) {
                                          bw.write(previous2);
                                          bw.write(frames2);
                                          dir2 = !dir2;
                                          frames2 = 0;
                                      }else{
                                          frames2 ++;
                                      }
                                  } else {
                                      if (unsigned2 > previous2) {
                                          bw.write(previous2);
                                          bw.write(frames2);
                                          dir2 = !dir2;
                                          frames2 = 0;
                                      }else{
                                          frames2 ++;
                                      }
                                  }
                                  previous2 = unsigned2;
                                  cursor ++;
                                  progress.setValue((int)(((float)(cursor / size)) * 100));
                              }
                            }
                            b.read(data);
                        }
                        bw.flush();
                        bw.close();
                        System.out.println("Done");
                        convert.setEnabled(true);
                        status.setText("finished");
                    } catch (Exception ex) {
                        status.setText("An error has occured");
                        ex.printStackTrace();
                        convert.setEnabled(true);
                }
                finally {
                    try {
                        s.close();
                    } catch (Exception ex) {
                        status.setText("An error has occured");
                        ex.printStackTrace();
                        convert.setEnabled(true);
                    }
                }

进度和状态对象可以忽略,因为它们是我的转换器工具的GUI的一部分。该算法将WAV文件转换为我的格式(DST)。

解码算法:

int start = bufferSize * (bufferNumber - 1);
short current;
short frames;
short count = 1;
short count2 = 1;
float jump;
for (int i = 0; i < bufferSize; i ++) {
    current = RAM.read(start + i);
    i++;
    frames = RAM.read(start + i);
    if (frames == 0) {
        buffer[count - 1] = current;
        count ++;
    } else {
        jump = current / frames;
        for (int i2 = 1; i2 < frames; i2++) {
            buffer[(2 * i2) - 1] = (short) (jump * i2);
            count ++;
        }
    }
    i++;
    current = RAM.read(start + i);
    i++;
    frames = RAM.read(start + i);
    if (frames == 0) {
        buffer[count2] = current;
        count2 ++;
    } else {
        jump = current / frames;
        for (int i2 = 1; i2 < frames; i2++) {
            buffer[2 * i2] = (short) (jump * i2);
            count2 ++;
        }
    }
}
bufferNumber ++;
if(bufferNumber > maxBuffer){
    bufferNumber = 1;
}

RAM对象只是一个字节数组。 bufferNumber和maxBuffer指的是DSP内核使用的处理缓冲区数量。 buffer是生成的音频写入的对象。此算法集用于转换立体声轨道,其格式与我的格式相同,但每个样本将包含两组数据,每个轨道一个。

问题

如何尽可能准确地找出每个样本之间丢失的音频,以及该方法的准确程度如何?我很乐意简单地使用WAV格式,但我的控制台仅限于内存(RAM)。此格式将处理音频所需的RAM空间减半。我还计划在ARM微控制器中实现该算法,这将是控制台的真正DSP。算法也应该很快,但准确性更重要。如果我需要澄清或解释任何进一步的事情,请告诉我,因为这是我的第一个大问题,我确信我忘记了一些事情。代码示例会很好,但不需要那么多。

修改

我设法让DSP输出一首歌,但它加速并充满静电。加速部分是由于它没有将轨道分成立体声(我认为)的故障。而静态是由于初始增量过于陡峭。这是我得到的图片: enter image description here

以下是DSP中使用的新代码:

            if (frames == 0) {
                buffer[i - 1] = current;
                //System.out.println(current);
            } else {
                for (int i2 = 1; i2 < frames + 1; i2++) {
                    jump = (float)(previous + ((float)(current - previous) / (frames - i2 + 1)));
                    //System.out.println((short)jump);
                    buffer[(2 * i2) - 1] = (short)(jump);
                }
            }
            previous = current;

我需要一种方法来平滑那些初始增量,我宁愿不使用复杂的算术,因为当我将它移植到硬件时,我的性能有限(最好能够在100MHZ ARM控制器上运行,同时能够保持44.1KHZ的采样率)。编辑:结果波实际上应该倒退。遗憾。

第二次修改: 我让DSP以立体声方式输出,但不幸的是,这并没有像我希望的那样修复任何其他东西。我还用编码器修复了一些错误,现在需要8位无符号音频。这已成为一个数学问题,所以我想我会在数学堆栈交换中发布一个类似的问题。那是浪费时间。它立即被放在了附近。

1 个答案:

答案 0 :(得分:0)

您基本上记录了信号的局部极值,并希望重建信号。最直接的方法是使用一些monotonic interpolation scheme。如果这符合您的需求,您可以尝试。但我猜,结果会非常不准确,因为信号的特征会被忽略。

我不是音频工程师,所以我的假设可能是错误的。但也许,你会得到这些想法。

信号基本上是正弦的混合物。计算两个关键帧之间任何段的正弦函数非常容易。这段时间是两倍的距离。幅度由幅度差的一半给出。这将为您提供正确击中两个关键样本的正弦值。此外,它将为您提供C1连续信号,因为连接点处的导数为零。对于一个好的信号,你可能需要更加平滑。因此,您可以开始使用适当的窗口函数在关键帧周围插入两个正弦。我会从一个简单的三角窗口开始,但其他人可能会给出更好的结果。这个程序将保留极值。

可能更容易直观地解决这个问题(使用信号图),因此您可以看到结果。

如果它与尺寸有关,那么您可能想要研究已建立的音频压缩方法。它们通常提供比1:2更好的压缩比。此外,我不明白为什么这种方法可以节省RAM,因为你必须在解码时计算所有样本。当然,这假设不是将完整的数据加载到RAM中而是流式传输。