0

在 gamedev 上发布了一个关于如何在 FMOD 中播放 nsf 文件(NES 控制台音乐)的问题。它没有得到任何结果,但从那以后我取得了一些进展。我决定最简单的方法是将现有播放器编译成 dll,然后从 C# 调用它来填充我的缓冲区。现在的问题是让它听起来正确,并确保我所有的参数都是正确的。

以下是迄今为止的事实:

  1. nsf dll是在处理shorts,所以数据是PCM16。
  2. 我使用的示例 nsf 的播放速率为 60 Hz。
  3. 只是为了玩,我用的是48000的频率。
  4. 基于 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;
}

所以这是我的一些问题:

  1. datalenexinfo.decodebuffersize、频率和读取回调的参数是什么关系?使用此代码示例,它以 3200 的形式出现。我不知道它与 decodebuffersize 之间的 4 因子来自何处。
  2. 在回调中是datalenbytes 的数量还是shorts?process_buffer 函数采用一个短数组及其长度。我希望 fmod 也在谈论短裤,因为我告诉它 PCM16。
  3. 也许由于某些完全不同的原因,我的播放质量很差。如果是这样,我不知道从哪里开始解决这个问题。有什么想法吗?
4

1 回答 1

1
  1. FMOD_CREATESOUNDEXINFO 'decodebuffersize' 成员位于 PCM 样本中,并控制在 'pcmreadcallback' 函数中将请求多少数据。因此,由于您将其设置为 800 和 PCM16,因此“datalen”将为 1600,因为该值以字节为单位。你提到的'datalen'值是1600(如预期的那样)但后来你说它是3200,也许我混淆了你的意思。最后,“频率”只是告诉 FMOD 播放数据的速度,这应该与“process_buffer”函数产生的结果相匹配。

  2. 'datalen' 以字节为单位。

  3. 一个简单的测试方法是生成 5 秒的音频,然后将其作为样本加载到 FMOD 中。这避免了读取回调的潜在并发症。

顺便说一句:'ex.length' 告诉 Sound::getLength 您的文件实际有多大,如果您使用的是 FMOD_CREATESAMPLE,这是分配的内存量。

于 2011-01-09T23:29:54.513 回答