Author Topic: BASS_ChannelGetLength problem  (Read 9604 times)

jwinder

  • Guest
BASS_ChannelGetLength problem
« on: 23 Sep '09 - 08:24 »
Hi all,

I'm using BASS .NET to 'stream' a file from local storage. Specifically, I'm using BASS_StreamCreateFileUser in buffered (non-push) mode. If you're wondering why, it's just a test before I begin using real streams from the internet.

The problem is, that whenever I try to get the length of the channel I get ridiculous values. For example, 18814299402240. Even results from BASS_ChannelGetPosition seem a little high - with values of over 1,000,000 being returned a few seconds into playback of a standard MP3 (roughly 5,000,000 bytes in length).

Interestingly, once the stream has finished playing (long after the reading has completed) the BASS_ChannelGetLength  function then returns a much lower number (which is the same as BASS_ChannelGetPosition) although it is still too high - i.e. roughly 40,000,000 for my test MP3.

Does anyone have a clue what I might be doing wrong? I have tried setting the BASS_STREAM_PRESCAN flag in the BASS_StreamCreateFileUser function, but it doesn't seem to make any difference.

I'm using the WinCE version, and the BASS.NET CE wrapper.

radio42

  • Posts: 4737
Re: BASS_ChannelGetLength problem
« Reply #1 on: 23 Sep '09 - 09:28 »
With BASS_StreamCreateFileUser you would actually have to implement all the BASS_FILEPROCS yourself.
The buffered file system (STREAMFILE_BUFFER) is what is used by BASS_StreamCreateURL. As the name suggests, data from the file is buffered so that it is readily available for decoding; BASS creates a thread dedicated to "downloading" the data. This is ideal for when the data is coming from a source that has high latency, like the internet. It is not possible to seek in buffered file streams, until the download has reached the requested position; it is not possible to seek at all if it is being streamed in blocks

The problem is, that with the STREAMFILE_BUFFER system the length or the position is not available once the complete file has been downloaded/processed.
So I guess you are receiving "-1" as the return values in your BASS_ChannelGetLength resp. BASS_ChannelGetPosition calls (indicating, that an error occurred).
And this means, that the length/position is not available as explained above.

So as you are streaming from a local file, why don't you use the STREAMFILE_NOBUFFER system resp. directly use BASS_StreamCreateFile?

Ian @ un4seen

  • Administrator
  • Posts: 24313
Re: BASS_ChannelGetLength problem
« Reply #2 on: 23 Sep '09 - 16:19 »
What value are you returning in your FILELENPROC function? Also, are you using the BASS_STREAM_BLOCK flag in the BASS_StreamCreateFileUser call? The BASS_STREAM_PRESCAN flag indeed doesn't apply when using the buffered file system.

Regarding the BASS_ChannelGetPosition/Length return values, please note that they are decoded byte positions. So for example, 40000000 equates to just under 227 seconds @ 44100hz stereo 16-bit, and 1000000 would be just under 6 seconds.

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #3 on: 24 Sep '09 - 21:40 »
Thanks for your replies!

My FILELENPROC function is returning the length of the file in bytes - specifically the length of the stream. In this case, it is roughly 4,320,000 (A 4.3MB file). Is this what I should be returning?

I'm not using BASS_STREAM_BLOCK flag. Normally I'm just using 'BASS_DEFAULT', but tried using BASS_STREAM_PRESCAN to see if it made a difference (it didn't).

Thanks for the clarification of the ChannelGetPosition/ChannelGetLength return values!

Here is the initialisation of the stream:

        Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero)
        this.bassStream = Bass.BASS_StreamCreateFileUser(BASSStreamSystem.STREAMFILE_BUFFER,
                BASSFlag.BASS_DEFAULT, this.fileProcs, IntPtr.Zero);

And here are my callback functions. Seek and Close, I've not bothered to implement yet - I doubt this is an issue? 'inStream' in this case is just a standard FileStream (although will be a NetworkStream in future).

        private int Bass_StreamSeek(long offset, IntPtr user)
        {
            return 0;
        }

        private int Bass_StreamRead(IntPtr buffer, int length, IntPtr user)
        {
            byte[] readBuffer = new byte[length];
            int bytesRead =  this.inStream.Read(readBuffer, 0, length);

            if (bytesRead > 0)
            {
                this.inStreamPosition += bytesRead;
                Marshal.Copy(readBuffer, 0, buffer, bytesRead);
            }

            return bytesRead;
        }

        private int Bass_StreamLength(IntPtr user)
        {
            return (int)this.inStreamLength;
        }

        private void Bass_StreamClose(IntPtr user)
        {
        }

