我正在编写自己的音频格式作为游戏控制台项目的一部分。该项目的一部分要求我编写一个模拟器,以便我确切知道如何在硬件中实现它的功能。我目前正在编写DSP部分,但我在编写解码算法时遇到了麻烦。在我走得更远之前,我将解释我的格式。
DST(Dingo Sound Track)音频格式
音频格式仅记录每个样本的数据片段:自上次样本以来的幅度和帧数。我会解释一下。转换音频文件(例如WAV)时,它会将当前样本与前一个样本进行比较。如果它检测到当前样本相对于前一个样本切换幅度方向,则它记录前一个样本和自上一个记录以来的帧数。它一直持续到文件结束。这是一个进一步解释的图表:
我需要做什么
我需要我的“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输出一首歌,但它加速并充满静电。加速部分是由于它没有将轨道分成立体声(我认为)的故障。而静态是由于初始增量过于陡峭。这是我得到的图片:
以下是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位无符号音频。这已成为一个数学问题,所以我想我会在数学堆栈交换中发布一个类似的问题。那是浪费时间。它立即被放在了附近。
答案 0 :(得分:0)
您基本上记录了信号的局部极值,并希望重建信号。最直接的方法是使用一些monotonic interpolation scheme。如果这符合您的需求,您可以尝试。但我猜,结果会非常不准确,因为信号的特征会被忽略。
我不是音频工程师,所以我的假设可能是错误的。但也许,你会得到这些想法。
信号基本上是正弦的混合物。计算两个关键帧之间任何段的正弦函数非常容易。这段时间是两倍的距离。幅度由幅度差的一半给出。这将为您提供正确击中两个关键样本的正弦值。此外,它将为您提供C1连续信号,因为连接点处的导数为零。对于一个好的信号,你可能需要更加平滑。因此,您可以开始使用适当的窗口函数在关键帧周围插入两个正弦。我会从一个简单的三角窗口开始,但其他人可能会给出更好的结果。这个程序将保留极值。
可能更容易直观地解决这个问题(使用信号图),因此您可以看到结果。
如果它与尺寸有关,那么您可能想要研究已建立的音频压缩方法。它们通常提供比1:2更好的压缩比。此外,我不明白为什么这种方法可以节省RAM,因为你必须在解码时计算所有样本。当然,这假设不是将完整的数据加载到RAM中而是流式传输。