java - 如何查找丢失的音频片段

标签 java algorithm audio

作为游戏机项目的一部分,我正在编写自己的音频格式。该项目的一部分要求我编写一个仿真器,因此我确切地知道如何在硬件中实现其功能。我目前正在编写DSP部分,但是在编写解码算法时遇到了麻烦。在继续之前,我将解释我的格式。
DST(Dingo音轨)音频格式
音频格式仅记录每个样本的数据:振幅和自上次样本以来的帧数。我会解释。转换音频文件(例如WAV)时,它将当前样本与前一个样本进行比较。如果检测到当前样本相对于前一个样本切换幅度方向,则会记录前一个样本以及自上次记录以来的帧数。它一直持续到文件末尾。这是进一步解释的图:
enter image description here
我需要做什么
我需要我的“DSP”来仅使用给定的信息尽可能准确地找出每个样本之间的数据。我不认为这是我的编码算法,因为当我在Audacity中播放文件时,我可以整理出原始歌曲。但是,当我尝试使用解码算法播放它时,会得到零星的点击。我可以直接播放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内核使用的处理缓冲区的数量。缓冲区是将结果音频写入的对象。此算法集旨在转换立体声轨道,在我的格式中,立体声轨道的工作方式相同,但每个样本将包含两组数据,每个轨道一组。
问题
我如何尽可能准确地找出每个样本之间缺少的音频,以及该方法的准确性如何?我只想简单地使用WAV格式,但是我的控制台内存有限(RAM)。此格式将处理音频所需的RAM空间减半。我还计划在ARM微 Controller 中实现此算法,它将成为控制台的真正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 Controller 上运行的同时能够保持44.1KHZ采样率)。编辑:结果波实际上应该是向后的。抱歉。
第二编辑:
我让DSP以立体声输出,但不幸的是,它并没有像我希望的那样修复其他任何东西。我还修复了编码器的一些错误,因此现在需要8位无符号音频。这已经成为一个数学问题,所以我想我将在Mathematics Stack Exchange中发布一个类似的问题。好吧,那是浪费时间。它立刻被搁置了。

最佳答案

您基本上已经记录了信号的局部极值,并且想要重建信号。最简单的方法是使用一些monotonic interpolation scheme。您可以尝试是否满足您的需求。但是我想,结果会非常不准确,因为信号的特性会被忽略。

我不是音频工程师,所以我的假设可能是错误的。但是也许,您对这些想法有所了解。

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

视觉上(使用信号图)解决此问题可能更容易,因此您可以看到结果。

如果这一切都与尺寸有关,那么也许您想研究已建立的音频压缩方法。通常,它们的压缩率要比1:2好得多。另外,我不明白为什么这种方法会节省RAM,因为解码时必须计算所有样本。当然,这是假设不是将完整的数据加载到RAM中而是将其分流传输。

关于java - 如何查找丢失的音频片段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31935027/

相关文章:

java - EAR 移动用户界面

algorithm - 回溯尾递归算法可以转换为迭代吗?

iOS - 打电话时在后台播放声音

java - ceylon 目录的 Jimfs 路径

java - 使用来自依赖 jar 的 Spring CacheManager

java - 编译带有边界的泛型时出错

python - 从字节创建 .wav 文件

python - 欧拉计划 #23 错误答案

c# - 树算法实现c#

javascript - 当时间太短时,音频不会在setInterval函数中播放