Page 1 of 1

Get the sample rate of the default playback audio device

Posted: Mon Nov 23, 2020 7:43 pm
by myQwil
I'm currently working on some real-time audio generation and I want the sample rate to be whatever the playback device's sample rate happens to be, so as to prevent resampling from occurring. I want to avoid hard-coding the sample rate to an arbitrary amount like 44100 or 48000. Is there a way to retrieve this value?

Re: Get the sample rate of the default playback audio device

Posted: Tue Nov 24, 2020 11:24 am
by breuning
Depends on the OS, I think you can find audio device sample rate in the Windows Registry

For me (Win10 x64) it is:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render
The subfolders are named after all the audio device GUID, the active one will have a DeviceState 0x0000001 word

In HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render\{DEVICEGUID}\Properties
You'll find the values "{f19f064d-082c-4e27-bc73-6882a1bb8e4c},0" and "{e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},0", which according to my search control the sampling rate. They contain binary data and I have no idea what to make of that.

I also found this: https://stackoverflow.com/questions/262 ... o-playback

Re: Get the sample rate of the default playback audio device

Posted: Tue Nov 24, 2020 2:06 pm
by zorg
There might be a cleaner (and OS-independent) solution;

Unfortunately i have not found an online function list that OpenALSoft implements (which löve uses), so i can't give exact examples at the moment, but if there's a method to query the currently used device's sampling rate, then you can use the LuaJIT FFI (which löve has) to declare that method, and call it from lua.

Code example missing due to above reason. https://github.com/kcat/openal-soft/wik ... troduction
Edit: Maybe something like this, untested:

Code: Select all

local ffi = require 'ffi'
local liblove = (ffi.os == "Windows") and ffi.load('love') or ffi.C
rffi.cdef[[
typedef struct ALCcontext ALCcontext;
typedef struct ALCdevice ALCdevice;
typedef int ALCenum;
typedef int ALCsizei;
typedef int ALCint;
typedef void ALCvoid;

void alcGetIntegerv(
    ALCdevice* device,
    ALCenum param,
    ALCsizei size,
    ALCint* data
);
ALCcontext* alcGetCurrentContext(ALCvoid);
ALCdevice* alcGetContextsDevice(ALCcontext *context);
]]

srate = ffi.new('ALCint',1)
local ALC_FREQUENCY = 0x1007
local context = liblove.alcGetCurrentContext()
local device = liblove.alcGetContextsDevice(context)
liblove.alcGetIntegerv(device, ALC_FREQUENCY, srate)
print(tostring(srate[1]))

Do note though, that resampling shouldn't cause too many issues in most cases anyway, although i do recall some people who did say otherwise. :3

Re: Get the sample rate of the default playback audio device

Posted: Tue Nov 24, 2020 7:21 pm
by myQwil
That works, thank you :)
I needed to make a slight change to get it working:

Code: Select all

	buf = Buffer(4)
	local ALC_FREQUENCY = 0x1007
	local context = liblove.alcGetCurrentContext()
	local device = liblove.alcGetContextsDevice(context)
	liblove.alcGetIntegerv(device, ALC_FREQUENCY, 1, buf:ptr())
	print(buf:at(0))
Buffer is a custom class that's part of the C library I'm currently working on. It's basically just a thin wrapper around a std::vector that I was using as a fallback for test case scenarios. Just for the sake of reference, it looks like this:

Code: Select all

	
class Buffer {
	vector<unsigned char> data;
public:
	Buffer   (size_t n)   { data.reserve(n);        }
	~Buffer  ()           { data.shrink_to_fit();   }
	void *ptr(size_t i=0) { return &data[i];        }
	int   at (size_t i=0) { return *(int*)&data[i]; }
};

Re: Get the sample rate of the default playback audio device

Posted: Tue Nov 24, 2020 9:23 pm
by zorg
I see, glad that it worked. :3

Technically, here, alcGetIntegerv just puts an integer containing the device's sampling rate into the memory area pointed to by the last parameter, so you can store it in any var, if you have a pointer to its memory location; me typedef-ing and using ALCInt was just for simplicity's sake. (And it should work with that type as well, i probably just wrote it wrong)

I'll touch up my snippet tomorrow, since i'll probably consider using this as well, in my stuff.

Just out of curiosity, what kind of thing are you making that uses real-time audio synthesis? a game, or a composing app, or...?

Re: Get the sample rate of the default playback audio device

Posted: Tue Nov 24, 2020 10:38 pm
by myQwil
LuaPd https://github.com/myQwil/luapd
It's a spiritual successor to love-pd-audio https://github.com/ghoulsblade/love-pd-audio
That project was updated 7 or 8 years ago so it's been in need of an update.

So far I've only worked on it in a Linux environment. At some point I hope to build it for Windows and Mac as well.

Re: Get the sample rate of the default playback audio device

Posted: Wed Nov 25, 2020 12:41 am
by zorg
Neat, i might include it in my composing sw whenever both gets to that state. :3

Re: Get the sample rate of the default playback audio device

Posted: Sat Dec 05, 2020 7:23 pm
by myQwil
I got it to work without the Buffer class.
ffi.new needs to be written as "ALCint[1]"
Also, the library that Windows users need to load is "OpenAL32"
Here's the full solution.

samplerate.lua:

Code: Select all

local ffi = require('ffi')
local openal = (ffi.os == 'Windows') and ffi.load('OpenAL32') or ffi.C
ffi.cdef[[
typedef struct ALCcontext ALCcontext;
typedef struct ALCdevice  ALCdevice;
typedef int    ALCenum;
typedef int    ALCsizei;
typedef int    ALCint;
typedef void   ALCvoid;

void alcGetIntegerv(
	 ALCdevice *device
	,ALCenum    param
	,ALCsizei   size
	,ALCint    *data
);

ALCcontext *alcGetCurrentContext(ALCvoid);
ALCdevice  *alcGetContextsDevice(ALCcontext *context);
]]

local srate = ffi.new('ALCint[1]')
local ALC_FREQUENCY = 0x1007
local context = openal.alcGetCurrentContext()
local device  = openal.alcGetContextsDevice(context)
openal.alcGetIntegerv(device, ALC_FREQUENCY, 1, srate)

return srate[0]
Implement with:

Code: Select all

samplerate = require('samplerate')