2

I am working on a mp3 player with C# using the winmm.dll

I have problem now with my rewind function.

I start a Timer when the Play function starts.

When I start the FFW function I want it ffw 5000ms from the current value of elapsedTime.

for example if I start playback of a song and press FFW 6seconds into the song, playback should continue at 6000ms + 5000ms(ffw5sec).

But nothing is happening. What did I do wrong this time ?

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;
            }
         }

    }
}
user2864740
  • 60,010
  • 15
  • 145
  • 220
xoxo_tw
  • 269
  • 1
  • 7
  • 20

1 Answers1

1

In this line of code:

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

You concatenate the string form of elapsedTime and ffw5sec, and the string form of elapsedTime is not parsable, i.e. you cannot parse it to an integer. Also in your question you don't tell others which timer you are using exactly. In C# .NET framework, there are three kinds of Timer classes.

There is:

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

But neither the string form of any of these timer classes is not parsable.

The string form of System.Timers.Timer is "System.Timers.Timer", the string form of System.Threading.Timer is "System.Threading.Timer", and the string form of System.Windows.Forms.Timer is "System.Windows.Forms.Timer, Interval: {0}", where {0} is replaced by System.Windows.Forms.Timer.Interval property.

I know anyway that you don't use System.Threading.Timer, because in the code you posted in your question I see that you used an empty constructor to create the timer Timer(), and System.Threading.Timer does not contain a constructor that takes 0 arguments, but still I don't know if it is either System.Timers.Timer or System.Windows.Forms.Timer.

The syntax of the string format of the seek command is:

"seek {0} to {1}"

where {0} is replaced by either the path and name or alias of the audio file to seek, in your case, {0} is MyMp3. And {1} is replaced by the time in the audio file, which must be an integer only

The expression: (elapsedTime.ToString() + ffw5sec.ToString()), doesn't return a string that can be parsed to an integer at all.

The driver cannot carry out this command.

It is also recommended to use String.Format method to prepare and make your mci string commands for mciSendString function, instead of using concatenating strings method, because String.Format method is easier to use and more convenient than the method of concatenating strings. This way you won't get confused and make mistakes. Also formatted mci string commands by String.Format are more readable than concatenated mci string commands.

Another mistake that you have done is that you first convert elapsedTime and ffw5sec to string and then concatenate them together, and then concatenate the result string to the mci string command.

This is wrong to do it like that. You should first add the integer values of elapsedTime and ffw5sec, which represent the time, and then convert the result to string and concatenate it to the mci string command.

What I am trying to say is that you should not call ToString twice, but you can use it once.

The wrong way that you made the mci string command is:

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

The correct way to make the mci string command is:

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

This will work only in case that elapsedTime is integer, but if not, then you should use one of its properties that return the time as integer.

If the string form of elapsedTime was parsable, by your mistake, the mci string command was: "seek MyMp3 to 50006000", because you concatenate "5000" with "6000", which the result is "50006000".

But in the correct way, the mci string command should be: "seek MyMp3 to 11000", because you first add 5000 and 6000, which the result is 11000 as integer.

You even don't have to use ToString() at all, because you can concatenate string with integer, not only string with string, which results string anyway. For example both cases work:

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

and

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

So there is even better way to make the mci string command:

"seek MyMp3 to " + (elapsedTime + ffw5sec)

But still the brackets are important, because without, 5000 was concatenated with the mci string command, and then ffw5sec. The result was again the wrong one:

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

Also don't use Timer class at all, because this class is used to invoke callback method every interval time, while you are doing something else outside the timer and the callback method.

The Timer class is not used for to measure elapsed time. You should use the Stopwatch class instead, which can be found in System.Diagnostics namespace. It is very important that the System reference has already been added to your project, so you can find this class in this namespace.

The Stopwatch class also has Start method, like the Timer class, but the difference is that the Start method of the Timer class starts executing the callback function given in either Elapsed event, or Tick event or in the constructor, according to what timer you are using as I mentioned before.

Instances of the Stopwatch class have no contact to any callback method. Instead they have private time data field, and public properties to return that time data in some forms.

There is ElapsedMilliseconds property that returns the time as long (64 bits integer) that was elapsed while all the time that Stopwatch was running measured in milliseconds unit.

There is also ElapsedTicks property, which is same as ElapsedMilliseconds, but the returned time is measured in ticks unit.

Much more than 1000 ticks pass in one second than milliseconds.

The Elapsed property is same as ElapsedMilliseconds and ElapsedTicks, but the time is not returned as long integer at all, but as TimeSpan structure, which stores the private time data of the stopwatch instance as private too, and also can be returned as either int (32 bits integer), long or double in many units by it's properties, like Days, Hours, Milliseconds, Minutes, Seconds, Ticks and et cetera.

Anyway, change the type of elapsedTime from Timer to Stopwatch, and don't forget first to use System.Diagnostics namespace in order to use this class. Also you need in your project references the System reference.

Now at last, you can fix your command:

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

This should work great, but don't forget sometimes to use either Reset or Restart method to reset the elapsed time to zero. Use Restart method if you want Stopwatch to be in running state after that elapsed time was reset to zero. Otherwise just use the Reset method.

If there is still problem, then you will have to use DllImport to extern the mciGetErrorString function and use it to find out your problems when mciSendString doesn't work, because you input in it incorrect parameters.

The declaration of mciGetErrorString in C# is:

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

It is recommended to add the above piece of code into your C# program between the other declared methods no matter where, but in the Program class where the entry point Main method is declared.

Whenever mciSendString cannot carry out the command that you input into it, it returns an error code as integer.

You should input that error code into mciGetErrorString. mciGetErrorString will modify the StringBuilder instance and write in there the error string.

Then output the string form of the StringBuilder instance using either Object.ToString() or Console.Write or Console.WriteLine method.

The output will describe the error, which is the reason why mciSendString failed to carry out the command. This will help you to find out your problem, and think directly to your solution.

I hope that all this will help you. Good Luck!

Alex Essilfie
  • 12,339
  • 9
  • 70
  • 108