| 
 | 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