0

给定一个应该在 C# 中播放 SNES SPC 文件和 FMOD 的 DLL,为什么这个调用会system.createSound失败?

var ret = system.init(32, FMOD.INITFLAGS.NORMAL, (IntPtr)null);
var soundEx = new FMOD.CREATESOUNDEXINFO()
{
    cbsize = Marshal.SizeOf(soundEx),
    fileoffset = 0,
    length = ~0U,
    numchannels = 2,
    defaultfrequency = 32000,
    format = FMOD.SOUND_FORMAT.PCM16,
    pcmreadcallback = pcmreadcallback,
    pcmsetposcallback = pcmsetposcallback,
    dlsname = null,
};
var mode = FMOD.MODE.DEFAULT | FMOD.MODE.OPENUSER
         | FMOD.MODE.LOOP_NORMAL | FMOD.MODE.CREATESTREAM;
ret = system.createSound((string)null, mode, ref soundEx, ref sound);
//^-- ERR_INVALID_PARAM
ret = system.playSound(FMOD.CHANNELINDEX.FREE, sound, false, ref channel);

将其usercreatedsound与 FMOD 附带的示例进行比较:

FMOD.MODE mode = (FMOD.MODE._2D | FMOD.MODE.DEFAULT
               | FMOD.MODE.OPENUSER | FMOD.MODE.LOOP_NORMAL
               | FMOD.MODE.HARDWARE);
//snip
createsoundexinfo.cbsize            = Marshal.SizeOf(createsoundexinfo);
createsoundexinfo.fileoffset        = 0;
createsoundexinfo.length            = frequency * channels * 2 * 2;
createsoundexinfo.numchannels       = (int)channels;
createsoundexinfo.defaultfrequency  = (int)frequency;
createsoundexinfo.format            = FMOD.SOUND_FORMAT.PCM16;
createsoundexinfo.pcmreadcallback   = pcmreadcallback;
createsoundexinfo.pcmsetposcallback = pcmsetposcallback;
createsoundexinfo.dlsname           = null;
//snop
result = system.createSound(
    (string)null, 
    (mode | FMOD.MODE.CREATESTREAM), 
    ref createsoundexinfo,
    ref sound);

长度,频率......无关紧要。

编辑:我已经确认 SPC 播放器可以正常工作,至少就初始化而言,并且 FMOD 附带的示例可以正常构建和运行。除了调整设置以使其运行之外,唯一特别有意义的更改是以 4.0 风格编写。

4

1 回答 1

1

不一定在所有声卡上都可以播放任意采样率。32Khz 不是 44.1、48、96 等“常见”速率...

  • 你试过 FMOD.MODE.SOFTWARE 吗?
  • 您可能可以使用ASIO4ALL使用仲裁采样率,但您需要切换到 ASIO。

你绑定到 FMOD 了吗?如果没有,那么考虑BASS.NET,你将比 FMOD 拥有更多的控制权来做这些事情。

使用 BASSMix 的示例:请参阅下面的备注

using System;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Mix;

namespace XXX
{
    public class Resampler : IDisposable
    {
        private readonly int _channels;
        private readonly string _filename;
        private readonly int _samplerate;

        public Resampler(string filename, int samplerate, int channels)
        {
            if (filename == null) throw new ArgumentNullException("filename");
            if (samplerate <= 0) throw new ArgumentNullException("samplerate");
            if (channels <= 0) throw new ArgumentNullException("channels");

            #region Initialize BASS stuff

            #endregion

            _filename = filename;
            _samplerate = samplerate;
            _channels = channels;
        }

        #region IDisposable Members

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        #endregion

        public void Resample(string filename)
        {
            if (filename == null) throw new ArgumentNullException("filename");
            Exceptions.ThrowIfPathInvalid(filename);

            #region Create stream and mixer

            int channel = Bass.BASS_StreamCreateFile(filename, 0, 0,
                                                     BASSFlag.BASS_STREAM_PRESCAN | BASSFlag.BASS_STREAM_DECODE |
                                                     BASSFlag.BASS_SAMPLE_FLOAT);
            if (channel == 0)
                throw new BassException("Couldn't create stream.");

            var mixer = BassMix.BASS_Mixer_StreamCreate(22050, 1, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT);
            if (mixer == 0)
                throw new BassException("Couldn't create mixer stream.");

            if (!BassMix.BASS_Mixer_StreamAddChannel(mixer, channel,
                                                     BASSFlag.BASS_MIXER_DOWNMIX | BASSFlag.BASS_MIXER_FILTER |
                                                     BASSFlag.BASS_MIXER_NORAMPIN))
                throw new BassException("Couldn't add stream channel to mixer.");

            #endregion

            int sr = 22050;
            int secs = 20;
            int samples0 = sr * secs;
            var buffer = new float[samples0];
            int length = sizeof(float) * buffer.Length;
            var getData = Bass.BASS_ChannelGetData(mixer, buffer, length | (int)BASSData.BASS_DATA_FLOAT);
            if (getData != length)
            {
                throw new BassException("");
            }
            var bassMixerChannelRemove = BassMix.BASS_Mixer_ChannelRemove(channel);
            var streamFree = Bass.BASS_StreamFree(channel);
            var bassStreamFree = Bass.BASS_StreamFree(mixer);
        }
    }
}

(在 VS2010 中使用最新的低音)

一些额外的提示:

  • 你必须先调用 BASS.Bass_Init,

  • 不要忘记复制你的EXE旁边的bass.dll和bassmix.dll,

  • 我个人会坚持使用 BASS_SAMPLE_FLOAT,即使您的源不是,最大的好处是您不会使用这种格式进行剪辑,我告诉您这是因为我为 NES 鸸鹋制作了一个音频包装器,其 APU 的输出相当“动态的”,它被证明是有帮助的。这也使事情变得更简单,质量最好。

  • 在您的情况下,您希望使用推送流并将其与您的 SNES 输出一起提供:Bass..::..BASS_StreamCreatePush

  • 创建一个“标准”混音器流(不是解码流),插入您的推送流,播放混音器

  • 在示例中,它是一个解码流,因为我需要处理数据,而不是回放它。

  • BassException 只是我自己创建的,将其更改为您想要的任何内容

  • 您最好的朋友将是 CHM 帮助文件,也是他们的论坛。

  • 我建议你从这段代码开始,然后在你有任何问题时发布你的新代码。

不得不说,BASS 一开始是很难掌握的,但最后还是很有收获的。最近在学习 OpenGL 3.X 的时候也有同样的感觉;一开始我很难理解,但现在我可以做很多事情了。我会说 BASS 与 OpenGL 和 3D 一样。

抱歉,自从上次以来我病得很重,大部分时间都没有使用PC,但是我看到了您的消息,想尽快回复您,以便您继续前进。如果有人向 SO 抱怨我的答案布局:我稍后会修复它。

于 2012-06-02T15:43:43.217 回答