Author Topic: How to use WASAPI stream  (Read 330 times)

zak_8

  • Posts: 5
How to use WASAPI stream
« on: 8 Jun '17 - 14:46 »
I am trying to write a program that takes the speakers stream and counts the BPM (in real time), for that I'm using BPMCounter of BASS lib.

Code example:

Code: [Select]
            // create a stream
        Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
        _stream = Bass.BASS_StreamCreateFile("file.mp3", 0, 0, BASSFlag.BASS_DEFAULT);
        Bass.BASS_ChannelSetAttribute(_stream, BASSAttribute.BASS_ATTRIB_VOL, 0.3f); // volume of the stream
        Bass.BASS_ChannelGetInfo(_stream, info);
And the following code calculates the beat:

Code: [Select]
private void timerBPM_Tick(object sender, System.EventArgs e)
        {
            if (_stream == 0 || Bass.BASS_ChannelIsActive(_stream) != BASSActive.BASS_ACTIVE_PLAYING)
            {
                this.timerBPM.Stop();
                return;
            }
            bool beat = _bpm.ProcessAudio(_stream, true);
            if (beat)
            {
                this.labelBPM = _bpm.BPM.ToString("#00.0");
                Console.WriteLine(this.labelBPM);
            }
This example using of MP3 file as stream and i want to use the speakers sound instead of the file. How can i do convert from WASAPI stream to stream like BASS_StreamCreateFile?
« Last Edit: 8 Jun '17 - 14:50 by zak_8 »

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: How to use WASAPI stream
« Reply #1 on: 8 Jun '17 - 16:58 »
It sounds like you want to get the BPM of the sound that's playing on an output device? If so, you will need to use the corresponding WASAPI "loopback" input device. You will need the BASSWASAPI add-on, with which you can access the default loopback device by using device=-3 in a BASS_WASAPI_Init call. As you're using BASS.Net, you could try its BassWasapiHandler class (which makes the BASS_WASAPI_Init/etc calls for you), and use its "InputChannel" property in your "_bpm.ProcessAudio" call.

zak_8

  • Posts: 5
Re: How to use WASAPI stream
« Reply #2 on: 8 Jun '17 - 23:43 »
FIrst of all, thank you for your answer.

I have tried to use your advice and I wrote the following:
Code: [Select]
public partial class Form1 : Form
    {
        private DispatcherTimer timerBPM;
        private BassWasapiHandler _wasapi;
        string labelBPM = null;
        // create a BPMCounter instance, the timer will be fired every 20ms
        private BPMCounter _bpm = new BPMCounter(20, 44100);


        public Form1()
        {
            InitializeComponent();

            var device = BassWasapi.BASS_WASAPI_GetDeviceInfo(5);
            // device.name = "Speakers / Headphones (Realtek High Definition Audio)"
            // create a stream
            Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);

            _wasapi = new BassWasapiHandler(5, false, 48000, 2, 0f, 0f);
            Console.WriteLine("WASAPI init");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            timerBPM = new DispatcherTimer();
            timerBPM.Tick += new EventHandler(timerBPM_Tick);
            timerBPM.Interval = TimeSpan.FromMilliseconds(20); //40hz refresh rate

            this.timerBPM.Start();
        }


        private void timerBPM_Tick(object sender, System.EventArgs e)
        {
            bool beat = _bpm.ProcessAudio(_wasapi.InputChannel, true);
            if (beat)
            {
                this.labelBPM = _bpm.BPM.ToString("#00.0");
                Console.WriteLine(this.labelBPM);
            }
        }
    }

it still doesn't work as Bass.BASS_StreamCreateFile, What am I doing wrong?

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: How to use WASAPI stream
« Reply #3 on: 9 Jun '17 - 17:41 »
Is WASAPI device #5 a loopback device or is it the output device? It should be the loopback device. I think you can use the "IsLoopback" property to check that with BASS.Net. You also need to call the Init and Start methods on the BassWasapiHandler. An example code snippet can be found in the BassWasapiHandler documentation:

   www.bass.radio42.com/help/html/3cc63e6e-272a-36dc-cedb-248d52eeeaf3.htm