Hope you can help me!

Ian @ un4seen

  • Administrator
  • Posts: 24313
Re: BASS_ChannelGetLength problem
« Reply #4 on: 25 Sep '09 - 13:58 »
That code looks fine. Can you please confirm what the problem is, is it just that the initial length estimate is wrong? If so, that will be based on the file's contents, so please upload the file in question to have a look at here...

   ftp.un4seen.com/incoming/

You could also try playing the file with the pre-compiled PLUGINS.EXE example, and see what it says about the length.

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #5 on: 25 Sep '09 - 15:28 »
I've just tried the same code in a Win32 application using the standard BASS.NET DLL and it works fine - the length returned is accurate and consistent (as is the calculated duration). The same code in a WinCE application, using the BASS.NET CE version returns the incorrect length. So perhaps it's a problem with the WinCE version?

I'll upload an example file, but I've tried it with three so far and get incorrect results with them all.

Hope you can help! :)

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #6 on: 25 Sep '09 - 15:44 »
There seems to be an issue with your FTP server at the moment. I'll try again later. Like I say, the issue doesn't appear to be specific to any one file anyway!

Ian @ un4seen

  • Administrator
  • Posts: 24313
Re: BASS_ChannelGetLength problem
« Reply #7 on: 25 Sep '09 - 17:09 »
Yep, it looks like the FTP server was down at that time. It should be back up now.

Please do also try to reproduce the problem with the pre-compiled PLUGINS.EXE example included in the WinCE package, to confirm whether it applies to BASS in general, or just your app/BASS.Net.

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #8 on: 25 Sep '09 - 17:32 »
I tried the plugins example and indeed it does return the correct length! So it must either be a problem with my code, or with BASS .NET, I guess? Here is my StreamPlayer class in it's entirety:

Code: [Select]
   internal class StreamPlayer : IDisposable
    {
        #region Fields

        private int bassStream;
        private BASS_FILEPROCS fileProcs;

        private Stream inStream;
        private long inStreamLength;
        private long inStreamPosition;

        #endregion Fields

        #region Constructors

        public StreamPlayer(string email, string key)
        {
            BassNet.Registration(email, key);

            if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero) == false)
                throw new Exception("BASS failed to initialise");

            this.bassStream = -1;
            this.fileProcs = new BASS_FILEPROCS(this.Bass_StreamClose, this.Bass_StreamLength, this.Bass_StreamRead, this.Bass_StreamSeek);
            this.inStream = null;
            this.inStreamLength = -1;
            this.inStreamPosition = -1;
        }

        #endregion Constructors

        #region IDisposable Methods

        public void Dispose()
        {
            this.ReleaseStream();
            Bass.BASS_Free();
        }

        #endregion IDisposable Methods

        #region Methods

        public void SetStream(Stream s, long length)
        {
            // Release the current stream, if one exists
            this.ReleaseStream();

            // Set the input stream
            this.inStream = s;
            this.inStreamLength = length;
            this.inStreamPosition = 0;

            // Create the new stream
            this.bassStream = Bass.BASS_StreamCreateFileUser(BASSStreamSystem.STREAMFILE_BUFFER,
                BASSFlag.BASS_DEFAULT, this.fileProcs, IntPtr.Zero);
        }

        public bool Play()
        {
            if (this.bassStream != -1)
                return Bass.BASS_ChannelPlay(this.bassStream, false);
            else
                return false;
        }

        public bool Pause()
        {
            if (this.bassStream != -1)
                return Bass.BASS_ChannelPause(this.bassStream);
            else
                return false;
        }

        public bool Stop()
        {
            if (this.bassStream != -1)
            {
                this.inStream.Close();
                Bass.BASS_ChannelStop(this.bassStream);
                this.bassStream = -1;

                return true;
            }
            else
            {
                return false;
            }
        }

        public long GetBufferPosition()
        {
            return this.inStreamPosition;
        }

        public long GetChannelPosition()
        {
            if (this.bassStream != -1)
                return Bass.BASS_ChannelGetPosition(this.bassStream, BASSMode.BASS_POS_BYTES);
            else
                return -1L;
        }

        public long GetChannelLength()
        {
            if (this.bassStream != -1)
                return Bass.BASS_ChannelGetLength(this.bassStream, BASSMode.BASS_POS_BYTES);
            else
                return -1L;
        }

        public double GetEstimatedPositionSeconds()
        {
            if (this.bassStream != -1)
                return Bass.BASS_ChannelBytes2Seconds(this.bassStream, Bass.BASS_ChannelGetPosition(this.bassStream));
            else
                return -1.0;
        }

        public double GetEstimatedDurationSeconds()
        {
            if (this.bassStream != -1)
                return Bass.BASS_ChannelBytes2Seconds(this.bassStream, Bass.BASS_ChannelGetLength(this.bassStream));
            else
                return -1.0;
        }

        private void ReleaseStream()
        {
            if (this.bassStream != -1)
            {
                Bass.BASS_ChannelStop(this.bassStream);
                Bass.BASS_StreamFree(this.bassStream);
            }
        }

        #endregion Methods

        #region Bass Stream Callbacks

        private int Bass_StreamSeek(long offset, IntPtr user)
        {
            return (int)this.inStream.Seek(offset, SeekOrigin.Begin);
        }

        private int Bass_StreamRead(IntPtr buffer, int length, IntPtr user)
        {
            byte[] readBuffer = new byte[length];
            int bytesRead = this.inStream.Read(readBuffer, 0, length);

            if (bytesRead > 0)
            {
                this.inStreamPosition += bytesRead;
                Marshal.Copy(readBuffer, 0, buffer, bytesRead);
            }

            return bytesRead;
        }

        private int Bass_StreamLength(IntPtr user)
        {
            return (int)this.inStreamLength;
        }

        private void Bass_StreamClose(IntPtr user)
        {
            this.inStream.Close();
        }

        #endregion Bass Stream Callbacks
    }



