Author Topic: BASS_FX_BPM_DecodeGet() broken ?  (Read 1117 times)

aybe

  • Posts: 145
BASS_FX_BPM_DecodeGet() broken ?
« on: 15 Sep '11 - 18:31 »
Hello,

Shouldn't BASS_FX_BPM_BKGRND make BASS_FX_BPM_DecodeGet() return right away ? e.g. background processing
Actually the method returns only when the process is done.

UPDATE 2 :
I tried all that in-place as well, but exactly the same results (with no funny stuff like threading etc ...), never reaches 100%

UPDATE :
I have been doing some extra tests and came across an interesting issue now !
The function return like 99.96145 ... but never 100%
Since the background flag doesn't work I decided to create two delegates like BPMPROCESSPROC,
one provides the progress, the other provides the result, running on another thread.

In the doc. you wrote that sometimes the exact length isn't known until a certain time
but it's already a very good approximation though not pin-point ... and also about using BASS_STREAM_PRESCAN.

I tested with the original .WAV, it goes up to 99.98033 %,
then I compressed it to an 320kbps MP3, it goes till 99.96145 %

I did check the stream duration before and after, they are the same.

Could this be because of the file length detection ? Threading ?

Thank you  :D

Here's the code I did :
Should you want to try, you create a new WinForms C# project, drop two labels and you are set.
I have uploaded the files on your ftp as well

Thanks !!!

Code: [Select]
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Fx;

namespace FeatureDetector
{
    public partial class Form1 : Form
    {
        private readonly DetectionProgress _detectionProgress;
        private readonly DetectionResult<float> _detectionResult;

        public Form1()
        {
            InitializeComponent();
            _detectionProgress = Progress;
            _detectionResult = Result;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var stream = Stream.Create(@"c:\users\aybe\music\Didier Sinclair - Lovely Flight.wav",
                                       BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE |
                                       BASSFlag.BASS_STREAM_PRESCAN);

            var bpmDetector = new BpmDetector(stream, _detectionProgress, _detectionResult);
            bpmDetector.Detect();
        }

        private void Progress(object sender, float progress)
        {
            BeginInvoke((Action) (() => { label1.Text = progress.ToString(); }));
        }

        private void Result(object sender, float result)
        {
            BeginInvoke((Action) (() => { label2.Text = result.ToString(); }));
        }
    }

    public abstract class Detector<T>
    {
        /// <summary>
        ///   Feature detection target stream.
        /// </summary>
        protected readonly Stream Stream;

        private readonly DetectionProgress _detectionProgress;

        private readonly DetectionResult<T> _detectionResult;

        protected Detector(Stream stream, DetectionProgress detectionProgress, DetectionResult<T> detectionResult)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");
            if (detectionProgress == null)
                throw new ArgumentNullException("detectionProgress");
            if (detectionResult == null) throw new ArgumentNullException("detectionResult");

            if (!stream.Info.IsDecodingChannel)
                throw new ArgumentOutOfRangeException("stream", @"Stream is not a decoding stream.");

            Stream = stream;
            _detectionProgress = detectionProgress;
            _detectionResult = detectionResult;
        }

        protected void OnDetectionResult(T result)
        {
            var detectionResult = _detectionResult;
            if (detectionResult != null)
                detectionResult.BeginInvoke(this, result, null, null);
        }