By the way, if you won't be playing anything via BASS, then you could use the "no sound" device (device 0) in the BASS_Init call.

zak_8

  • Posts: 5
Re: How to use WASAPI stream
« Reply #4 on: 11 Jun '17 - 00:30 »
Thank you, I've done all your instructions and it still doesn't work like the stream of Bass.BASS_StreamCreateFile.
I played the following link in the background:
https://www.youtube.com/watch?v=usctjhJGYic&t=47s
This is 80 BPM Metronome Click Track and I expected to prints of 80s in the console, similar to the results I got from Bass.BASS_StreamCreateFile.

WASAPI device #5 is a loopback as you can see below.
In addition, I've also called to init and start methods on the BassWasapiHandler.

This is the new code (after chnages):
Code: [Select]
public partial class Form1 : Form
    {
        private DispatcherTimer timerBPM;
        private BassWasapiHandler _wasapi;
        string labelBPM = null;
        // create a BPMCounter instance, the timer will be fired every 20ms
        private BPMCounter _bpm = new BPMCounter(20, 44100);

        private WASAPIPROC _process;        //callback function to obtain data


        public Form1()
        {
            InitializeComponent();

            _process = new WASAPIPROC(Process);

            var device = BassWasapi.BASS_WASAPI_GetDeviceInfo(5);
            // device.name = "Speakers / Headphones (Realtek High Definition Audio)"
            //device.IsLoopback = true

            // setup BASS - "no sound" device
            Bass.BASS_Init(0, 48000, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);

            // assign WASAPI output in shared-mode
            _wasapi = new BassWasapiHandler(5, false, 0, 0, 1f, 0.05f);
            // init and start WASAPI
            _wasapi.Init();
            Console.WriteLine("WASAPI init");
            _wasapi.Start();
            Console.WriteLine("WASAPI started");

            timerBPM = new DispatcherTimer();
            timerBPM.Tick += new EventHandler(timerBPM_Tick);
            timerBPM.Interval = TimeSpan.FromMilliseconds(20); //40hz refresh rate

            this.timerBPM.Start();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ///
        }


        // WASAPI callback, required for continuous recording
        private int Process(IntPtr buffer, int length, IntPtr user)
        {
            return length;
        }

        private void timerBPM_Tick(object sender, System.EventArgs e)
        {
            bool beat = _bpm.ProcessAudio(_wasapi.InputChannel, true);
            if (beat)
            {
                this.labelBPM = _bpm.BPM.ToString("#00.0");
                Console.WriteLine(this.labelBPM);
            }
        }
    }

What can i do better?
Thanks.
« Last Edit: 11 Jun '17 - 13:59 by zak_8 »

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: How to use WASAPI stream
« Reply #5 on: 12 Jun '17 - 17:16 »
I have never actually used BASS.Net's BassWasapiHandler class myself (I'm not a .Net user), so I'm not entirely sure how to use it, but from the BassWasapiHandler.InputChannel documentation, it looks like you would need to set a DSP function on the "InputChannel" handle (via BASS_ChannelSetDSP) and then make the "_bpm.ProcessAudio" call in that (instead of in a timer). It looks like you also need to set BassWasapiHandler.UseInput to true.

radio42

  • Posts: 4563
Re: How to use WASAPI stream
« Reply #6 on: 13 Jun '17 - 07:35 »
Correct.
The used constructor of the WasapiHandler should automatically detect, if the given device 'SupportsRecording' and as such set the 'UseInput' property to true (you might check this during debugging).
As such, a dummy 'InputChannel' is created, which can be used to setup any DSP/FX to be applied to the Wasapi input or just retrieve the recorded sample data.

zak_8

  • Posts: 5
Re: How to use WASAPI stream
« Reply #7 on: 19 Jun '17 - 08:10 »
I solved it by using OutputChannel attribute in _bpm.ProcessAudio calling.