gstreamer/subprojects/gst-plugins-bad/sys/asio/gstasioutils.cpp

282 lines
7.1 KiB
C++

/* GStreamer
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstasioutils.h"
#include <windows.h>
#include <string.h>
#include <atlconv.h>
static gboolean
gst_asio_enum_check_class_root (GstAsioDeviceInfo * info, LPCWSTR clsid)
{
LSTATUS status;
HKEY root_key = nullptr;
HKEY device_key = nullptr;
HKEY proc_server_key = nullptr;
DWORD type = REG_SZ;
CHAR data[256];
DWORD size = sizeof (data);
gboolean ret = FALSE;
status = RegOpenKeyExW (HKEY_CLASSES_ROOT, L"clsid", 0, KEY_READ, &root_key);
if (status != ERROR_SUCCESS)
return FALSE;
/* Read registry HKEY_CLASS_ROOT/CLSID/{device-clsid} */
status = RegOpenKeyExW (root_key, clsid, 0, KEY_READ, &device_key);
if (status != ERROR_SUCCESS)
goto done;
/* ThreadingModel describes COM apartment */
status = RegOpenKeyExW (device_key,
L"InprocServer32", 0, KEY_READ, &proc_server_key);
if (status != ERROR_SUCCESS)
goto done;
status = RegQueryValueExA (proc_server_key,
"ThreadingModel", nullptr, &type, (LPBYTE) data, &size);
if (status != ERROR_SUCCESS)
goto done;
if (g_ascii_strcasecmp (data, "Both") == 0 ||
g_ascii_strcasecmp (data, "Free") == 0) {
info->sta_model = FALSE;
} else {
info->sta_model = TRUE;
}
ret = TRUE;
done:
if (proc_server_key)
RegCloseKey (proc_server_key);
if (device_key)
RegCloseKey (device_key);
if (root_key)
RegCloseKey (root_key);
return ret;
}
static GstAsioDeviceInfo *
gst_asio_enum_new_device_info_from_reg (HKEY reg_key, LPWSTR key_name)
{
LSTATUS status;
HKEY sub_key = nullptr;
WCHAR clsid_data[256];
WCHAR desc_data[256];
DWORD type = REG_SZ;
DWORD size = sizeof (clsid_data);
GstAsioDeviceInfo *ret = nullptr;
CLSID id;
HRESULT hr;
USES_CONVERSION;
status = RegOpenKeyExW (reg_key, key_name, 0, KEY_READ, &sub_key);
if (status != ERROR_SUCCESS)
return nullptr;
/* find CLSID value, used for CoCreateInstance */
status = RegQueryValueExW (sub_key,
L"clsid", 0, &type, (LPBYTE) clsid_data, &size);
if (status != ERROR_SUCCESS)
goto done;
hr = CLSIDFromString (W2COLE (clsid_data), &id);
if (FAILED (hr))
goto done;
ret = g_new0 (GstAsioDeviceInfo, 1);
ret->clsid = id;
ret->driver_name = g_utf16_to_utf8 ((gunichar2 *) key_name, -1,
nullptr, nullptr, nullptr);
/* human readable device description */
status = RegQueryValueExW (sub_key,
L"description", 0, &type, (LPBYTE) desc_data, &size);
if (status != ERROR_SUCCESS) {
GST_WARNING ("no description");
ret->driver_desc = g_strdup (ret->driver_name);
} else {
ret->driver_desc = g_utf16_to_utf8 ((gunichar2 *) desc_data, -1,
nullptr, nullptr, nullptr);
}
/* Check COM threading model */
if (!gst_asio_enum_check_class_root (ret, clsid_data)) {
gst_asio_device_info_free (ret);
ret = nullptr;
}
done:
if (sub_key)
RegCloseKey (sub_key);
return ret;
}
guint
gst_asio_enum (GList ** infos)
{
GList *info_list = nullptr;
DWORD index = 0;
guint num_device = 0;
LSTATUS status;
HKEY reg_key = nullptr;
WCHAR key_name[512];
g_return_val_if_fail (infos != nullptr, 0);
status = RegOpenKeyExW (HKEY_LOCAL_MACHINE, L"software\\asio", 0,
KEY_READ, &reg_key);
while (status == ERROR_SUCCESS) {
GstAsioDeviceInfo *info;
status = RegEnumKeyW (reg_key, index, key_name, 512);
if (status != ERROR_SUCCESS)
break;
index++;
info = gst_asio_enum_new_device_info_from_reg (reg_key, key_name);
if (!info)
continue;
info_list = g_list_append (info_list, info);
num_device++;
}
if (reg_key)
RegCloseKey (reg_key);
*infos = info_list;
return num_device;
}
GstAsioDeviceInfo *
gst_asio_device_info_copy (const GstAsioDeviceInfo * info)
{
GstAsioDeviceInfo *new_info;
if (!info)
return nullptr;
new_info = g_new0 (GstAsioDeviceInfo, 1);
new_info->clsid = info->clsid;
new_info->sta_model = info->sta_model;
new_info->driver_name = g_strdup (info->driver_name);
new_info->driver_desc = g_strdup (info->driver_desc);
return new_info;
}
void
gst_asio_device_info_free (GstAsioDeviceInfo * info)
{
if (!info)
return;
g_free (info->driver_name);
g_free (info->driver_desc);
g_free (info);
}
GstAudioFormat
gst_asio_sample_type_to_gst (ASIOSampleType type)
{
GstAudioFormat fmt;
switch (type) {
/*~~ MSB means big endian ~~ */
case ASIOSTInt16MSB:
fmt = GST_AUDIO_FORMAT_S16BE;
break;
/* FIXME: also used for 20 bits packed in 24 bits, how do we detect that? */
case ASIOSTInt24MSB:
fmt = GST_AUDIO_FORMAT_S24BE;
break;
case ASIOSTInt32MSB:
fmt = GST_AUDIO_FORMAT_S32BE;
break;
case ASIOSTFloat32MSB:
fmt = GST_AUDIO_FORMAT_F32BE;
break;
case ASIOSTFloat64MSB:
fmt = GST_AUDIO_FORMAT_F64BE;
break;
/* All these are aligned to a different boundary than the packing, not sure
* how to handle it, let's try the normal S32BE format */
case ASIOSTInt32MSB16:
case ASIOSTInt32MSB18:
case ASIOSTInt32MSB20:
case ASIOSTInt32MSB24:
fmt = GST_AUDIO_FORMAT_S32BE;
break;
/*~~ LSB means little endian ~~ */
case ASIOSTInt16LSB:
fmt = GST_AUDIO_FORMAT_S16LE;
break;
/* FIXME: also used for 20 bits packed in 24 bits, how do we detect that? */
case ASIOSTInt24LSB:
fmt = GST_AUDIO_FORMAT_S24LE;
break;
case ASIOSTInt32LSB:
fmt = GST_AUDIO_FORMAT_S32LE;
break;
case ASIOSTFloat32LSB:
fmt = GST_AUDIO_FORMAT_F32LE;
break;
case ASIOSTFloat64LSB:
fmt = GST_AUDIO_FORMAT_F64LE;
break;
/* All these are aligned to a different boundary than the packing, not sure
* how to handle it, let's try the normal S32LE format */
case ASIOSTInt32LSB16:
case ASIOSTInt32LSB18:
case ASIOSTInt32LSB20:
case ASIOSTInt32LSB24:
GST_WARNING ("weird alignment %ld, trying S32LE", type);
fmt = GST_AUDIO_FORMAT_S32LE;
break;
/*~~ ASIO DSD formats are don't have gstreamer mappings ~~ */
case ASIOSTDSDInt8LSB1:
case ASIOSTDSDInt8MSB1:
case ASIOSTDSDInt8NER8:
GST_ERROR ("ASIO DSD formats are not supported");
fmt = GST_AUDIO_FORMAT_UNKNOWN;
break;
default:
GST_ERROR ("Unknown asio sample type %ld", type);
fmt = GST_AUDIO_FORMAT_UNKNOWN;
break;
}
return fmt;
}