【发布时间】:2015-11-03 06:44:06
【问题描述】:
我正在编写自己的音频格式作为游戏机项目的一部分。项目的一部分需要我编写一个模拟器,所以我确切地知道如何在硬件中实现它的功能。我目前正在编写 DSP 部分,但在编写解码算法时遇到了麻烦。在继续之前,我先解释一下我的格式。
DST(Dingo 音轨)音频格式
音频格式仅记录每个样本的数据片段:自上次样本以来的幅度和帧数。我会解释的。转换音频文件(例如 WAV)时,它会将当前样本与前一个样本进行比较。如果它检测到当前样本相对于前一个样本切换幅度方向,它会记录前一个样本和自上次记录以来的帧数。它一直持续到文件末尾。这是一个图表来进一步解释:
我需要做什么
我需要我的“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 微控制器中实现这个算法,这将是控制台的真正 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 位无符号音频。这已经成为一个数学问题,所以我想我会在数学堆栈交换中发布一个类似的问题。那是浪费时间。它很快就被搁置了。
【问题讨论】: