ohmu
·
2026-10-15
1#include "sound.h"
2#include "common/debug.h"
3#include "common/strutil.h"
4
5#include <fcntl.h>
6#include <sndio.h>
7
8static void close_hdl(struct sioctl_hdl** phdl) {
9 assert(phdl);
10 if (*phdl) {
11 sioctl_close(*phdl);
12 }
13}
14
15enum { MAX_CHANNEL_NUM = 8 };
16
17typedef struct FFSoundDeviceBundle {
18 char name[SIOCTL_DISPLAYMAX];
19 double level[MAX_CHANNEL_NUM];
20 uint8_t iLevel;
21 bool mute[MAX_CHANNEL_NUM];
22 uint8_t iMute;
23} FFSoundDeviceBundle;
24
25static void enumerate_props(FFSoundDeviceBundle* bundle, struct sioctl_desc* desc, int val) {
26 if (!desc) {
27 return;
28 }
29
30 if (desc->type == SIOCTL_SEL) {
31 if (desc->display[0] != '\0' && ffStrEquals(desc->node0.name, "server")) {
32 ffStrCopy(bundle->name, desc->display, SIOCTL_DISPLAYMAX);
33 }
34 return;
35 }
36
37 if (desc->type != SIOCTL_NUM && desc->type != SIOCTL_SW) {
38 return;
39 }
40
41 if (!ffStrEquals(desc->node0.name, "output")) {
42 return;
43 }
44
45 if (ffStrEquals(desc->func, "level")) {
46 if (__builtin_expect(bundle->iLevel == MAX_CHANNEL_NUM, false)) {
47 return;
48 }
49 bundle->level[bundle->iLevel] = (double) val / (double) desc->maxval;
50 ++bundle->iLevel;
51 } else if (ffStrEquals(desc->func, "mute")) {
52 if (__builtin_expect(bundle->iMute == MAX_CHANNEL_NUM, false)) {
53 return;
54 }
55 bundle->mute[bundle->iMute] = !!val;
56 ++bundle->iMute;
57 }
58}
59
60const char* ffDetectSound([[maybe_unused]] FFSoundOptions* options, FFlist* devices) {
61 [[gnu::cleanup(close_hdl)]] struct sioctl_hdl* hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
62 if (!hdl) {
63 FF_DEBUG("sio_open() failed");
64 return "sio_open() failed";
65 }
66
67 FFSoundDeviceBundle bundle = {};
68 if (sioctl_ondesc(hdl, (void*) enumerate_props, &bundle) == 0) {
69 FF_DEBUG("sioctl_ondesc() failed");
70 return "sioctl_ondesc() failed";
71 }
72
73 if (bundle.iLevel != bundle.iMute || bundle.iLevel == 0) {
74 FF_DEBUG("Unexpected sioctl_ondesc() result");
75 return "Unexpected sioctl_ondesc() result";
76 }
77
78 FFSoundDevice* device = FF_LIST_ADD(FFSoundDevice, *devices);
79 ffStrbufInitS(&device->name, bundle.name);
80 ffStrbufInitS(&device->identifier, SIO_DEVANY);
81 ffStrbufInitStatic(&device->platformApi, "sndio");
82 device->volume = 0;
83 device->type = FF_SOUND_TYPE_ACTIVE | FF_SOUND_TYPE_MAIN;
84
85 double totalLevel = 0;
86 for (uint8_t i = 0; i < bundle.iLevel; ++i) {
87 if (!bundle.mute[i]) {
88 totalLevel += bundle.level[i];
89 }
90 }
91 device->volume = (uint8_t) ((totalLevel * 100 + bundle.iLevel / 2) / bundle.iLevel);
92
93 return nullptr;
94}