ofSoundBuffer corrupted last sample

Hello Everyone,

I have a little problem using ofSoundBuffer.
I’m streaming sound from a MS WASAPI microphone device through ofSoundStream.
Everything works fine except the final sample in the ofSoundBuffer is nonsense. (e.g. -8.10599937e+36).
This only occurs with a sample-rate of 44100 - so my first guess is there’s some sloppy re-sampling somewhere upstream. Anyone ever encountered something like this?

best,
MM

Hello @2666hz did you test with other mics too? Which platform do you use?
can you post a minimal code to reproduce your issue?
Cheers

Hi @2666hz there is a bug in the resampling algorithm. So if your sound card is setup to a different sample rate than what you set the ofSoundStream with it will resample and this bug will show up.

OK. Thanks.
Now my question is - why is it resampling?
I am only ever using samplerates from ofSoundDevice.sampleRates - do these not mean that the device supports these rates “natively”? or is the device capable of these samplerates, but actually setting the native samplerate done somehow elsewhere?
MM

The issues is that the sound device sample rate is set via the OS or some hardware specific software/control panel. As far as I can recall, OF is not capable of setting the hardware’s sample rate. Thus, when you setup OF to use a sample rate different than the one that is set to the hardware, it will need to resample. Thus, the easy way is to set up your computer’s sample rate and then use that same samplerate in openFrameworks.

not sure which platform are you in, but in macOs you can check system sample rate in an application called Audio Midi Setup

@dimitre I guess that windows because he is using MS WASAPI

1 Like

Yes - windows 10.

In my application, is there any way to get this “native” sample-rate?
Can I programmatically ensure that no re-sampling will take place?

MM

there might be a way to get that sample rate in windows but I am not sure. I dont use windows that much.

you could but it can be quite cumbersome. Let me know if you figured this out, otherwise I can check. I can not recall it from the top of my head.

i remember i had this problem quite a while ago and could copy&paste&adjust some demo code…

this was used in production, so it should “mostly work”…

Header:

#ifdef TARGET_WIN32
int ofxGetWasapiSampleRate(int deviceId);
#endif

Implementation: (double check the includes, you might not need all of them)

//-----------
//Either you need ofConstants, 
//or use the _WIN32 macro instead of TARGET_WIN32!!!!
#include "ofConstants.h"
//-----------


#ifdef TARGET_WIN32
#include <Mmdeviceapi.h>
#include <Mmreg.h>
#include <FunctionDiscoveryKeys_devpkey.h>
#include <winuser.h>
#include <commdlg.h>
#define _WIN32_DCOM

#include <windows.h>
#include <shlobj.h>
#include <tchar.h>
#include <stdio.h>
#endif


#ifdef TARGET_WIN32
int ofxGetWasapiSampleRate(int deviceId) {
	HRESULT hr;
	IMMDevice * pDevice = NULL;
	IMMDeviceEnumerator * pEnumerator = NULL;
	IPropertyStore* store = nullptr;
	PROPVARIANT deviceNameProperty;
	PWAVEFORMATEX deviceFormatProperties;
	PROPVARIANT prop;

	CoInitialize(NULL);

	// get the device enumerator
	hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (LPVOID *)&pEnumerator);
	if (FAILED(hr)) return 44100;

	// get default audio endpoint

	IMMDeviceCollection * devices;
	HRESULT res = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices);
	int resultRate = 0; 

	if (SUCCEEDED(res)) {
		uint32_t count = 0;
		devices->GetCount(&count);


		//		for (int i = 0; i < count; i++) 
		{
			// instead of the loop, we just get the first device: 
			int i = deviceId;
			if (i >= count || i < 0) return 44100;

			IMMDevice * dev;
			res = devices->Item(i, &dev);
			if (SUCCEEDED(res)) {
				res = dev->OpenPropertyStore(STGM_READ, &store);
				if (SUCCEEDED(res)) {
					res = store->GetValue(PKEY_AudioEngine_DeviceFormat, &prop);
					deviceFormatProperties = (PWAVEFORMATEX)prop.blob.pBlobData;

					res = store->GetValue(PKEY_Device_DeviceDesc, &prop);
					PropVariantInit(&deviceNameProperty);

					hr = store->GetValue(PKEY_Device_FriendlyName, &deviceNameProperty);
					if (FAILED(hr)) {
					}

					LPWSTR name = deviceNameProperty.pwszVal;
					std::cout << "Name = " << name << endl;
					LocalFree(name);
					std::cout << "Channels    = " << deviceFormatProperties->nChannels << std::endl;
					std::cout << "Sample rate = " << deviceFormatProperties->nSamplesPerSec << std::endl;
					std::cout << "Bit depth   = " << deviceFormatProperties->wBitsPerSample << std::endl;
					resultRate = deviceFormatProperties->nSamplesPerSec;
				}
				LocalFree(dev);
			}
		}
		LocalFree(devices);
	}

	if (resultRate > 0) return resultRate; 

	hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice);

	hr = pDevice->OpenPropertyStore(STGM_READ, &store);
	if (FAILED(hr)) {
		std::cout << "OpenPropertyStore failed!" << std::endl;
	}

	hr = store->GetValue(PKEY_AudioEngine_DeviceFormat, &prop);
	if (FAILED(hr)) {
		std::cout << "GetValue failed!" << std::endl;
	}

	deviceFormatProperties = (PWAVEFORMATEX)prop.blob.pBlobData;
	std::cout << "Channels    = " << deviceFormatProperties->nChannels << std::endl;
	std::cout << "Sample rate = " << deviceFormatProperties->nSamplesPerSec << std::endl;
	std::cout << "Bit depth   = " << deviceFormatProperties->wBitsPerSample << std::endl;
	return deviceFormatProperties->nSamplesPerSec;
}
#endif
1 Like

Thank you!
I will give it a go.
My software currently lets the user open a device using any of the available sound APIs - so ideally I would need the same code for all API. I would imagine that for the re-sampling to take place, it must first detect if re-sampling is needed at all. Am I right in understanding each incoming buffer comes with a sample-rate?

correct.
can you post the code that you are trying to use that gives you problems?