带有 winmm3.dll 的音频播放/倒带功能

Posted

技术标签:

【中文标题】带有 winmm3.dll 的音频播放/倒带功能【英文标题】:Audio playback/rewind function with winmm3.dll 【发布时间】:2014-03-15 17:38:23 【问题描述】:

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

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

我在播放功能启动时启动一个计时器。

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

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

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

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;
            
         

    

【问题讨论】:

【参考方案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

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

System.Timers.Timer的字符串形式为"System.Timers.Timer"System.Threading.Timer的字符串形式为"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.Timer 还是 System.Windows.Forms.Timer

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

"seek 0 to 1"

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

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

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

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

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

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

我想说的是,你不应该调用 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一样,但是返回的时间是以tick为单位的。

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

Elapsed 属性与 ElapsedMilliseconds 和 ElapsedTicks 相同,但时间完全不是作为长整数返回,而是作为 TimeSpan 结构,将秒表实例的私有时间数据也存储为私有,也可以返回为可以是 int(32 位整数)、long 或 double,取决于它的属性,如 Days、Hours、Milliseconds、Minutes、Seconds、Ticks 等。

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

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

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

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

如果仍然有问题,那么你将不得不使用 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 未能执行命令的原因。这将帮助您找出问题,并直接思考您的解决方案。

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

【讨论】:

以上是关于带有 winmm3.dll 的音频播放/倒带功能的主要内容,如果未能解决你的问题,请参考以下文章

在网页上播放/暂停/倒带 mp3 音频

在 xuggler 中转发和倒带音频

HTML5 音频:设置 currentTime 然后在 iPhone 上播放自发倒带 50-100 毫秒

jPlayer - 通过 Javascript 实现倒带功能

iPhone中的自定义音频播放器

uniapp实现音频播放抢占系统音频焦点