I'm trying to retrieve (and probably later set) the master sound volume in Linux. I'm using PulseAudio, but ideally it should work for ALSA too.
I found this very helpful post on how to set the volume, and from that I was able to deduce the existence of snd_mixer_selem_get_playback_volume() to retrieve the current setting. However on my system, this seems to be giving me wrong readings - where mixer programs show 100%, this tops out at about 66%.
If I open pavucontrol I can see the volume for this output device matches the readings I get from here, so I assume it's giving me the hardware volume setting, not the global master volume that I wanted.
Compile the below code with gcc audio_volume.c -o audio_volume -lasound or altenatively with gcc audio_volume.c -o audio_volume_oss -DOSSCONTROL. OSS version is written very crudely and does not give precise results. Setting volume to 100 sets 97% volume in my system. ALSA version works for me and PulseAudio in openSUSE. Needs some cleanup and love but I hope it is what you need, I grant the permission to use it on the WTFPL license.
#include <unistd.h>
#include <fcntl.h>
#ifdef OSSCONTROL
#define MIXER_DEV "/dev/dsp"
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdio.h>
#else
#include <alsa/asoundlib.h>
#endif
typedef enum {
AUDIO_VOLUME_SET,
AUDIO_VOLUME_GET,
} audio_volume_action;
/*
Drawbacks. Sets volume on both channels but gets volume on one. Can be easily adapted.
*/
int audio_volume(audio_volume_action action, long* outvol)
{
#ifdef OSSCONTROL
int ret = 0;
int fd, devs;
if ((fd = open(MIXER_DEV, O_WRONLY)) > 0)
{
if(action == AUDIO_VOLUME_SET) {
if(*outvol < 0 || *outvol > 100)
return -2;
*outvol = (*outvol << 8) | *outvol;
ioctl(fd, SOUND_MIXER_WRITE_VOLUME, outvol);
}
else if(action == AUDIO_VOLUME_GET) {
ioctl(fd, SOUND_MIXER_READ_VOLUME, outvol);
*outvol = *outvol & 0xff;
}
close(fd);
return 0;
}
return -1;;
#else
snd_mixer_t* handle;
snd_mixer_elem_t* elem;
snd_mixer_selem_id_t* sid;
static const char* mix_name = "Master";
static const char* card = "default";
static int mix_index = 0;
long pmin, pmax;
long get_vol, set_vol;
float f_multi;
snd_mixer_selem_id_alloca(&sid);
//sets simple-mixer index and name
snd_mixer_selem_id_set_index(sid, mix_index);
snd_mixer_selem_id_set_name(sid, mix_name);
if ((snd_mixer_open(&handle, 0)) < 0)
return -1;
if ((snd_mixer_attach(handle, card)) < 0) {
snd_mixer_close(handle);
return -2;
}
if ((snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
snd_mixer_close(handle);
return -3;
}
ret = snd_mixer_load(handle);
if (ret < 0) {
snd_mixer_close(handle);
return -4;
}
elem = snd_mixer_find_selem(handle, sid);
if (!elem) {
snd_mixer_close(handle);
return -5;
}
long minv, maxv;
snd_mixer_selem_get_playback_volume_range (elem, &minv, &maxv);
fprintf(stderr, "Volume range <%i,%i>\n", minv, maxv);
if(action == AUDIO_VOLUME_GET) {
if(snd_mixer_selem_get_playback_volume(elem, 0, outvol) < 0) {
snd_mixer_close(handle);
return -6;
}
fprintf(stderr, "Get volume %i with status %i\n", *outvol, ret);
/* make the value bound to 100 */
*outvol -= minv;
maxv -= minv;
minv = 0;
*outvol = 100 * (*outvol) / maxv; // make the value bound from 0 to 100
}
else if(action == AUDIO_VOLUME_SET) {
if(*outvol < 0 || *outvol > VOLUME_BOUND) // out of bounds
return -7;
*outvol = (*outvol * (maxv - minv) / (100-1)) + minv;
if(snd_mixer_selem_set_playback_volume(elem, 0, *outvol) < 0) {
snd_mixer_close(handle);
return -8;
}
if(snd_mixer_selem_set_playback_volume(elem, 1, *outvol) < 0) {
snd_mixer_close(handle);
return -9;
}
fprintf(stderr, "Set volume %i with status %i\n", *outvol, ret);
}
snd_mixer_close(handle);
return 0;
#endif
}
int main(void)
{
long vol = -1;
printf("Ret %i\n", audio_volume(AUDIO_VOLUME_GET, &vol));
printf("Master volume is %i\n", vol);
vol = 100;
printf("Ret %i\n", audio_volume(AUDIO_VOLUME_SET, &vol));
return 0;
}
Related
I'm writing a Linux KVM hypervisor for x86 16-bit guests running in real mode. When doing interrupt calls (int ... instruction), I've encountered the KVM_INTERNAL_ERROR_SIMUL_EX error on Linux kernel 3.13.0. The same code is running fine on Linux kernel 3.16.0. Am I missing something? Is there a workaround I can add to my code to make it work with Linux kernel 3.13.0 (and possibly earlier)?
The test guest calls int 0x18 ... int 0x4f, all of which is handled in the hypervisor (C code after KVM_RUN has returned). When it's working correctly, all of the interrupt calls work. On Linux kernel 3.13.0, int 0x21 starts failing (and then int 0x22, int 0x23 and int 0x24 would also fail).
I was trying to write the shortest example C code to demonstrate the problem, here it is:
/* Based on: https://gist.github.com/zserge/d68683f17c68709818f8baab0ded2d15
* Based on: https://gist.githubusercontent.com/zserge/d68683f17c68709818f8baab0ded2d15/raw/b79033254b092ec9121bb891938b27dd128030d7/kvm-host-simple.c
*
* Compile: gcc -ansi -pedantic -s -O2 -W -Wall -o kvm16 kvm16.c && ./kvm16
*
* Expected correct output (e.g. on Linux 3.16.0 compiled for i386 (i686)):
*
* ...
* info: int 0x4f iret to: ...
* info: success, exiting
*
* Failure output (e.g. on Linux 3.13.0 compiled for amd64 (x86_64)):
*
* info: int 0x20 iret to: cs=0x0070 ip=0x0013
* fatal: KVM internal error suberror=2
*
* // Encounter unexpected simultaneous exceptions.
* #define KVM_INTERNAL_ERROR_SIMUL_EX 2
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/kvm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#define INT_NUM 0x21 /* Also works for INT_NUM == 0x20. */
int main(int argc, char *argv[]) {
int kvm_fd, vm_fd, vcpu_fd;
void *mem;
struct kvm_userspace_memory_region region;
struct kvm_run *run;
struct kvm_regs regs;
struct kvm_sregs sregs;
(void)argc; (void)argv;
if ((kvm_fd = open("/dev/kvm", O_RDWR)) < 0) {
perror("failed to open /dev/kvm");
return 1;
}
if ((vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0)) < 0) {
perror("failed to create vm");
return 1;
}
if ((mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0)) == NULL) {
perror("mmap");
return 1;
}
memset(®ion, 0, sizeof(region));
region.slot = 0;
region.guest_phys_addr = 0;
region.memory_size = 0x1000;
region.userspace_addr = (uintptr_t)mem;
if (ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, ®ion) < 0) {
perror("KVM_SET_USER_MEMORY_REGION");
return 1;
}
{ /* 8086 real mode machine code. */
char *p = (char*)mem + 0x700;
unsigned int_num;
for (int_num = 0; int_num < 0x100; ++int_num) {
*(unsigned short*)((char*)mem + int_num * 4) = int_num; /* Interrupt vector INT_NUM offset := INT_NUM. */
*(unsigned short*)((char*)mem + int_num * 4 + 2) = 0x54; /* Interrupt vector INT_NUM segment := 0x54. */
}
*p++ = (char)0xf4; /* hlt. */
for (int_num = 0x18; int_num < 0x50; ++int_num) {
*p++ = (char)0xcd; /* int int_num. */
*p++ = (char)int_num;
}
*p++ = (char)0xf4;
}
memset((char*)mem + 0x540, '\xf4', 0x100); /* 256 times hlt. Interrupt vectors point here. */
if ((vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0)) < 0) {
perror("KVM_CREATE_VCPU");
return 1;
}
{
int kvm_run_mmap_size = ioctl(kvm_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
if (kvm_run_mmap_size < 0) {
perror("KVM_GET_VCPU_MMAP_SIZE");
return 1;
}
run = (struct kvm_run *)mmap(
NULL, kvm_run_mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpu_fd, 0);
if (run == NULL) {
perror("mmap kvm_run");
return 1;
}
}
memset(®s, '\0', sizeof(regs));
if (ioctl(vcpu_fd, KVM_GET_SREGS, &sregs) < 0) {
perror("KVM_GET_SREGS");
return 1;
}
{
int fd = open("kvm16.sregs", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}
if (write(fd, &sregs, sizeof(sregs)) != sizeof(sregs)) {
perror("write");
return 1;
}
if (close(fd) != 0) {
perror("close");
return 1;
}
}
sregs.cs.base = (sregs.cs.selector = 0x70) << 4;
sregs.ds.base = (sregs.ds.selector = sregs.cs.selector) << 4;
sregs.es.base = (sregs.es.selector = sregs.cs.selector) << 4;
sregs.ss.base = (sregs.ss.selector = sregs.cs.selector) << 4;
if (ioctl(vcpu_fd, KVM_GET_REGS, ®s) < 0) {
perror("KVM_GET_REGS");
return 1;
}
regs.rflags = 1 << 1; /* Reserved bit in EFLAGS. Even needed after KVM_GET_REGS. */
regs.rip = 0;
regs.rsp = 0x1000 - 0x700;
if (ioctl(vcpu_fd, KVM_SET_SREGS, &sregs) < 0) {
perror("KVM_SET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_SET_REGS, ®s) < 0) {
perror("KVM_SET_REGS");
return 1;
}
for (;;) {
int ret = ioctl(vcpu_fd, KVM_RUN, 0);
unsigned short cs, ip;
if (ret < 0) {
perror("KVM_RUN");
return 1;
}
if (ioctl(vcpu_fd, KVM_GET_SREGS, &sregs) < 0) {
perror("KVM_GET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_GET_REGS, ®s) < 0) {
perror("KVM_GET_REGS");
return 1;
}
cs = sregs.cs.selector;
ip = regs.rip;
if (run->exit_reason == KVM_EXIT_HLT) {
fprintf(stderr, "info: hlt: cs=0x%04x ip=0x%04x\n", cs, ip - 1);
if (cs == 0x70) {
if (ip != 0 + 1) {
fprintf(stderr, "info: success, exiting\n");
return 0; /* EXIT_SUCCESS after the second `hlt' in the code. */
}
} else if (cs == 0x54) { /* Simulate iret. */
const char *csip_ptr = (const char*)mem + ((unsigned short)sregs.ss.selector << 4) + (unsigned short)regs.rsp;
const unsigned short int_ip = ((const unsigned short*)csip_ptr)[0];
const unsigned short int_cs = ((const unsigned short*)csip_ptr)[1];
const unsigned short int_flags = ((const unsigned short*)csip_ptr)[2];
fprintf(stderr, "info: int 0x%02x iret to: cs=0x%04x ip=0x%04x\n", ip - 1, int_cs, int_ip);
sregs.cs.base = (sregs.cs.selector = int_cs) << 4;
regs.rip = int_ip;
if (int_flags & (1 << 9)) regs.rflags |= (1 << 9); /* Set IF back to 1 if it was 1. */
regs.rsp += 6; /* pop ip, pop cs, popfw . */
if (ioctl(vcpu_fd, KVM_SET_SREGS, &sregs) < 0) {
perror("KVM_SET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_SET_REGS, ®s) < 0) {
perror("KVM_SET_REGS");
return 1;
}
} else {
fprintf(stderr, "fatal: unexpected hlt: cs=0x%04x ip=0x%04x\n", cs, ip - 1);
return 5;
}
} else if (run->exit_reason == KVM_EXIT_INTERNAL_ERROR) {
fprintf(stderr, "fatal: KVM internal error suberror=%d\n", (unsigned)run->internal.suberror);
return 4;
} else {
fprintf(stderr, "fatal: unexpected KVM exit: exit_reason=%d cs=0x%04x ip=0x%04x\n", run->exit_reason, cs, ip);
return 2;
}
}
}
I'm currently working on a (plain) C project, on Windows 7, and I need help.
The program is supposed to start playing a sound of given frequency when I press a key, and then stop it when I press another key.
Once it starts playing, it shouldn't stop until the user presses the second key, so I can't specify a duration.
Is there a way to do it with WinApi? I'm searching for a function like:
sound(frequency);
that would start playing a sound at frequency frequency.
If frequency is 0, then all sounds should stop.
I searched all over Stackoverflow, but I couldn't find any solution except Beep(); that needs a duration, and PlaySound(); that needs a wav file. I'd like to keep it simple. I don't want a big program that generates wav files, only a built-in function.
Ah, I did found such a function on Stack a few days ago, but I can't find it again.
(P.S: this question has no answer with WAV file synthesis from scratch. It's another question. So please do not tell me the close question has not been answered, because it's FALSE.)
Here is a program I wrote that uses waveOutOpen and waveOutWrite to generate 48 kHz stereo audio in real time. It is possible to generate the audio using the main thread, but this program launches a separate thread so that it can provide the nice kind of API you wanted.
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#define BUFFER_COUNT 4
#define BUFFER_SAMPLE_COUNT 1000
#define SAMPLES_PER_SECOND 48000
HWAVEOUT waveOut;
HANDLE waveEvent;
WAVEHDR headers[BUFFER_COUNT];
int16_t buffers[BUFFER_COUNT][BUFFER_SAMPLE_COUNT * 2];
WAVEHDR * currentHeader;
double volume = 6000;
double phase;
double phase_increment;
double audio_value;
void sound(double frequency) {
if (frequency == 0) {
phase_increment = 0;
return;
}
phase_increment = 2 * M_PI / SAMPLES_PER_SECOND * frequency;
}
void fill_buffer(int16_t * buffer) {
for (size_t i = 0; i < BUFFER_SAMPLE_COUNT * 2; i += 2) {
if (phase_increment == 0) {
phase = 0;
audio_value *= 0.9;
}
else {
phase += phase_increment;
if (phase > 0) { phase -= 2 * M_PI; }
audio_value = sin(phase) * volume;
}
buffer[i + 0] = audio_value; // Left channel
buffer[i + 1] = audio_value; // Right channel
}
}
__stdcall DWORD audio_thread(LPVOID param) {
while (1) {
DWORD waitResult = WaitForSingleObject(waveEvent, INFINITE);
if (waitResult) {
fprintf(stderr, "Failed to wait for event.\n");
return 1;
}
BOOL success = ResetEvent(waveEvent);
if (!success) {
fprintf(stderr, "Failed to reset event.\n");
return 1;
}
while (currentHeader->dwFlags & WHDR_DONE) {
fill_buffer((int16_t *)currentHeader->lpData);
MMRESULT result = waveOutWrite(waveOut, currentHeader, sizeof(WAVEHDR));
if (result) {
fprintf(stderr, "Failed to write wave data. Error code %u.\n", result);
return 1;
}
currentHeader++;
if (currentHeader == headers + BUFFER_COUNT) { currentHeader = headers; }
}
}
}
int audio_init() {
WAVEFORMATEX format = { 0 };
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = 2;
format.nSamplesPerSec = SAMPLES_PER_SECOND;
format.wBitsPerSample = 16;
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec;
waveEvent = CreateEvent(NULL, true, false, NULL);
if (waveEvent == NULL) {
fprintf(stderr, "Failed to create event.");
return 1;
}
MMRESULT result = waveOutOpen(&waveOut, WAVE_MAPPER, &format,
(DWORD_PTR)waveEvent, 0, CALLBACK_EVENT);
if (result) {
fprintf(stderr, "Failed to start audio output. Error code %u.\n", result);
return 1;
}
for (size_t i = 0; i < BUFFER_COUNT; i++) {
headers[i] = (WAVEHDR) {
.lpData = (char *)buffers[i],
.dwBufferLength = BUFFER_SAMPLE_COUNT * 4,
};
result = waveOutPrepareHeader(waveOut, &headers[i], sizeof(WAVEHDR));
if (result) {
fprintf(stderr, "Failed to prepare header. Error code %u.\n", result);
return 1;
}
headers[i].dwFlags |= WHDR_DONE;
}
currentHeader = headers;
HANDLE thread = CreateThread(NULL, 0, audio_thread, NULL, 0, NULL);
if (thread == NULL) {
fprintf(stderr, "Failed to start thread");
return 1;
}
return 0;
}
int main() {
if (audio_init()) { return 1; }
sound(400); Sleep(500);
sound(300); Sleep(500);
sound(600); Sleep(500);
for (int i = 0; i < 3; i++) {
sound(200); Sleep(50);
sound(0); Sleep(200);
sound(300); Sleep(50);
sound(0); Sleep(200);
sound(400); Sleep(50);
sound(0); Sleep(200);
sound(500); Sleep(50);
sound(0); Sleep(200);
sound(600); Sleep(50);
sound(0); Sleep(200);
}
}
I compiled in the MinGW 64-bit environment of MSYS2 using this command:
gcc -g -O2 -Wall -Wextra -Wno-unused-parameter wave.c -lwinmm -owave
Note the extra library I linked in: -lwinmm.
This code uses 4 buffers with 1000 samples each, so at any given time there will be no more than 83 ms worth of sound queued up to be played. This works fine on my system, but perhaps on other systems you might need to increase these parameters to ensure smooth playback. The buffer count is probably the first one I would try increasing if there are problems.
There might be some multithreading things I am getting wrong in the code. The phase_increment variable is written in the main thread and read in the audio thread. Perhaps I should explicitly make that be an atomic variable, and insert a memory barrier after I write to it.
I have a program which reads a file of domain names (one domain per line) and performs asynchronous DNS resolution and then downloads the landing page for each domain. The network communication is performed with an epoll based event loop. Testing has shown that increasing the number of concurrent connections does not increase performance in terms of Mbits/s throughput and in terms of number of pages downloaded. The results of my test are as follows. The first test is for 1024 concurrent connections, the second test for 2048, the third for 4096 and the fourth for 8192:
:~$ ./crawler com.lowercase
iterations=156965 total domains=5575 elapsed=65.51s domains/s=85.10 KB=28163 Mbit/s=2.86
:~$ ./crawler com.lowercase
iterations=88339 total domains=10525 elapsed=64.98s domains/s=161.98 KB=52936 Mbit/s=5.41
:~$ ./crawler com.lowercase
iterations=143989 total domains=9409 elapsed=64.80s domains/s=145.20 KB=48166 Mbit/s=4.94
:~$ ./crawler com.lowercase
iterations=109597 total domains=10532 elapsed=65.13s domains/s=161.71 KB=51874 Mbit/s=5.29
As you can see there is really no trend of increase in terms of domains downloaded per second or Mbits/s throughput. These results run contrary to expectations. Can anyone explain these results? Suggest a fix to improve performance with increasing number of connections?
For those interested a full code listing is provided below:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <ares.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#define MAXWAITING 1000 /* Max. number of parallel DNS queries */
#define MAXTRIES 3 /* Max. number of tries per domain */
#define DNSTIMEOUT 3000 /* Max. number of ms for first try */
#define DNS_MAX_EVENTS 10000
#define DNS_MAX_SERVERS 2
#define SERVERS "1.0.0.1,8.8.8.8" /* DNS server to use (Cloudflare & Google) */
#define MAXDOMAINS 8192
#define PORT 80
#define MAXBUF 1024
#define MAX_EPOLL_EVENTS 8192
#define MAX_CONNECTIONS 8192
#define TIMEOUT 10000
ares_socket_t dns_client_fds[ARES_GETSOCK_MAXNUM] = {0};
struct epoll_event ev, dns_events[DNS_MAX_EVENTS];
int i,bitmask,nfds, epollfd, timeout, fd_count, ret;
int epfd;
int sockfd[MAX_CONNECTIONS];
struct epoll_event event[MAX_CONNECTIONS];
struct sockaddr_in dest[MAX_CONNECTIONS];
char resolved[MAXDOMAINS][254];
char ips[MAXDOMAINS][128];
int current = 0, active = 0, next = 0;
char servers[MAX_CONNECTIONS][128];
char domains[MAX_CONNECTIONS][254];
char get_buffer[MAX_CONNECTIONS][1024];
char buffer[MAX_CONNECTIONS][MAXBUF];
int buffer_used[MAX_CONNECTIONS];
struct timespec startTime, stopTime;
int i, num_ready, connections = 0, done = 0, total_bytes = 0, total_domains = 0, iterations = 0, count = 0;
FILE * fp;
struct epoll_event events[MAX_EPOLL_EVENTS];
static int nwaiting;
static void state_cb(void *data, int s, int read, int write)
{
//printf("Change state fd %d read:%d write:%d\n", s, read, write);
}
static void callback(void *arg, int status, int timeouts, struct hostent *host)
{
nwaiting--;
if(!host || status != ARES_SUCCESS){
//fprintf(stderr, "Failed to lookup %s\n", ares_strerror(status));
return;
}
char ip[INET6_ADDRSTRLEN];
if (host->h_addr_list[0] != NULL){
inet_ntop(host->h_addrtype, host->h_addr_list[0], ip, sizeof(ip));
strcpy(resolved[current], host->h_name);
strcpy(ips[current], ip);
if (current < MAXDOMAINS - 1) current++; else current = 0;
active++;
printf("active %d\r", active);
}
}
static void wait_ares(ares_channel channel)
{
nfds=0;
bitmask=0;
for (i =0; i < DNS_MAX_SERVERS ; i++) {
if (dns_client_fds[i] > 0) {
if (epoll_ctl(epollfd, EPOLL_CTL_DEL, dns_client_fds[i], NULL) < 0) {
continue;
}
}
}
memset(dns_client_fds, 0, sizeof(dns_client_fds));
bitmask = ares_getsock(channel, dns_client_fds, DNS_MAX_SERVERS);
for (i =0; i < DNS_MAX_SERVERS ; i++) {
if (dns_client_fds[i] > 0) {
ev.events = 0;
if (ARES_GETSOCK_READABLE(bitmask, i)) {
ev.events |= EPOLLIN;
}
if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
ev.events |= EPOLLOUT;
}
ev.data.fd = dns_client_fds[i];
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, dns_client_fds[i], &ev) < 0) {
if(errno == EEXIST) {
nfds++;
continue;
}
continue;
}
nfds++;
}
}
if(nfds==0)
{
return;
}
timeout = 1000;//millisecs
fd_count = epoll_wait(epollfd, dns_events, DNS_MAX_EVENTS, timeout);
if (fd_count < 0) {
return;
}
if (fd_count > 0) {
for (i = 0; i < fd_count; ++i) {
ares_process_fd(channel, ((dns_events[i].events) & (EPOLLIN) ? dns_events[i].data.fd:ARES_SOCKET_BAD), ((dns_events[i].events) & (EPOLLOUT)? dns_events[i].data.fd:ARES_SOCKET_BAD));
}
} else {
ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
}
}
void make_socket_and_connect (int sock)
{
if ( (sockfd[sock] = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0 ) {
perror("Socket");
exit(errno);
}
count++;
event[sock].events = EPOLLIN|EPOLLOUT;
event[sock].data.fd = sockfd[sock];
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd[sock], &event[sock]);
bzero(&dest[sock], sizeof(dest[sock]));
dest[sock].sin_family = AF_INET;
dest[sock].sin_port = htons(PORT);
if ( inet_pton(AF_INET, servers[sock], &dest[sock].sin_addr.s_addr) == 0 ) {
printf("\n");
perror(servers[sock]);
exit(errno);
}
if ( connect(sockfd[sock], (struct sockaddr*)&dest[sock], sizeof(dest[sock])) != 0 ) {
if(errno != EINPROGRESS) {
printf("%s\n", servers[sock]);
perror("Connect again ");
//exit(errno);
}
buffer_used[sock] = 0;
}
}
int is_valid_ip(char *domain)
{
if (!strcmp(domain, "255.255.255.255"))
return 0;
if (!strcmp(domain, "192.168.1.0"))
return 0;
if (!strcmp(domain, "127.0.0.0"))
return 0;
return 1;
}
void close_socket (int socket)
{
close(sockfd[socket]);
count--;
epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd[socket], &event[socket]);
}
void get_domain_and_ip(int id)
{
//close_socket(id);
active--;
get_domain_name:
strcpy(servers[id], ips[next]);
strcpy(domains[id], resolved[next]);
if (next < (MAXDOMAINS - 1)) next++; else next = 0;
if (is_valid_ip(servers[id]))
{
make_socket_and_connect(id);
total_domains++;
}
else
goto get_domain_name;
}
void get_domain_and_ip_without_connect(int id)
{
get_domain_name2:
strcpy(servers[id], ips[next]);
strcpy(domains[id], resolved[next]);
if (next < (MAXDOMAINS - 1)) next++; else next = 0;
if (!is_valid_ip(servers[id]))
goto get_domain_name2;
}
void get_time()
{
clock_gettime(CLOCK_MONOTONIC, &stopTime);
uint64_t msElapsed = (stopTime.tv_nsec - startTime.tv_nsec) / 1000000 + (stopTime.tv_sec - startTime.tv_sec) * 1000;
double seconds = (double)msElapsed / 1000.0;
iterations++;
fprintf(stderr, "iterations=%d total domains=%d elapsed=%2.2fs domains/s=%2.2f KB=%d Mbit/s=%2.2f num_ready=%d count=%d active=%d next=%d current=%d.....\r"
, iterations, total_domains, seconds, total_domains/seconds, total_bytes/1024, 8*total_bytes/seconds/1024/1204, num_ready, count, active, next, current);
}
ssize_t send_data(int id)
{
ssize_t nByte = send(sockfd[id], get_buffer[id] + buffer_used[id], strlen(get_buffer[id]) - buffer_used[id], 0);
return nByte;
}
ssize_t recv_data(int id)
{
ssize_t nByte = recv(sockfd[id], buffer[id], sizeof(buffer[id]), 0);
return nByte;
}
int wait()
{
int ret = epoll_wait(epfd, events, MAX_EPOLL_EVENTS, TIMEOUT/*timeout*/);
return ret;
}
int main(int argc, char *argv[]) {
sigaction(SIGPIPE, &(struct sigaction){SIG_IGN}, NULL);
FILE * fp;
char domain[254];
size_t len = 0;
ssize_t read;
ares_channel channel;
int status, dns_done = 0;
int optmask;
status = ares_library_init(ARES_LIB_INIT_ALL);
if (status != ARES_SUCCESS) {
printf("ares_library_init: %s\n", ares_strerror(status));
return 1;
}
struct ares_options options = {
.timeout = DNSTIMEOUT, /* set first query timeout */
.tries = MAXTRIES /* set max. number of tries */
};
optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
status = ares_init_options(&channel, &options, optmask);
if (status != ARES_SUCCESS) {
printf("ares_init_options: %s\n", ares_strerror(status));
return 1;
}
status = ares_set_servers_csv(channel, SERVERS);
if (status != ARES_SUCCESS) {
printf("ares_set_servers_csv: %s\n", ares_strerror(status));
return 1;
}
memset(dns_client_fds, 0, sizeof(dns_client_fds));
memset((char *)&ev, 0, sizeof(struct epoll_event));
memset((char *)&dns_events[0], 0, sizeof(dns_events));
epollfd = epoll_create(DNS_MAX_SERVERS);
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
do{
if (nwaiting >= MAXWAITING || dns_done) {
do {
wait_ares(channel);
} while (nwaiting > MAXWAITING);
}
if (!dns_done) {
if (fscanf(fp, "%253s", domain) == 1) {
ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
nwaiting++;
} else {
//fprintf(stderr, "done sending\n");
dns_done = 1;
}
}
} while (active < MAX_CONNECTIONS);
/*---Open sockets for streaming---*/
for (i = 0; i < MAX_CONNECTIONS; i++)
{
if ( (sockfd[i] = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0 ) {
perror("Socket");
exit(errno);
}
count++;
}
/*---Add sockets to epoll---*/
epfd = epoll_create1(0);
for (i = 0; i < MAX_CONNECTIONS; i++)
{
event[i].events = EPOLLIN|EPOLLOUT;
event[i].data.fd = sockfd[i];
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd[i], &event[i]);
}
/*---Initialize server address/port structs---*/
for (i = 0; i < MAX_CONNECTIONS; i++)
{
get_domain_and_ip_without_connect(i);
//printf("%s %s\n", servers[i], domains[i]);
bzero(&dest[i], sizeof(dest[i]));
dest[i].sin_family = AF_INET;
dest[i].sin_port = htons(PORT);
if ( inet_pton(AF_INET, servers[i], &dest[i].sin_addr.s_addr) == 0 ) {
perror(servers[i]);
exit(errno);
}
}
/*---Connect to servers---*/
for (i = 0; i < MAX_CONNECTIONS; i++)
{
if ( connect(sockfd[i], (struct sockaddr*)&dest[i], sizeof(dest[i])) != 0 ) {
if(errno != EINPROGRESS) {
perror("Connect ");
//exit(errno);
}
buffer_used[i] = 0;
}
}
clock_gettime(CLOCK_MONOTONIC, &startTime);
while (1)
{
/*---Do async DNS---*/
while (active < MAXDOMAINS && nwaiting > 0) {
//printf("active = %d MAXDOMAINS = %d nwaiting = %d MAXWAITING = %d\n", active, MAXDOMAINS, nwaiting, MAXWAITING);
if (nwaiting >= MAXWAITING || dns_done) {
do {
wait_ares(channel);
} while (nwaiting > MAXWAITING);
}
if (!dns_done) {
if (fscanf(fp, "%253s", domain) == 1) {
ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
nwaiting++;
} else {
//fprintf(stderr, "done sending\n");
dns_done = 1;
}
}
} //while (active < MAXDOMAINS);
/*---Wait to be able to send---*/
num_ready = wait();
get_time();
if (!num_ready) break;
for(i = 0; i < num_ready; i++) {
int index;
if(events[i].events & EPOLLOUT) {
for (int j = 0; j < MAX_CONNECTIONS; j++)
{
if (events[i].data.fd == sockfd[j])
{
index = j;
break;
}
}
snprintf(get_buffer[index], sizeof(get_buffer[index]),
"GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\r\n\r\n", "/", domains[i]);
ssize_t nByte = 0;
if (buffer_used[index] < strlen(get_buffer[index]))
nByte = send_data(index);
if (nByte > 0)
{
buffer_used[index] += nByte;
total_bytes += nByte;
}
if (nByte == -1 && errno == EPIPE)
{
get_domain_and_ip(index);
}
}
if(events[i].events & EPOLLIN) {
for (int j = 0; j < MAX_CONNECTIONS; j++)
{
if (events[i].data.fd == sockfd[j])
{
index = j;
break;
}
}
bzero(buffer[index], MAXBUF);
ssize_t nByte = recv_data(index);
//if (nByte > 0) printf("Received: %s from %s at %s \n", buffer[index], domains[index], servers[index]);
if (nByte > 0) total_bytes += nByte;
if (nByte == 0)
{
close_socket(index);
if (!done)
{
get_domain_and_ip(index);
}
}
}
}
get_time();
if (done && count == 0) break;
}
ares_destroy(channel);
ares_library_cleanup();
fclose(fp);
printf("\nFinished without errors\n");
return 0;
}
EDIT
As requested I've performed more extensive testing with smaller numbers of concurrent connections. The results are below. The results are for 2, 4, 8, 16, 32, 64, 128, 256 and 512 concurrent connections.
total domains=4 elapsed=60.37s domains/s=0.07 KB=5 Mbit/s=0.00
total domains=0 elapsed=60.39s domains/s=0.00 KB=5 Mbit/s=0.00
total domains=17 elapsed=60.40s domains/s=0.28 KB=73 Mbit/s=0.01
total domains=17 elapsed=60.48s domains/s=0.28 KB=106 Mbit/s=0.01
total domains=40 elapsed=60.45s domains/s=0.66 KB=189 Mbit/s=0.02
total domains=172 elapsed=60.37s domains/s=2.85 KB=907 Mbit/s=0.10
total domains=312 elapsed=60.56s domains/s=5.15 KB=1272 Mbit/s=0.14
total domains=1165 elapsed=60.65s domains/s=19.21 KB=5687 Mbit/s=0.62
total domains=1966 elapsed=60.34s domains/s=32.58 KB=9884 Mbit/s=1.09
The tests show that for smaller numbers of concurrent connections we do see an increase in performance for increasing numbers of concurrent connections.
EDIT
As requested in chat here is a link to the file of domains I am testing with. Warning - the file is over 2GB in size. It has over 130M domains. domains Or if you just want 100,000 here is another link to play with 100,000 domains
I have made a C-program that should talk USB. But I have got an error -1 that says I have som Input/Output errors.
My C code is here below. I going to explain how it works. The function getDevices() print out the vendor ID and the product ID and also the device name. Remember those when you running the function getDevices().
Then you have to choose what device you want to connect. When you have choose the device, then you write in the vendor ID and product ID here.
#define USB_VENDOR_ID 1155
#define USB_PRODUCT_ID 14415 /* USB product ID used by the device */
After that, you need to run the function connectUSB(). It simply connect to your USB depening on which USB_VENDOR_ID and USB_PRODUCT_ID you have been written.
I have issues to read the incomming message. I got the error LIBUSB_ERROR_IO = -1 when I try to read.
So why does I get -1 error when I try to read? I have tried with:
Run the C-code as full administration rights, e.g root
Unmout the USB device inside the computer, not plug it out
Tested to have a USB reader software ON meanwhile I use this C-program
If you want to try my code, you have to install LibUSB. From debbased systems such as Debian, Ubuntu, Raspberry etc. it can be done by writing this command in the terminal.
sudo apt-get install libusb-1.0.0-dev libusb-1.0-0
My C-code below.
#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include <string.h>
libusb_context *CONTEXT; //a libusb session
libusb_device_handle *DEVICEHANDLE; //a device handle
libusb_device * DEVICE_POINTER; // a pointer to a device
libusb_device **ARRAY_OF_POINTERS_TO_DEVICE; // an array of pointers to devices
ssize_t NUMBER_OF_USB_DEVICES; // Initial zero devices
static uint8_t receiveBuf[64];
uint8_t transferBuf[64];
uint16_t counter = 0;
#define USB_ENDPOINT_IN 0x80 /* endpoint address */
#define USB_ENDPOINT_OUT 0x01 /* endpoint address */
#define USB_TIMEOUT 3000 /* Connection timeout (in ms) */
#define USB_VENDOR_ID 1155
#define USB_PRODUCT_ID 14415 /* USB product ID used by the device */
/*
* Here we are going to read the USB
*/
int readUSB() {
int nread, r, counter = 0;
nread = 4;
int transfered;
unsigned char data[4];
r = libusb_bulk_transfer(DEVICEHANDLE, USB_ENDPOINT_IN, data, nread, &transfered, USB_TIMEOUT);
if (LIBUSB_ERROR_TIMEOUT == r) {
printf("LIBUSB_ERROR_TIMEOUT = %d\n", r);
return -1;
}else if(LIBUSB_ERROR_PIPE == r){
printf("LIBUSB_ERROR_PIPE = %d\n", r);
return -1;
}else if(LIBUSB_ERROR_OVERFLOW == r){
printf("LIBUSB_ERROR_OVERFLOW = %d\n", r);
return -1;
}else if(LIBUSB_ERROR_NO_DEVICE == r){
printf("LIBUSB_ERROR_NO_DEVICE = %d\n", r);
return -1;
}else if(LIBUSB_ERROR_IO == r){
printf("LIBUSB_ERROR_IO = %d\n", r);
return -1;
} else {
printf("r = %d, %d receive %d bytes from device: %s\n", r, ++counter, nread, data);
return 0;
}
return 0;
}
/*
* Here are we going to write to the USB
*/
int writeUSB() {
int n, ret;
uint16_t count = 0;
//count up
n = sprintf(transferBuf, "%d\0", count++);
//write transfer
//probably unsafe to use n twice...
ret = libusb_bulk_transfer(DEVICEHANDLE, USB_ENDPOINT_OUT, transferBuf, n,
&n, USB_TIMEOUT);
//Error handling
switch (ret) {
case 0:
printf("send %d bytes to device\n", n);
return 0;
case LIBUSB_ERROR_TIMEOUT:
printf("ERROR in bulk write: %d Timeout\n", ret);
break;
case LIBUSB_ERROR_PIPE:
printf("ERROR in bulk write: %d Pipe\n", ret);
break;
case LIBUSB_ERROR_OVERFLOW:
printf("ERROR in bulk write: %d Overflow\n", ret);
break;
case LIBUSB_ERROR_NO_DEVICE:
printf("ERROR in bulk write: %d No Device\n", ret);
break;
default:
printf("ERROR in bulk write: %d\n", ret);
break;
}
return -1;
return 0;
}
/*
* This will connect to our USB device
*/
int connectUSB() {
int r;
r = libusb_init(&CONTEXT);
if(r > 0){
printf("Libusb_init error %d\n", r);
return -1;
}
//libusb_set_debug(CONTEXT, 0);
//Open Device with VendorID and ProductID
DEVICEHANDLE = libusb_open_device_with_vid_pid(CONTEXT, USB_VENDOR_ID, USB_PRODUCT_ID);
if (DEVICEHANDLE == NULL) {
perror("DEVICEHANDLE == NULL");
return -1;
}
//Claim Interface 0 from the device
r = libusb_claim_interface(DEVICEHANDLE, 0);
if (r == LIBUSB_ERROR_NOT_FOUND) {
fprintf(stderr, "LIBUSB_ERROR_NOT_FOUND = %d\n", r);
return -1;
}else if(r == LIBUSB_ERROR_BUSY){
fprintf(stderr, "LIBUSB_ERROR_BUSY = %d\n", r);
return -1;
}else if(r == LIBUSB_ERROR_NO_DEVICE){
fprintf(stderr, "LIBUSB_ERROR_NO_DEVICE = %d\n", r);
return -1;
}
printf("Interface claimed\n");
return 0;
}
/*
* This will show all the devices for USB and send it thru sockets
*/
int getDevices() {
/*
* Special local device handle for only get the name of the USB device
*/
libusb_device_handle *DEVICEHANDLE_NULL; //a device handle
/*
* Compute the number of USB
*/
int returnValue = libusb_init(NULL);
NUMBER_OF_USB_DEVICES = libusb_get_device_list(NULL,
&ARRAY_OF_POINTERS_TO_DEVICE);
/*
* Create our list of Vendor, Product and device
* Vendor and product are important for connect the USB and device is important for the user to see which USB to connect
*/
uint16_t vendor[NUMBER_OF_USB_DEVICES];
uint16_t product[NUMBER_OF_USB_DEVICES];
char device[NUMBER_OF_USB_DEVICES][256 * 2];
/*
* Loop thru all USB devices
*/
ssize_t deviceIndex = 0;
while (deviceIndex < NUMBER_OF_USB_DEVICES) {
/*
* Get the description of the USB device
*/
DEVICE_POINTER = ARRAY_OF_POINTERS_TO_DEVICE[deviceIndex];
struct libusb_device_descriptor deviceDescriptor;
returnValue = libusb_get_device_descriptor(DEVICE_POINTER,
&deviceDescriptor);
if (returnValue != LIBUSB_SUCCESS)
break;
/*
* Open the USB device with NULL. It's only because we want the name of the USB device
*/
DEVICEHANDLE_NULL = NULL;
returnValue = libusb_open(DEVICE_POINTER, &DEVICEHANDLE_NULL);
if (returnValue != LIBUSB_SUCCESS) {
/*
* There was an error. Not success.
*/
if (DEVICEHANDLE_NULL != NULL) {
libusb_close(DEVICEHANDLE_NULL);
DEVICEHANDLE_NULL = NULL;
}
/*
* Write as there was no info at all to display
*/
product[deviceIndex] = 0;
vendor[deviceIndex] = 0;
memcpy(device[deviceIndex], "-", 256 * 2 * sizeof(char));
deviceIndex++;
continue;
}
/*
* Get the string associated with iManufacturer index.
*/
const int STRING_LENGTH = 256;
unsigned char stringManufacturer[STRING_LENGTH];
unsigned char stringProduct[STRING_LENGTH];
char stringDeviceName[STRING_LENGTH * 2];
if (DEVICEHANDLE_NULL != NULL && deviceDescriptor.iManufacturer > 0) {
returnValue = libusb_get_string_descriptor_ascii(DEVICEHANDLE_NULL,
deviceDescriptor.iManufacturer, stringManufacturer,
STRING_LENGTH);
if (returnValue < 0)
break;
}
/*
* Get string associated with iProduct index.
*/
if (DEVICEHANDLE_NULL != NULL && deviceDescriptor.iProduct > 0) {
returnValue = libusb_get_string_descriptor_ascii(DEVICEHANDLE_NULL,
deviceDescriptor.iProduct, stringProduct, STRING_LENGTH);
if (returnValue < 0)
break;
}
/*
* Combine manufacturer and product
*/
strcpy(stringDeviceName, (char*) stringManufacturer);
strcat(stringDeviceName, " "); // a space only
strcat(stringDeviceName, (char*) stringProduct);
//printf("%s\n", stringDeviceName);
/*
* Save them all into arrays
*/
product[deviceIndex] = deviceDescriptor.idProduct;
vendor[deviceIndex] = deviceDescriptor.idVendor;
memcpy(device[deviceIndex], stringDeviceName, 256 * 2 * sizeof(char));
/*
* Close and try next one.
*/
if (DEVICEHANDLE_NULL != NULL) {
libusb_close(DEVICEHANDLE_NULL);
DEVICEHANDLE_NULL = NULL;
}
/*
* Next USB device
*/
deviceIndex++;
}
/*
* Print our result what we found and send them to socket
*/
for (int i = 0; i < 11; i++) {
printf("Name: %s\n", device[i]);
printf("Vendor: %u\n", vendor[i]);
printf("Product: %u\n\n", product[i]);
}
libusb_exit(NULL);
return 0;
}
How do you listen to changes in volume on the Master channel on the default sound card? I'd like to be notified through dbus or a callback or something that the volume has changed.
I have tried looking and the ALSA and PulseAudio APIs and they only seem to allow you to set and get the volume, but not listen for changes in the volume.
Any programming language is fine.
Edit: In the second example, an event isn't generated for me when volume is below 5% or above 100%. The first example works perfectly as far as I know.
pactl subscribe will print out data about the sinks when the volume changes. What I'm doing now is piping the output to a small C program that will run a script.
run.sh:
pactl subscribe | grep --line-buffered "sink" | ./prog
or for a specific sink, e.g. 3:
pactl subscribe | grep --line-buffered "sink #3" | ./prog
prog.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
while(1){
while(getchar() != '\n');
system("./volume_notify.sh");
}
}
When the volume of the sink is changed, pactl will print a line, which will cause the program to run the script.
-or-
Here's an example based on the amixer monitor, as referenced by CL.
The while loop will iterate each time the volume changes, so put your callback in there.
#include <stdio.h>
#include <alsa/asoundlib.h>
#define MAX_CARDS 256
int monitor_native(char const *name);
int open_ctl(const char *name, snd_ctl_t **ctlp);
void close_all(snd_ctl_t* ctls[], int ncards);
int main(int argc, char* argv[]){
const char *ctl_name = "hw:0";
while(monitor_native(ctl_name) == 1){
//volume has been changed, do something
system("~/.volume_notify.sh");
}
return 0;
}
int monitor_native(char const *name) {
snd_ctl_t *ctls[MAX_CARDS];
int ncards = 0;
int i, err = 0;
if (!name) {
int card = -1;
while (snd_card_next(&card) >= 0 && card >= 0) {
char cardname[16];
if (ncards >= MAX_CARDS) {
fprintf(stderr, "alsactl: too many cards\n");
close_all(ctls, ncards);
return -E2BIG;
}
sprintf(cardname, "hw:%d", card);
err = open_ctl(cardname, &ctls[ncards]);
if (err < 0) {
close_all(ctls, ncards);
return err;
}
ncards++;
}
} else {
err = open_ctl(name, &ctls[0]);
if (err < 0) {
close_all(ctls, ncards);
return err;
}
ncards++;
}
for (;ncards > 0;) {
pollfd* fds = new pollfd[ncards];
for (i = 0; i < ncards; i++) {
snd_ctl_poll_descriptors(ctls[i], &fds[i], 1);
}
err = poll(fds, ncards, -1);
if (err <= 0) {
err = 0;
break;
}
for (i = 0; i < ncards; i++) {
unsigned short revents;
snd_ctl_poll_descriptors_revents(ctls[i], &fds[i], 1, &revents);
if (revents & POLLIN) {
snd_ctl_event_t *event;
snd_ctl_event_alloca(&event);
if (snd_ctl_read(ctls[i], event) < 0) {
continue;
}
if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) {
continue;
}
unsigned int mask = snd_ctl_event_elem_get_mask(event);
if (mask & SND_CTL_EVENT_MASK_VALUE) {
close_all(ctls, ncards);
return 1;
}
}
}
}
close_all(ctls, ncards);
return 0;
}
int open_ctl(const char *name, snd_ctl_t **ctlp) {
snd_ctl_t *ctl;
int err;
err = snd_ctl_open(&ctl, name, SND_CTL_READONLY);
if (err < 0) {
fprintf(stderr, "Cannot open ctl %s\n", name);
return err;
}
err = snd_ctl_subscribe_events(ctl, 1);
if (err < 0) {
fprintf(stderr, "Cannot open subscribe events to ctl %s\n", name);
snd_ctl_close(ctl);
return err;
}
*ctlp = ctl;
return 0;
}
void close_all(snd_ctl_t* ctls[], int ncards) {
for (ncards -= 1; ncards >= 0; --ncards) {
snd_ctl_close(ctls[ncards]);
}
}
This is possible with the ALSA API.
When you have a control device, call snd_ctl_subscribe_events() to enable events.
Then use snd_ctl_read() to read events; to wait for them, use blocking mode or poll().
If the event is of type SND_CTL_EVENT_ELEM, and if its event bit mask contains SND_CTL_EVENT_MASK_VALUE, that element's value has changed.
See the implementation of amixer monitor for an example.
Similar to #sealj553's answer, but does not need a C-program:
pactl subscribe | grep --line-buffered "sink" | xargs -n1 ./volume_notify.sh