2

我正在使用 winmm.dll 使用 C# 开发 mp3 播放器

我的倒带功能现在有问题。

当播放功能启动时,我启动了一个计时器。

当我启动 FFW 函数时,我希望它距离 elapsedTime 的当前值 5000 毫秒。

例如,如果我开始播放歌曲并在歌曲中按 FFW 6 秒,则播放应在 6000 毫秒 + 5000 毫秒(ffw5 秒)处继续。

但什么都没有发生。这次我做错了什么?

namespace kTP
{
    public class MusicPlayer
    {
        [DllImport("winmm.dll")]
        private static extern long mciSendString(string lpstrCommand, StringBuilder lpstrReturnString, int uReturnLength, int hwndCallback);

        private bool isPlaying = false;
        private int ffw5sec = 5000;

        Timer elapsedTime = new Timer();

        public void Open(string file)
        {
            string command = "open \"" + file + "\" type MPEGVideo alias MyMp3";
            mciSendString(command, null, 0, 0);
        }

        public void Play()
        {
            isPlaying = true;
            elapsedTime.Start();
            elapsedTime.Interval = (1000);

            string command = "play MyMp3";
            mciSendString(command, null, 0, 0);
        }

        public void Pause()
        {
            isPlaying = false;
            string command = "pause MyMp3";
            mciSendString(command, null, 0, 0);
        }

        public void Stop()
        {
            isPlaying = false;
            string command = "stop MyMp3";
            mciSendString(command, null, 0, 0);

            elapsedTime.Stop();

            command = "close MyMp3";
            mciSendString(command, null, 0, 0);
        }

            // return to start of song and resume playback
        public void ReturnToStart()
        {
            isPlaying = true;
            string command = "seek MyMp3 to start";
            mciSendString(command, null, 0, 0);

            command = "play MyMp3";
            mciSendString(command, null, 0, 0);
        }

            // Fast Forward
                // needs a better skip Implementation
        public void FFW()
        {
            if (isPlaying == true)
            {
                string command = "set MyMp3 time format ms";
                mciSendString(command, null, 0, 0);

                command = "seek MyMp3 to " + (elapsedTime.ToString() + ffw5sec.ToString());
                mciSendString(command, null, 0, 0);

                command = "play MyMp3";
                mciSendString(command, null, 0, 0);

                //ffw5sec += 5000;
            }
         }

    }
}
4

1 回答 1

1

在这行代码中:

command = "seek MyMp3 to " + (elapsedTime.ToString() + ffw5sec.ToString());

您将 elapsedTime 和 ffw5sec 的字符串形式连接起来,而 elapsedTime 的字符串形式是不可解析的,即您无法将其解析为整数。同样在您的问题中,您没有告诉其他人您正在使用哪个计时器。在 C# .NET 框架中,有三种 Timer 类。

有:

System.Timers.Timer,
System.Threading.Timer, and
System.Windows.Forms.Timer

但是这些计时器类的字符串形式都不是不可解析的。

is 的字符串形式, System.Timers.Timeris"System.Timers.Timer"的字符串形式,System.Threading.Timeris"System.Threading.Timer"的字符串形式,其中替换为System.Windows.Forms.Timer属性。"System.Windows.Forms.Timer, Interval: {0}"{0}System.Windows.Forms.Timer.Interval

无论如何,我知道您不使用System.Threading.Timer,因为在您发布在问题中的代码中,我看到您使用空构造函数来创建计时器Timer(),并且System.Threading.Timer不包含带有 0 个参数的构造函数,但我仍然不知道如果是System.Timers.TimerSystem.Windows.Forms.Timer

seek 命令的字符串格式的语法是:

"seek {0} to {1}"

where{0}被替换为要查找的音频文件的路径和名称或别名,在您的情况下, {0} 是MyMp3. 并{1}替换为音频文件中的时间,只能是整数

表达式: (elapsedTime.ToString() + ffw5sec.ToString()), 根本不返回可以解析为整数的字符串。

驱动程序无法执行此命令。

也建议使用String.Format方法来准备和制作你的mci字符串命令用于mciSendString函数,而不是使用连接字符串的方法,因为String.Format方法比连接字符串的方法更容易使用和方便。这样你就不会感到困惑和犯错。同样,由 String.Format 格式化的 mci 字符串命令比串联的 mci 字符串命令更具可读性。

您犯的另一个错误是您首先将 elapsedTime 和 ffw5sec 转换为字符串,然后将它们连接在一起,然后将结果字符串连接到 mci string 命令。

这样做是错误的。您应该首先将elapsedTime 和 ffw5sec的数值相加,它们表示时间,然后将结果转换为字符串并连接到 mci 字符串命令。

我想说的是你不应该调用 ToString 两次,但你可以使用一次。

