带有 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 的音频播放/倒带功能的主要内容,如果未能解决你的问题,请参考以下文章
HTML5 音频:设置 currentTime 然后在 iPhone 上播放自发倒带 50-100 毫秒