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