您制作 mci 字符串命令的错误方式是:

"seek MyMp3 to " + (elapsedTime.ToString() + ffw5sec.ToString())"

制作 mci 字符串命令的正确方法是:

"seek MyMp3 to " + (elapsedTime + ffw5sec).ToString()

这仅在 elapsedTime 为整数的情况下才有效,但如果不是,则应使用其将时间返回为整数的属性之一。

如果 elapsedTime 的字符串形式是可解析的,那么 mci 字符串命令是:"seek MyMp3 to 50006000",因为您将“5000”与“6000”连接起来,结果是“50006000”。

但按正确的方式,mci 字符串命令应该是:"seek MyMp3 to 11000",因为你先将 5000 和 6000 相加,结果是 11000 作为整数。

您甚至根本不必使用ToString(),因为您可以将字符串与整数连接,而不仅仅是字符串与字符串,无论如何都会产生字符串。例如,两种情况都有效:

"seek MyMp3 to " + **"** 11000 **"** = "seek MyMp3 to 11000",

"seek MyMp3 to " + 11000 = "seek MyMp3 to 11000"

所以还有更好的方法来制作 mci 字符串命令:

"seek MyMp3 to " + (elapsedTime + ffw5sec)

但是括号仍然很重要,因为没有,5000 与 mci 字符串命令连接,然后是 ffw5sec。结果又是错误的:

"seek MyMp3 to " + elapsedTime + ffw5sec = "seek MyMp3 to 50006000"

也不要使用 Timer 类,因为该类用于在每个间隔时间调用回调方法,而您在定时器和回调方法之外执行其他操作。

Timer 类不用于测量经过的时间。您应该改用 Stopwatch 类,它可以在 System.Diagnostics 命名空间中找到。系统引用已经添加到您的项目中是非常重要的,因此您可以在这个命名空间中找到这个类。

Stopwatch 类也有 Start 方法,与 Timer 类一样,但不同之处在于 Timer 类的 Start 方法开始执行 Elapsed 事件或 Tick 事件或构造函数中给出的回调函数,具体取决于您是什么计时器正如我之前提到的那样使用。

Stopwatch 类的实例与任何回调方法都没有联系。相反,它们具有私有时间数据字段,以及以某些形式返回该时间数据的公共属性。

有一个ElapsedMilliseconds属性可以返回秒表运行的所有时间(以毫秒为单位)所经过的时间(64 位整数)。

还有一个ElapsedTicks属性,与 相同ElapsedMilliseconds,但返回的时间以刻度为单位。

一秒内超过 1000 个滴答声超过了毫秒。

Elapsed 属性与 ElapsedMilliseconds 和 ElapsedTicks 相同,但时间完全不是作为长整数返回,而是作为 TimeSpan 结构,它也将秒表实例的私有时间数据存储为私有,也可以返回为 int ( 32 位整数),根据它的属性,在许多单位中为 long 或 double,例如天、小时、毫秒、分钟、秒、滴答声等。

无论如何,将 elapsedTime 的类型从 更改TimerStopwatch,并且不要忘记首先使用 System.Diagnostics 命名空间才能使用此类。您还需要在项目中引用系统参考。

现在终于可以修复命令了:

command = String.Format("seek MyMp3 to {0}", elapsedTime.ElapsedMilliseconds + ffw5sec);

这应该很好用,但不要忘记有时使用 Reset 或 Restart 方法将经过的时间重置为零。如果您希望秒表在经过时间重置为零后处于运行状态,请使用重新启动方法。否则只需使用重置方法。

如果仍然有问题,那么您将不得不使用 DllImport 来外部 mciGetErrorString 函数,并在 mciSendString 不起作用时使用它来找出您的问题,因为您输入了错误的参数。

C# 中 mciGetErrorString 的声明为:

[DllImport("winmm.dll")]
static extern Int32 mciGetErrorString(Int32 errorCode, StringBuilder errorText, Int32 errorTextSize);

建议将上面这段代码添加到您的 C# 程序中,无论在哪里声明的其他方法之间,但在声明入口点 Main 方法的 Program 类中。

每当 mciSendString 无法执行您输入的命令时,它会以整数形式返回错误代码。

您应该将该错误代码输入到 mciGetErrorString 中。mciGetErrorString 将修改 StringBuilder 实例并在其中写入错误字符串。

然后使用 Object.ToString() 或 Console.Write 或 Console.WriteLine 方法输出 StringBuilder 实例的字符串形式。

输出将描述错误,这就是 mciSendString 未能执行命令的原因。这将帮助您找出问题,并直接考虑您的解决方案。

我希望这一切都会对你有所帮助。祝你好运!

于 2014-12-20T13:10:16.037 回答