radio42

  • Posts: 4737
Re: BASS_ChannelGetLength problem
« Reply #9 on: 25 Sep '09 - 21:37 »
Yes, that is true.
In the .Net compact framework (and such in the BASS.NET CE version) an external function call cannot return a QWORD (long in C#) .
So as with a lot of other functions I needed to change that to a DWORD (int).

So the original signature of the FILELENPROC returns a QWORD (long), but due to the mashaling limitation of the .Net compact framework it can only return a DWORD (int).

Lets see, if we can find any workaround this for you. But that yould mean, that I would need another 'special' function in the bass.dll just for the CE version.

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #10 on: 26 Sep '09 - 21:18 »
Well, either way I need a solution as if I can't get the length of the audio stream then it's a pretty big limitation of my application! Can anyone spot any issues with my code?

Ian @ un4seen

  • Administrator
  • Posts: 24313
Re: BASS_ChannelGetLength problem
« Reply #11 on: 28 Sep '09 - 12:32 »
An updated BASS.DLL is in the CE package now, which will treat the FILELENPROC return value as a DWORD (rather than QWORD) when used with BASS.Net, to account for the lack of 64-bit return values with the compact framework...

   www.un4seen.com/forum/?topic=9534

It does mean that you are limited to 4GB files, but I guess that won't be much of an issue on a mobile device.

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #12 on: 28 Sep '09 - 13:10 »
Awesome! I'll try it later and see if it helps. It's only a music / MP3 player so I don't (realistically) need to support files over 4GB anyway.

Many thanks! I'll let you know how it goes.

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #13 on: 28 Sep '09 - 18:49 »
:(

It didn't make any difference! A 3:47 minute MP3 is still saying it is 09:19 in length.

I tried changing from BASS_StreamCreateFileUser to BASS_StreamCreateFile, opening the file just with the file name and that does return the correct length. Unfortunately this is not a viable solution as I eventually need to be able to play the MP3 from a (network) stream.

This would indicate that the problem is either with my file procs (see above) or with the BASS_StreamCreateFileUser implementation in the BASS CE DLL.

Please help. :'(

Ian @ un4seen

  • Administrator
  • Posts: 24313
Re: BASS_ChannelGetLength problem
« Reply #14 on: 29 Sep '09 - 17:54 »
The 64-bit vs 32-bit issue could have explained a BASS_ChannelGetLength value of 18814299402240 (as in the original post), but I don't think it'd explain a length of 9:19 (assuming that is minutes:seconds :)), as that would be well within the 32-bit limit, so that's probably down to something else. Is the problem happening with all files, or only a particular file?

radio42

  • Posts: 4737
Re: BASS_ChannelGetLength problem
« Reply #15 on: 29 Sep '09 - 18:19 »
And which function do you actually use to calculate the length?
What is your "GetEstimatedDurationSeconds" returning exactly?

jwinder

  • Guest
Re: BASS_ChannelGetLength problem
« Reply #16 on: 30 Sep '09 - 09:50 »
I just tried another MP3 and... it worked!

I'll run some more tests tonight if I get a chance - maybe do a comparison of BASS_StreamCreateFile vs BASS_StreamCreateFileUser streams. Hopefully we can find out what the problem is from that! :p