Author Topic: How to get both FFT and wave data on a Record Device  (Read 98 times)

Houri

  • Guest
Hello,

I have a thread in a C# WinForms application which generates a bitmap containing the waveform using Bass.BASS_ChannelGetData(handle, buffer, size) and the flag BASSFlag.BASS_SAMPLE_FLOAT on RecordStart, this works well to get the PCM data in real-time, I am also looking to get the FFT data using  Bass.BASS_ChannelGetData(handle, fft =  new float[2048], (int)BASSData.BASS_DATA_FFT4096).

it seems that I can only use either the waveform or the FFT but not both at the same time, what happens is the first one used works fine but the second call to BASS_ChannelGetData will often return 0, the order of requesting FFT or wave data doesn't seem to matter, the first call works while the second doesn't.

How can I use both calls to BASS_ChannelGetData on the same iteration and get the wave data buffer and its FFT representation?

Is there a function that I can provide the wave data and it returns its FFT representation?

Thanks.

Ian @ un4seen

  • Administrator
  • Posts: 23404
Re: How to get both FFT and wave data on a Record Device
« Reply #1 on: 17 Feb '21 - 14:37 »
Is your recording using a RECORDPROC callback function? If so, it should be possible to get both FFT and PCM data from BASS_ChannelGetData, one after the other. If you don't use a RECORDPROC callback, the problem is that each BASS_ChannelGetData call will remove the data it used from the recording's buffer, so that data won't be available for the next call.

Houri

  • Guest
Re: How to get both FFT and wave data on a Record Device
« Reply #2 on: 17 Feb '21 - 21:23 »
Thanks for the reply, I was not using the record process delegate, I just attempted to do but it seems that I can't do any heavy processing on the data retrieved inside the delegate itself since it's running on Bass thread and it would block the processes, I can't even allocate a new float[] since it takes time as the documentation notes clearly, I will attempt to do some thread synchronisation (most likely a ConcurrentQueue<float>).

Do you have recommended solution to this?
What does Bass use internally to convert sample data into FFT and is it possible to replicate or access in C#?

Ian @ un4seen

  • Administrator
  • Posts: 23404
Re: How to get both FFT and wave data on a Record Device
« Reply #3 on: 18 Feb '21 - 16:03 »
Each recording with a RECORDPROC has its own thread, so processing delays are usually fine and won't affect anything else. The RECORDPROC's processing just needs to be faster than realtime speed to avoid the recording buffer overflowing. What processing are you doing? If the recording is just for visual purposes (eg. waveform/spectrum displays) then the RECORDPROC could do nothing (simply "return true") and you can make the BASS_ChannelGetData calls outside of it, eg. in a timer.

Regarding doing the FFT yourself (on the received PCM data), I'm not familiar with what FFT options are available for .Net but I'm sure there are some. Pretty much any FFT library should be fine.

Houri

  • Guest
Re: How to get both FFT and wave data on a Record Device
« Reply #4 on: 18 Feb '21 - 17:37 »
the processing I'm doing is amplifying then normalising the PCM then detecting patterns in both time and frequency domains, then visualise the sample waveform and FFT while highlighting the patterns and frequencies detected in one bitmap and publish both the PCM and FFT along with bitmap using SignalR, all this in one go doesn't complete as fast as real-time.

I like the FFT coming out of Bass library, the whole thing is combat and well documented so I'll just have to find to deal with the data on a separate thread, I'll come back with a solution once find one! thanks plenty for your help :)

on a separate matter, it seems that the output of the PCM depends on system (windows) volume, is there a flag I can set somewhere to get me the PCM data irrelevant of the system volume or mute status?

Houri

  • Guest
Re: How to get both FFT and wave data on a Record Device
« Reply #5 on: 18 Feb '21 - 18:21 »
a working solution for now is to start two recordings on the same device which will get us two handles, use one handle for PCM data and the other handle for getting FFT data, please let me know if you approve of this method :D

Ian @ un4seen

  • Administrator
  • Posts: 23404
Re: How to get both FFT and wave data on a Record Device
« Reply #6 on: 19 Feb '21 - 15:42 »
If the total processing is slower than realtime then I guess you're currently making the 2 BASS_ChannelGetData calls in different threads to split the workload and make it faster? One way you could still do that with a single recording is to use "push" streams (see BASS_StreamCreate) feeding worker threads, ie. the RECORDPROC sends the data to the push streams and the worker threads read from them. For example, if you want 2 threads to process the recorded data, you might do something like this:

Code: [Select]
// create push streams with same format as the recording
for (int a = 0; a < 2; a++)
pushstream[a] = BASS_StreamCreate(freq, chans, BASS_STREAM_DECODE, STREAMPROC_PUSH, NULL);

...

BOOL CALLBACK RecordProc(HRECORD handle, const void *buffer, DWORD length, void *user)
{
// send the data to the push streams
for (int a = 0; a < 2; a++)
BASS_StreamPutData(pushstream[a], buffer, length);

return TRUE; // continue recording
}

Each worker thread would then use BASS_ChannelGetData on their push stream to get the data. You can use the FFT flags in those calls if wanted.

Regarding the recording/input level, it isn't possible to bypass the system settings but they can be adjusted with BASS_RecordSetInput (or manually in Windows' Sound control panel).

Houri

  • Guest
Re: How to get both FFT and wave data on a Record Device
« Reply #7 on: 19 Feb '21 - 21:32 »
That's a neat solution, works like a charm.
Here's what I suggested previously for people reading this later on which also works but without using RECORDPROC

Code: [Select]
// Setup a device based on deviceIndex and two recordings one for PCM and the other for FFT, store the handles.
 // init the device
 if (!Bass.BASS_RecordInit(deviceIndex))
            {
                throw new Exception(Bass.BASS_ErrorGetCode().ToString());
            }
// get the device info
            var recordInfo = Bass.BASS_RecordGetInfo();
            frequency = recordInfo.freq;
            channels = recordInfo.Channels;
// start PCM recording
            pcmHandle = Bass.BASS_RecordStart(frequency, channels, BASSFlag.BASS_SAMPLE_FLOAT, null, IntPtr.Zero);
// start FFT recording
            fftHandle = Bass.BASS_RecordStart(frequency, channels, BASSFlag.BASS_SAMPLE_FLOAT, null, IntPtr.Zero);
// use this to delay your thread, milliseconds.
            var ms = (int)Math.Round((1.0 / frequency) * 1000000);

...

//in your thread
// get the pcm data
float[] buffer = null;
// checked available data size
            var size = Bass.BASS_ChannelGetData(pcmHandle, buffer, (int)BASSData.BASS_DATA_AVAILABLE);
            if (size > 0)
            {
              // create a float buffer based on the byte size divided by 4, 4 bytes in a one float
            buffer = new float[size / 4];
// fill the buffer with the data.
            Bass.BASS_ChannelGetData(pcmHandle, buffer, size);
            }

 var fft = new float[2048];
if(Bass.BASS_ChannelGetData(fftHandle, fft, (int)BASSData.BASS_DATA_FFT4096)>0){
// process fft buffer.
}

// don't forget to free and dispose of your resources once your app is done.


Houri

  • Guest
Re: How to get both FFT and wave data on a Record Device
« Reply #8 on: 19 Feb '21 - 21:41 »
For the audio level, I found that Bass.BASS_RecordGetInput gets you the level which you can use to amplify the signal with  value / volume * 100.

although I ended up by setting all the applications and system volume to 100 and controlling the volume with hardware to get the correct amplitudes.