我在 gamedev 上发布了一个关于如何在 FMOD 中播放 nsf 文件(NES 控制台音乐)的问题。它没有得到任何结果,但从那以后我取得了一些进展。我决定最简单的方法是将现有播放器编译成 dll,然后从 C# 调用它来填充我的缓冲区。现在的问题是让它听起来正确,并确保我所有的参数都是正确的。
以下是迄今为止的事实:
- nsf dll是在处理
short
s,所以数据是PCM16。 - 我使用的示例 nsf 的播放速率为 60 Hz。
- 只是为了玩,我用的是48000的频率。
- 基于 2 和 3,dll 计算出必要的缓冲区大小 48000 / 60hz = 800。这意味着它将
short
为每个模拟的 NES 帧渲染 800 秒的缓冲区。
到目前为止,我的 C# 代码可以以正确的音高和速度播放 nsf,但它非常粗糙/模糊,我将其归因于 FMOD 读取回调给出的数据长度为 1600 的事实,而我应该期待 800。我尝试过使用所有数字,但它要么崩溃,要么音乐改变音高、节奏,或两者兼而有之。
这是我的一些 C# 代码:
uint channels = 1, frequency = 48000;
FMOD.MODE mode = (FMOD.MODE.DEFAULT | FMOD.MODE.OPENUSER | FMOD.MODE.LOOP_NORMAL);
FMOD.Sound sound = new FMOD.Sound();
FMOD.CREATESOUNDEXINFO ex = new FMOD.CREATESOUNDEXINFO();
ex.cbsize = Marshal.SizeOf(ex);
ex.fileoffset = 0;
ex.format = FMOD.SOUND_FORMAT.PCM16;
// does this even matter? It doesn't change my results as long as it's long enough for one update
ex.length = frequency;
ex.numchannels = (int)channels;
ex.defaultfrequency = (int)frequency;
ex.pcmreadcallback = pcmreadcallback;
ex.dlsname = null;
// eventually I will calculate this with frequency / nsf hz, but I'm just testing for now
ex.decodebuffersize = 800;
// from the dll
load_nsf_file("file.nsf", 8, (int)frequency); // 8 is the track number to play
var result = system.createSound(
(string)null,
(mode | FMOD.MODE.CREATESTREAM),
ref ex,
ref sound);
channel = new FMOD.Channel();
result = system.playSound(FMOD.CHANNELINDEX.FREE, sound, false, ref channel);
private FMOD.RESULT PCMREADCALLBACK(IntPtr soundraw, IntPtr data, uint datalen)
{
// from the dll
process_buffer(data, (int)800); // if I use datalen, it usually crashes (I can't get datalen to = 800 safely)
return FMOD.RESULT.OK;
}
所以这是我的一些问题:
datalen
exinfo.decodebuffersize、频率和读取回调的参数是什么关系?使用此代码示例,它以 3200 的形式出现。我不知道它与 decodebuffersize 之间的 4 因子来自何处。- 在回调中是
datalen
指byte
s 的数量还是short
s?process_buffer 函数采用一个短数组及其长度。我希望 fmod 也在谈论短裤,因为我告诉它 PCM16。 - 也许由于某些完全不同的原因,我的播放质量很差。如果是这样,我不知道从哪里开始解决这个问题。有什么想法吗?