        protected void OnDetectionProgress(float percent)
        {
            var detectionProgress = _detectionProgress;
            if (detectionProgress != null)
                detectionProgress.BeginInvoke(this, percent, null, null);
        }
    }

    public class BpmDetector : Detector<float>
    {
        private readonly BPMPROCESSPROC _onBpmProcessProc;
        private readonly ThreadStart _onThreadStart;

        public BpmDetector(Stream stream, DetectionProgress detectionProgress, DetectionResult<float> detectionResult)
            : base(stream, detectionProgress, detectionResult)
        {
            _onBpmProcessProc = OnBpmProcessProc;
            _onThreadStart = OnThreadStart;
        }

        public void Detect()
        {
            var thread = new Thread(_onThreadStart);
            thread.Start();
        }

        private void OnThreadStart()
        {
            /* Notice :
             * There are two delegates because detecting bpm doesn't work in background
             * and because progress never really reaches 100% */

            // Decode the bpm
            var bpm = BassFx.BASS_FX_BPM_DecodeGet(Stream.Handle, 0, Stream.Duration, 0,
                                                   BASSFXBpm.BASS_FX_BPM_BKGRND |
                                                   BASSFXBpm.BASS_FX_BPM_MULT2, _onBpmProcessProc);
            // Then submit result
            OnDetectionResult(bpm);
        }

        private void OnBpmProcessProc(int channel, float percent)
        {
            OnDetectionProgress(percent);
        }
    }

    /// <summary>
    ///   Represents the method that will receive detection result of a <see cref = "Detector{T}" />.
    /// </summary>
    /// <typeparam name = "T"></typeparam>
    /// <param name = "sender"></param>
    /// <param name = "result"></param>
    public delegate void DetectionResult<in T>(object sender, T result);

    /// <summary>
    ///   Represents the method that will receive detection progress of a <see cref = "Detector{T}" />.
    /// </summary>
    /// <typeparam name = "T"></typeparam>
    /// <param name = "sender"></param>
    /// <param name = "progress"></param>
    public delegate void DetectionProgress(object sender, float progress);

    public class Stream : IDisposable
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly int _handle;
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] private bool _isDisposed;

        private Stream(int handle)
        {
            _handle = handle;
        }

        public double Duration
        {
            get { return Bass.BASS_ChannelBytes2Seconds(_handle, Length); }
        }

        public int Handle
        {
            get { return _handle; }
        }

        public BASS_CHANNELINFO Info
        {
            get { return Bass.BASS_ChannelGetInfo(_handle); }
        }

        public bool IsDisposed
        {
            get { return _isDisposed; }
        }

        public long Length
        {
            get { return Bass.BASS_ChannelGetLength(_handle); }
        }

        #region IDisposable Members

        /// <summary>
        ///   Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            if (!_isDisposed)
            {
                Bass.BASS_StreamFree(_handle);
                _isDisposed = true;
            }
        }

        #endregion

        /// <summary>
        ///   Returns a <see cref = "T:System.String" /> that represents the current <see cref = "T:System.Object" />.
        /// </summary>
        /// <returns>
        ///   A <see cref = "T:System.String" /> that represents the current <see cref = "T:System.Object" />.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override string ToString()
        {
            return string.Format("Handle: {0}, IsDisposed: {1}", _handle, _isDisposed);
        }

        public static Stream Create(string file, BASSFlag flags)
        {
            if (file == null) throw new ArgumentNullException("file");

            ThrowIfNotInit();

            var handle = Bass.BASS_StreamCreateFile(file, 0, 0, flags);
            if (handle == 0)
                ThrowLastError();

            var stream = new Stream(handle);
            return stream;
        }

        private static void ThrowIfNotInit()
        {
            var init = Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
            var already = Bass.BASS_ErrorGetCode() == BASSError.BASS_ERROR_ALREADY;
            if (!init && !already)
                throw new InvalidOperationException("Couldn't initialize BASS.");
        }

        private static void ThrowLastError()
        {
            string message = Bass.BASS_ErrorGetCode().ToString();
            throw new InvalidOperationException(message);
        }
    }
}
« Last Edit: 19 Sep '11 - 19:22 by aybe »

(: JOBnik! :)

  • Posts: 1065
Re: BASS_FX_BPM_DecodeGet() broken ?
« Reply #1 on: 18 Sep '11 - 08:27 »
Hi ;D

Thanks for the info, I'll check it :)

aybe

  • Posts: 145
Re: BASS_FX_BPM_DecodeGet() broken ?
« Reply #2 on: 19 Sep '11 - 19:12 »
Alright, thanks  :D

aybe

  • Posts: 145
Re: BASS_FX_BPM_DecodeGet() broken ?
« Reply #3 on: 28 Sep '11 - 19:45 »
Hi ;D

Thanks for the info, I'll check it :)

Hi,

I just fixed it like that, the background is not working but in the end i just report the progress myself.

Code: [Select]
        private void OnStart()
        {
            var channel = Channel;
            var stream = channel.Stream;

            // Pass duration to the proc for reporting total progress
            var duration = channel.Duration;

            // Decode
            BASSFXBpm flags = 0; // Not running in background !
            var tempo = BassFx.BASS_FX_BPM_BeatDecodeGet(stream.Handle, 0, duration, flags, _bpmbeatproc,
                                                         new IntPtr((int)duration));

            // 100%
            if (tempo)
            {
                SetResult(new BeatCollection(_beats));
                ReportProgress(100, true);
            }
        }

        private void Bpmbeatproc(int handle, double beatpos, IntPtr user)
        {
            // Add position to list
            _beats.Add(beatpos);

            // Get duration from pointer and report progress
            var duration = user.ToInt32();
            var percent = (int)(1.0d / duration * beatpos * 100);
            ReportProgress(percent, false);
        }

Thanks !