-
-
Save gowrav/3160c30347fbdc7ab7fe0ba135e3746b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Libraries required | |
GCC /G++ compiler | |
PulseAudio | |
https://freedesktop.org/software/pulseaudio/doxygen/parec-simple_8c-example.html | |
ensure pulse audio binaries are runnnig before using the APIs | |
run$ pulseaudio -vvvv | |
Following message is an indication that you need to run pulseaudio binary | |
W: [] caps.c: Normally all extra capabilities would be dropped now, but that's impossible because PulseAudio was built without capabilities support. | |
-----failed: Connection refused | |
run either "pulseaudio --start" or "pulseaudio -v" to start the binary and keep the app running | |
Listing sources in the device | |
pulseaudio-device-list.c | |
compile$ gcc pulseaudio-device-list.c -o devlist -lpulse | |
run$ ./devlist | |
Record Sound from primary device | |
compile$ gcc micpa.c -o micpa -lpulse -lstdc++ -lpulse-simple | |
run$ ./micpa > test.raw | |
Convert test.raw to wave file: | |
sox -t raw -b 16 -e signed-integer -r 44100 test.raw test.wav | |
Echo live mic audio RAW on display | |
compile$ gcc parec-simple.c -o parec -lpulse -lstdc++ -lpulse-simple | |
run$ ./parec | |
simple playback tool using the simple API | |
compile$ gcc pacat-simple.c -o pacat -lpulse -lstdc++ -lpulse-simple | |
run$ ./pacat | |
Record Log and playback tool using the simple API | |
compile$ gcc mmp_audiosource.c -o mmp -lpulse -lstdc++ -lpulse-simple | |
run$ ./mmp | |
{ | |
choose from the listed input and output device to connect the pipes for playthrough and logging | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (c) 2010-2012 Julius project team, Nagoya Institute of Technology | |
* All rights reserved | |
*/ | |
#include <stdio.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <stddef.h> | |
#include <unistd.h> | |
#include <pulse/simple.h> | |
#include <pulse/error.h> | |
#define BUFSIZE 32 | |
#define RATE 44100 | |
//#define RATE 16000 | |
int16_t buffer[BUFSIZE]; | |
static pa_simple *s = NULL; | |
static char name_buf[] = "PulseAudio default device"; | |
/** | |
* Connection initialization: check connectivity and open for recording. | |
* | |
* @param sfreq [in] required sampling frequency | |
* @param dummy [in] a dummy data | |
* | |
* @return TRUE on success, FALSE on failure. | |
*/ | |
int adin_pulseaudio_standby(int sfreq, void *dummy) | |
{ | |
return 0; | |
} | |
/** | |
* Start recording. | |
* @a pathname is dummy. | |
* | |
* @param arg [in] argument | |
* | |
* @return TRUE on success, FALSE on failure. | |
*/ | |
int adin_pulseaudio_begin(char *arg) | |
{ | |
int error; | |
static const pa_sample_spec ss = { | |
.format = PA_SAMPLE_S16LE, | |
.rate = RATE, | |
.channels = 1 | |
}; | |
if (!(s = pa_simple_new(NULL, "Julius", PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) { | |
printf("Error: adin_pulseaudio: pa_simple_new() failed: %s\n", pa_strerror(error)); | |
return 1; | |
} | |
return 0; | |
} | |
/** | |
* Stop recording. | |
* | |
* @return TRUE on success, FALSE on failure. | |
*/ | |
int adin_pulseaudio_end() | |
{ | |
if (s != NULL) { | |
pa_simple_free(s); | |
s = NULL; | |
} | |
return 0; | |
} | |
/** | |
* @brief Read samples from device | |
* | |
* Try to read @a sampnum samples and returns actual number of recorded | |
* samples currently available. This function will block until | |
* at least one sample was obtained. | |
* | |
* @param buf [out] samples obtained in this function | |
* @param sampnum [in] wanted number of samples to be read | |
* | |
* @return actural number of read samples, -2 if an error occured. | |
*/ | |
int adin_pulseaudio_read (int16_t *buf, int sampnum) | |
{ | |
int error; | |
int cnt, bufsize; | |
bufsize = sampnum * sizeof(int16_t); | |
if (bufsize > BUFSIZE) bufsize = BUFSIZE; | |
if (pa_simple_read(s, buf, bufsize, &error) < 0) { | |
printf("Error: pa_simple_read() failed: %s\n", pa_strerror(error)); | |
} | |
cnt = bufsize / sizeof(int16_t); | |
return (cnt); | |
} | |
int main() | |
{ | |
adin_pulseaudio_begin("nothing"); | |
while(1) | |
{ | |
adin_pulseaudio_read(buffer, 32); | |
/* And write it to STDOUT */ | |
if (write(STDOUT_FILENO, buffer, 32) != 32) { | |
fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); | |
goto finish; | |
} | |
usleep(5); | |
} | |
finish: | |
adin_pulseaudio_end(); | |
return 0; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef HAVE_CONFIG_H | |
#include <config.h> | |
#endif | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <pulse/simple.h> | |
#include <pulse/error.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <time.h> | |
#include <stdlib.h> | |
#include <pulse/pulseaudio.h> | |
#define BUFSIZE 1024 | |
// Field list is here: http://0pointer.de/lennart/projects/pulseaudio/doxygen/structpa__sink__info.html | |
typedef struct pa_devicelist { | |
uint8_t initialized; | |
char name[512]; | |
uint32_t index; | |
char description[256]; | |
} pa_devicelist_t; | |
void pa_state_cb(pa_context *c, void *userdata); | |
void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); | |
void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); | |
int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output); | |
// This callback gets called when our context changes state. We really only | |
// care about when it's ready or if it has failed | |
void pa_state_cb(pa_context *c, void *userdata) { | |
pa_context_state_t state; | |
int *pa_ready = userdata; | |
state = pa_context_get_state(c); | |
switch (state) { | |
// There are just here for reference | |
case PA_CONTEXT_UNCONNECTED: | |
case PA_CONTEXT_CONNECTING: | |
case PA_CONTEXT_AUTHORIZING: | |
case PA_CONTEXT_SETTING_NAME: | |
default: | |
break; | |
case PA_CONTEXT_FAILED: | |
case PA_CONTEXT_TERMINATED: | |
*pa_ready = 2; | |
break; | |
case PA_CONTEXT_READY: | |
*pa_ready = 1; | |
break; | |
} | |
} | |
// pa_mainloop will call this function when it's ready to tell us about a sink. | |
// Since we're not threading, there's no need for mutexes on the devicelist | |
// structure | |
void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { | |
pa_devicelist_t *pa_devicelist = userdata; | |
int ctr = 0; | |
// If eol is set to a positive number, you're at the end of the list | |
if (eol > 0) { | |
return; | |
} | |
// We know we've allocated 16 slots to hold devices. Loop through our | |
// structure and find the first one that's "uninitialized." Copy the | |
// contents into it and we're done. If we receive more than 16 devices, | |
// they're going to get dropped. You could make this dynamically allocate | |
// space for the device list, but this is a simple example. | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_devicelist[ctr].initialized) { | |
strncpy(pa_devicelist[ctr].name, l->name, 511); | |
strncpy(pa_devicelist[ctr].description, l->description, 255); | |
pa_devicelist[ctr].index = l->index; | |
pa_devicelist[ctr].initialized = 1; | |
break; | |
} | |
} | |
} | |
// See above. This callback is pretty much identical to the previous | |
void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { | |
pa_devicelist_t *pa_devicelist = userdata; | |
int ctr = 0; | |
if (eol > 0) { | |
return; | |
} | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_devicelist[ctr].initialized) { | |
strncpy(pa_devicelist[ctr].name, l->name, 511); | |
strncpy(pa_devicelist[ctr].description, l->description, 255); | |
pa_devicelist[ctr].index = l->index; | |
pa_devicelist[ctr].initialized = 1; | |
break; | |
} | |
} | |
} | |
int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output) { | |
// Define our pulse audio loop and connection variables | |
pa_mainloop *pa_ml; | |
pa_mainloop_api *pa_mlapi; | |
pa_operation *pa_op; | |
pa_context *pa_ctx; | |
// We'll need these state variables to keep track of our requests | |
int state = 0; | |
int pa_ready = 0; | |
// Initialize our device lists | |
memset(input, 0, sizeof(pa_devicelist_t) * 16); | |
memset(output, 0, sizeof(pa_devicelist_t) * 16); | |
// Create a mainloop API and connection to the default server | |
pa_ml = pa_mainloop_new(); | |
pa_mlapi = pa_mainloop_get_api(pa_ml); | |
pa_ctx = pa_context_new(pa_mlapi, "test"); | |
// This function connects to the pulse server | |
pa_context_connect(pa_ctx, NULL, 0, NULL); | |
// This function defines a callback so the server will tell us it's state. | |
// Our callback will wait for the state to be ready. The callback will | |
// modify the variable to 1 so we know when we have a connection and it's | |
// ready. | |
// If there's an error, the callback will set pa_ready to 2 | |
pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); | |
// Now we'll enter into an infinite loop until we get the data we receive | |
// or if there's an error | |
for (;;) { | |
// We can't do anything until PA is ready, so just iterate the mainloop | |
// and continue | |
if (pa_ready == 0) { | |
pa_mainloop_iterate(pa_ml, 1, NULL); | |
continue; | |
} | |
// We couldn't get a connection to the server, so exit out | |
if (pa_ready == 2) { | |
pa_context_disconnect(pa_ctx); | |
pa_context_unref(pa_ctx); | |
pa_mainloop_free(pa_ml); | |
return -1; | |
} | |
// At this point, we're connected to the server and ready to make | |
// requests | |
switch (state) { | |
// State 0: we haven't done anything yet | |
case 0: | |
// This sends an operation to the server. pa_sinklist_info is | |
// our callback function and a pointer to our devicelist will | |
// be passed to the callback The operation ID is stored in the | |
// pa_op variable | |
pa_op = pa_context_get_sink_info_list(pa_ctx, | |
pa_sinklist_cb, | |
output | |
); | |
// Update state for next iteration through the loop | |
state++; | |
break; | |
case 1: | |
// Now we wait for our operation to complete. When it's | |
// complete our pa_output_devicelist is filled out, and we move | |
// along to the next state | |
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { | |
pa_operation_unref(pa_op); | |
// Now we perform another operation to get the source | |
// (input device) list just like before. This time we pass | |
// a pointer to our input structure | |
pa_op = pa_context_get_source_info_list(pa_ctx, | |
pa_sourcelist_cb, | |
input | |
); | |
// Update the state so we know what to do next | |
state++; | |
} | |
break; | |
case 2: | |
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { | |
// Now we're done, clean up and disconnect and return | |
pa_operation_unref(pa_op); | |
pa_context_disconnect(pa_ctx); | |
pa_context_unref(pa_ctx); | |
pa_mainloop_free(pa_ml); | |
return 0; | |
} | |
break; | |
default: | |
// We should never see this state | |
fprintf(stderr, "in state %d\n", state); | |
return -1; | |
} | |
// Iterate the main loop and go again. The second argument is whether | |
// or not the iteration should block until something is ready to be | |
// done. Set it to zero for non-blocking. | |
pa_mainloop_iterate(pa_ml, 1, NULL); | |
} | |
} | |
/* A simple routine calling UNIX write() in a loop */ | |
static ssize_t loop_write(int fd, const void*data, size_t size) { | |
ssize_t ret = 0; | |
while (size > 0) { | |
ssize_t r; | |
if ((r = write(fd, data, size)) < 0) | |
return r; | |
if (r == 0) | |
break; | |
ret += r; | |
data = (const uint8_t*) data + r; | |
size -= (size_t) r; | |
} | |
return ret; | |
} | |
int main(int argc, char *argv[]) { | |
int ctr; | |
printf("MMP AudioSource\n"); | |
// This is where we'll store the input device list | |
pa_devicelist_t pa_input_devicelist[16]; | |
// This is where we'll store the output device list | |
pa_devicelist_t pa_output_devicelist[16]; | |
printf("Fetching Devices\n"); | |
if (pa_get_devicelist(pa_input_devicelist, pa_output_devicelist) < 0) { | |
fprintf(stderr, "failed to get device list\n"); | |
return 1; | |
} | |
printf("Listing Devices\n"); | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_output_devicelist[ctr].initialized) { | |
break; | |
} | |
printf("=======[ Output Device #%d ]=======\n", ctr+1); | |
printf("Description: %s\n", pa_output_devicelist[ctr].description); | |
printf("Name: %s\n", pa_output_devicelist[ctr].name); | |
printf("Index: %d\n", pa_output_devicelist[ctr].index); | |
printf("\n"); | |
} | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_input_devicelist[ctr].initialized) { | |
break; | |
} | |
printf("=======[ Input Device #%d ]=======\n", ctr+1); | |
printf("Description: %s\n", pa_input_devicelist[ctr].description); | |
printf("Name: %s\n", pa_input_devicelist[ctr].name); | |
printf("Index: %d\n", pa_input_devicelist[ctr].index); | |
printf("\n"); | |
} | |
// Make your device selection here | |
char *InputSource = NULL; | |
char *OutputSource = NULL; | |
printf("\n\nMake your device selection here\n"); | |
printf("Input : <Please enter an Index (integer) value> :"); | |
int inI;scanf("%d", &inI); | |
printf("InputDevice : %s\n",pa_input_devicelist[inI].name); | |
InputSource = pa_input_devicelist[inI].name; | |
printf("\n\nOutput : <Please enter an Index (integer) value> :"); | |
int outI;scanf("%d", &outI); | |
printf("OutputDevice : %s\n",pa_output_devicelist[outI].name); | |
OutputSource = pa_output_devicelist[outI].name; | |
/* The sample type to use */ | |
static const pa_sample_spec ss = { | |
.format = PA_SAMPLE_S16LE, | |
.rate = 44100, | |
.channels = 2 | |
}; | |
pa_simple *s = NULL; | |
pa_simple *t = NULL; | |
int ret = 1; | |
int error; | |
/* Create the recording stream */ | |
//Default > pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error) | |
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, InputSource, "record", &ss, NULL, NULL, &error))) { | |
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
/* Create a new playback stream */ | |
//Default > pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error) | |
if (!(t = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, OutputSource, "playback", &ss, NULL, NULL, &error))) { | |
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
// Continously Read and output Audio Data | |
printf("\nPrint Audio Data (RAW) \n"); | |
for (;;) { | |
uint8_t buf[BUFSIZE]; | |
/* Record some data ... */ | |
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) { | |
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
/* And write it to STDOUT */ | |
if (loop_write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf)) { | |
fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); | |
goto finish; | |
} | |
/* ... and play it */ | |
if (pa_simple_write(t, buf, (size_t) sizeof(buf), &error) < 0) { | |
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
} | |
/* Make sure that every single sample was played */ | |
if (pa_simple_drain(t, &error) < 0) { | |
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
ret = 0; | |
finish: | |
if (s) | |
pa_simple_free(s); | |
return ret; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** | |
This file is part of PulseAudio. | |
PulseAudio is free software; you can redistribute it and/or modify | |
it under the terms of the GNU Lesser General Public License as published | |
by the Free Software Foundation; either version 2.1 of the License, | |
or (at your option) any later version. | |
PulseAudio 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 | |
General Public License for more details. | |
You should have received a copy of the GNU Lesser General Public License | |
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. | |
***/ | |
#ifdef HAVE_CONFIG_H | |
#include <config.h> | |
#endif | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <pulse/simple.h> | |
#include <pulse/error.h> | |
#define BUFSIZE 1024 | |
int main(int argc, char*argv[]) { | |
/* The Sample format to use */ | |
static const pa_sample_spec ss = { | |
.format = PA_SAMPLE_S16LE, | |
.rate = 44100, | |
.channels = 2 | |
}; | |
pa_simple *s = NULL; | |
int ret = 1; | |
int error; | |
/* replace STDIN with the specified file if needed */ | |
if (argc > 1) { | |
int fd; | |
if ((fd = open(argv[1], O_RDONLY)) < 0) { | |
fprintf(stderr, __FILE__": open() failed: %s\n", strerror(errno)); | |
goto finish; | |
} | |
if (dup2(fd, STDIN_FILENO) < 0) { | |
fprintf(stderr, __FILE__": dup2() failed: %s\n", strerror(errno)); | |
goto finish; | |
} | |
close(fd); | |
} | |
/* Create a new playback stream */ | |
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error))) { | |
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
for (;;) { | |
uint8_t buf[BUFSIZE]; | |
ssize_t r; | |
#if 0 | |
pa_usec_t latency; | |
if ((latency = pa_simple_get_latency(s, &error)) == (pa_usec_t) -1) { | |
fprintf(stderr, __FILE__": pa_simple_get_latency() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
fprintf(stderr, "%0.0f usec \r", (float)latency); | |
#endif | |
/* Read some data ... */ | |
if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) { | |
if (r == 0) /* EOF */ | |
break; | |
fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno)); | |
goto finish; | |
} | |
/* ... and play it */ | |
if (pa_simple_write(s, buf, (size_t) r, &error) < 0) { | |
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
} | |
/* Make sure that every single sample was played */ | |
if (pa_simple_drain(s, &error) < 0) { | |
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
ret = 0; | |
finish: | |
if (s) | |
pa_simple_free(s); | |
return ret; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** | |
This file is part of PulseAudio. | |
PulseAudio is free software; you can redistribute it and/or modify | |
it under the terms of the GNU Lesser General Public License as published | |
by the Free Software Foundation; either version 2.1 of the License, | |
or (at your option) any later version. | |
PulseAudio 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 | |
General Public License for more details. | |
You should have received a copy of the GNU Lesser General Public License | |
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. | |
***/ | |
#ifdef HAVE_CONFIG_H | |
#include <config.h> | |
#endif | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <pulse/simple.h> | |
#include <pulse/error.h> | |
#define BUFSIZE 1024 | |
/* A simple routine calling UNIX write() in a loop */ | |
static ssize_t loop_write(int fd, const void*data, size_t size) { | |
ssize_t ret = 0; | |
while (size > 0) { | |
ssize_t r; | |
if ((r = write(fd, data, size)) < 0) | |
return r; | |
if (r == 0) | |
break; | |
ret += r; | |
data = (const uint8_t*) data + r; | |
size -= (size_t) r; | |
} | |
return ret; | |
} | |
int main(int argc, char*argv[]) { | |
/* The sample type to use */ | |
static const pa_sample_spec ss = { | |
.format = PA_SAMPLE_S16LE, | |
.rate = 44100, | |
.channels = 2 | |
}; | |
pa_simple *s = NULL; | |
int ret = 1; | |
int error; | |
/* Create the recording stream */ | |
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) { | |
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
for (;;) { | |
uint8_t buf[BUFSIZE]; | |
/* Record some data ... */ | |
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) { | |
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); | |
goto finish; | |
} | |
/* And write it to STDOUT */ | |
if (loop_write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf)) { | |
fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); | |
goto finish; | |
} | |
} | |
ret = 0; | |
finish: | |
if (s) | |
pa_simple_free(s); | |
return ret; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <time.h> | |
#include <stdlib.h> | |
#include <pulse/pulseaudio.h> | |
// Field list is here: http://0pointer.de/lennart/projects/pulseaudio/doxygen/structpa__sink__info.html | |
typedef struct pa_devicelist { | |
uint8_t initialized; | |
char name[512]; | |
uint32_t index; | |
char description[256]; | |
} pa_devicelist_t; | |
void pa_state_cb(pa_context *c, void *userdata); | |
void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); | |
void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); | |
int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output); | |
// This callback gets called when our context changes state. We really only | |
// care about when it's ready or if it has failed | |
void pa_state_cb(pa_context *c, void *userdata) { | |
pa_context_state_t state; | |
int *pa_ready = userdata; | |
state = pa_context_get_state(c); | |
switch (state) { | |
// There are just here for reference | |
case PA_CONTEXT_UNCONNECTED: | |
case PA_CONTEXT_CONNECTING: | |
case PA_CONTEXT_AUTHORIZING: | |
case PA_CONTEXT_SETTING_NAME: | |
default: | |
break; | |
case PA_CONTEXT_FAILED: | |
case PA_CONTEXT_TERMINATED: | |
*pa_ready = 2; | |
break; | |
case PA_CONTEXT_READY: | |
*pa_ready = 1; | |
break; | |
} | |
} | |
// pa_mainloop will call this function when it's ready to tell us about a sink. | |
// Since we're not threading, there's no need for mutexes on the devicelist | |
// structure | |
void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { | |
pa_devicelist_t *pa_devicelist = userdata; | |
int ctr = 0; | |
// If eol is set to a positive number, you're at the end of the list | |
if (eol > 0) { | |
return; | |
} | |
// We know we've allocated 16 slots to hold devices. Loop through our | |
// structure and find the first one that's "uninitialized." Copy the | |
// contents into it and we're done. If we receive more than 16 devices, | |
// they're going to get dropped. You could make this dynamically allocate | |
// space for the device list, but this is a simple example. | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_devicelist[ctr].initialized) { | |
strncpy(pa_devicelist[ctr].name, l->name, 511); | |
strncpy(pa_devicelist[ctr].description, l->description, 255); | |
pa_devicelist[ctr].index = l->index; | |
pa_devicelist[ctr].initialized = 1; | |
break; | |
} | |
} | |
} | |
// See above. This callback is pretty much identical to the previous | |
void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { | |
pa_devicelist_t *pa_devicelist = userdata; | |
int ctr = 0; | |
if (eol > 0) { | |
return; | |
} | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_devicelist[ctr].initialized) { | |
strncpy(pa_devicelist[ctr].name, l->name, 511); | |
strncpy(pa_devicelist[ctr].description, l->description, 255); | |
pa_devicelist[ctr].index = l->index; | |
pa_devicelist[ctr].initialized = 1; | |
break; | |
} | |
} | |
} | |
int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output) { | |
// Define our pulse audio loop and connection variables | |
pa_mainloop *pa_ml; | |
pa_mainloop_api *pa_mlapi; | |
pa_operation *pa_op; | |
pa_context *pa_ctx; | |
// We'll need these state variables to keep track of our requests | |
int state = 0; | |
int pa_ready = 0; | |
// Initialize our device lists | |
memset(input, 0, sizeof(pa_devicelist_t) * 16); | |
memset(output, 0, sizeof(pa_devicelist_t) * 16); | |
// Create a mainloop API and connection to the default server | |
pa_ml = pa_mainloop_new(); | |
pa_mlapi = pa_mainloop_get_api(pa_ml); | |
pa_ctx = pa_context_new(pa_mlapi, "test"); | |
// This function connects to the pulse server | |
pa_context_connect(pa_ctx, NULL, 0, NULL); | |
// This function defines a callback so the server will tell us it's state. | |
// Our callback will wait for the state to be ready. The callback will | |
// modify the variable to 1 so we know when we have a connection and it's | |
// ready. | |
// If there's an error, the callback will set pa_ready to 2 | |
pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); | |
// Now we'll enter into an infinite loop until we get the data we receive | |
// or if there's an error | |
for (;;) { | |
// We can't do anything until PA is ready, so just iterate the mainloop | |
// and continue | |
if (pa_ready == 0) { | |
pa_mainloop_iterate(pa_ml, 1, NULL); | |
continue; | |
} | |
// We couldn't get a connection to the server, so exit out | |
if (pa_ready == 2) { | |
pa_context_disconnect(pa_ctx); | |
pa_context_unref(pa_ctx); | |
pa_mainloop_free(pa_ml); | |
return -1; | |
} | |
// At this point, we're connected to the server and ready to make | |
// requests | |
switch (state) { | |
// State 0: we haven't done anything yet | |
case 0: | |
// This sends an operation to the server. pa_sinklist_info is | |
// our callback function and a pointer to our devicelist will | |
// be passed to the callback The operation ID is stored in the | |
// pa_op variable | |
pa_op = pa_context_get_sink_info_list(pa_ctx, | |
pa_sinklist_cb, | |
output | |
); | |
// Update state for next iteration through the loop | |
state++; | |
break; | |
case 1: | |
// Now we wait for our operation to complete. When it's | |
// complete our pa_output_devicelist is filled out, and we move | |
// along to the next state | |
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { | |
pa_operation_unref(pa_op); | |
// Now we perform another operation to get the source | |
// (input device) list just like before. This time we pass | |
// a pointer to our input structure | |
pa_op = pa_context_get_source_info_list(pa_ctx, | |
pa_sourcelist_cb, | |
input | |
); | |
// Update the state so we know what to do next | |
state++; | |
} | |
break; | |
case 2: | |
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { | |
// Now we're done, clean up and disconnect and return | |
pa_operation_unref(pa_op); | |
pa_context_disconnect(pa_ctx); | |
pa_context_unref(pa_ctx); | |
pa_mainloop_free(pa_ml); | |
return 0; | |
} | |
break; | |
default: | |
// We should never see this state | |
fprintf(stderr, "in state %d\n", state); | |
return -1; | |
} | |
// Iterate the main loop and go again. The second argument is whether | |
// or not the iteration should block until something is ready to be | |
// done. Set it to zero for non-blocking. | |
pa_mainloop_iterate(pa_ml, 1, NULL); | |
} | |
} | |
int main(int argc, char *argv[]) { | |
int ctr; | |
printf("Hello World\n"); | |
// This is where we'll store the input device list | |
pa_devicelist_t pa_input_devicelist[16]; | |
// This is where we'll store the output device list | |
pa_devicelist_t pa_output_devicelist[16]; | |
printf("Fetching Devices\n"); | |
if (pa_get_devicelist(pa_input_devicelist, pa_output_devicelist) < 0) { | |
fprintf(stderr, "failed to get device list\n"); | |
return 1; | |
} | |
printf("Listing Devices\n"); | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_output_devicelist[ctr].initialized) { | |
break; | |
} | |
printf("=======[ Output Device #%d ]=======\n", ctr+1); | |
printf("Description: %s\n", pa_output_devicelist[ctr].description); | |
printf("Name: %s\n", pa_output_devicelist[ctr].name); | |
printf("Index: %d\n", pa_output_devicelist[ctr].index); | |
printf("\n"); | |
} | |
for (ctr = 0; ctr < 16; ctr++) { | |
if (! pa_input_devicelist[ctr].initialized) { | |
break; | |
} | |
printf("=======[ Input Device #%d ]=======\n", ctr+1); | |
printf("Description: %s\n", pa_input_devicelist[ctr].description); | |
printf("Name: %s\n", pa_input_devicelist[ctr].name); | |
printf("Index: %d\n", pa_input_devicelist[ctr].index); | |
printf("\n"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment