// Pulse Audio bits for QtSoundmodem #include #include #include #include #include #define UNUSED(x) (void)(x) extern char CaptureNames[16][256]; extern char PlaybackNames[16][256]; extern int PlaybackCount; extern int CaptureCount; #include void *handle = NULL; void *shandle = NULL; pa_mainloop * (*ppa_mainloop_new)(void); pa_mainloop_api * (*ppa_mainloop_get_api)(pa_mainloop * m); pa_context * (*ppa_context_new)(pa_mainloop_api *mainloop, const char *name); int (*ppa_context_connect)(pa_context * c, const char * server, pa_context_flags_t flags, const pa_spawn_api * api); void (*ppa_context_set_state_callback)(pa_context * c, pa_context_notify_cb_t cb, void * userdata); int (*ppa_mainloop_iterate)(pa_mainloop * m, int block, int * retval); void (*ppa_mainloop_free)(pa_mainloop * m); void (*ppa_context_disconnect)(pa_context * c); void (*ppa_context_unref)(pa_context * c); const char * (*ppa_strerror)(int error); pa_context_state_t(*ppa_context_get_state)(const pa_context *c); pa_operation * (*ppa_context_get_sink_info_list)(pa_context * c, pa_sink_info_cb_t cb, void * userdata); pa_operation * (*ppa_context_get_source_info_list)(pa_context * c, pa_source_info_cb_t cb, void * userdata); void (*ppa_operation_unref)(pa_operation * o); pa_operation_state_t(*ppa_operation_get_state)(const pa_operation * o); pa_simple * (*ppa_simple_new)(const char * server, const char * name, pa_stream_direction_t dir, const char * dev, const char * stream_name, const pa_sample_spec * ss, const pa_channel_map * map, const pa_buffer_attr * attr, int * error) = NULL; pa_usec_t(*ppa_simple_get_latency)(pa_simple * s, int * error); int(*ppa_simple_read)(pa_simple * s, void * data, size_t bytes, int * error); int(*ppa_simple_write)(pa_simple * s, void * data, size_t bytes, int * error); int(*ppa_simple_flush)(pa_simple * s, int * error); void(*ppa_simple_free)(pa_simple * s); int(*ppa_simple_drain)(pa_simple * s, int * error); void * getModule(void *handle, char * sym) { return dlsym(handle, sym); } void * initPulse() { // Load the pulse libraries if (handle) return handle; // already done handle = dlopen("libpulse.so", RTLD_LAZY); if (!handle) { fputs(dlerror(), stderr); return NULL; } if ((ppa_mainloop_new = getModule(handle, "pa_mainloop_new")) == NULL) return NULL; if ((ppa_mainloop_get_api = getModule(handle, "pa_mainloop_get_api")) == NULL) return NULL; if ((ppa_context_new = getModule(handle, "pa_context_new")) == NULL) return NULL; if ((ppa_context_connect = getModule(handle, "pa_context_connect")) == NULL) return NULL; if ((ppa_context_set_state_callback = getModule(handle, "pa_context_set_state_callback")) == NULL) return NULL; if ((ppa_mainloop_iterate = getModule(handle, "pa_mainloop_iterate")) == NULL) return NULL; if ((ppa_mainloop_free = getModule(handle, "pa_mainloop_free")) == NULL) return NULL; if ((ppa_context_disconnect = getModule(handle, "pa_context_disconnect")) == NULL) return NULL; if ((ppa_context_unref = getModule(handle, "pa_context_unref")) == NULL) return NULL; if ((ppa_strerror = getModule(handle, "pa_strerror")) == NULL) return NULL; if ((ppa_context_get_state = getModule(handle, "pa_context_get_state")) == NULL) return NULL; if ((ppa_context_get_sink_info_list = getModule(handle, "pa_context_get_sink_info_list")) == NULL) return NULL; if ((ppa_context_get_source_info_list = getModule(handle, "pa_context_get_source_info_list")) == NULL) return NULL; if ((ppa_operation_unref = getModule(handle, "pa_operation_unref")) == NULL) return NULL; if ((ppa_operation_get_state = getModule(handle, "pa_operation_get_state")) == NULL) return NULL; shandle = dlopen("libpulse-simple.so", RTLD_LAZY); if (!shandle) { fputs(dlerror(), stderr); return NULL; } if ((ppa_simple_new = getModule(shandle, "pa_simple_new")) == NULL) return NULL; if ((ppa_simple_get_latency = getModule(shandle, "pa_simple_get_latency")) == NULL) return NULL; if ((ppa_simple_read = dlsym(shandle, "pa_simple_read")) == NULL) return NULL; if ((ppa_simple_write = dlsym(shandle, "pa_simple_write")) == NULL) return NULL; if ((ppa_simple_flush = dlsym(shandle, "pa_simple_flush")) == NULL) return NULL; if ((ppa_simple_drain = dlsym(shandle, "pa_simple_drain")) == NULL) return NULL; if ((ppa_simple_free = dlsym(shandle, "pa_simple_free")) == NULL) return NULL; return shandle; } // 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 = ppa_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) { UNUSED(c); 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) { UNUSED(c); 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 = ppa_mainloop_new(); pa_mlapi = ppa_mainloop_get_api(pa_ml); pa_ctx = ppa_context_new(pa_mlapi, "test"); // This function connects to the pulse server ppa_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 ppa_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) { ppa_mainloop_iterate(pa_ml, 1, NULL); continue; } // We couldn't get a connection to the server, so exit out if (pa_ready == 2) { ppa_context_disconnect(pa_ctx); ppa_context_unref(pa_ctx); ppa_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 = ppa_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 (ppa_operation_get_state(pa_op) == PA_OPERATION_DONE) { ppa_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 = ppa_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 (ppa_operation_get_state(pa_op) == PA_OPERATION_DONE) { // Now we're done, clean up and disconnect and return ppa_operation_unref(pa_op); ppa_context_disconnect(pa_ctx); ppa_context_unref(pa_ctx); ppa_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. ppa_mainloop_iterate(pa_ml, 1, NULL); } } int listpulse() { int ctr; PlaybackCount = 0; CaptureCount = 0; // 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]; if (pa_get_devicelist(pa_input_devicelist, pa_output_devicelist) < 0) { fprintf(stderr, "failed to get device list\n"); return 1; } printf("Pulse Playback Devices\n\n"); for (ctr = 0; ctr < 16; ctr++) { if (!pa_output_devicelist[ctr].initialized) break; printf("Name: %s\n", pa_output_devicelist[ctr].name); strcpy(&PlaybackNames[PlaybackCount++][0], pa_output_devicelist[ctr].name); } printf("Pulse Capture Devices\n\n"); for (ctr = 0; ctr < 16; ctr++) { if (!pa_input_devicelist[ctr].initialized) break; printf("Name: %s\n", pa_input_devicelist[ctr].name); strcpy(&CaptureNames[CaptureCount++][0], pa_input_devicelist[ctr].name); } return 0; } pa_simple * OpenPulsePlayback(char * Server) { pa_simple * s; pa_sample_spec ss; ss.format = PA_SAMPLE_S16NE; ss.channels = 2; ss.rate = 12000; int error; s = (*ppa_simple_new)(NULL, // Use the default server. "QtSM", // Our application's name. PA_STREAM_PLAYBACK, Server, "Playback", // Description of our stream. &ss, // Our sample format. NULL, // Use default channel map NULL, // Use default buffering attributes. &error ); if (s == 0) printf("Playback pa_simple_new() failed: %s\n", ppa_strerror(error)); else printf("Playback Handle %x\n", (unsigned int)s); return s; } pa_simple * OpenPulseCapture(char * Server) { pa_simple * s; pa_sample_spec ss; ss.format = PA_SAMPLE_S16NE; ss.channels = 2; ss.rate = 12000; int error; pa_buffer_attr attr; attr.maxlength = -1; attr.tlength = -1; attr.prebuf = -1; attr.minreq = -1; attr.fragsize = 512; s = (*ppa_simple_new)(NULL, // Use the default server. "QtSM", // Our application's name. PA_STREAM_RECORD, Server, "Capture", // Description of our stream. &ss, // Our sample format. NULL, // Use default channel map &attr, &error ); if (s == 0) printf("Capture pa_simple_new() failed: %s\n", ppa_strerror(error)); else printf("Capture Handle %x\n", (unsigned int)s); return s; } pa_simple * spc = 0; // Capure Handle pa_simple * spp = 0; // Playback Handle int pulse_audio_open(char * CaptureDevice, char * PlaybackDevice) { pa_usec_t latency; int error; spc = OpenPulseCapture(CaptureDevice); spp = OpenPulsePlayback(PlaybackDevice); if (spc && spp) { if ((latency = ppa_simple_get_latency(spc, &error)) == (pa_usec_t)-1) { printf("cap simple_get_latency() failed: %s\n", ppa_strerror(error)); } else printf("cap %0.0f usec \n", (float)latency); if ((latency = ppa_simple_get_latency(spp, &error)) == (pa_usec_t)-1) { printf("play simple_get_latency() failed: %s\n", ppa_strerror(error)); } else printf("play %0.0f usec \n", (float)latency); return 1; } else return 0; } void pulse_audio_close() { int error; ppa_simple_flush(spc, &error); ppa_simple_free(spc); spc = 0; ppa_simple_drain(spp, &error); ppa_simple_free(spp); spp = 0; } int pulse_read(short * samples, int nSamples) { int error; int nBytes = nSamples * 4; if (spc == 0) return 0; if (ppa_simple_read(spc, samples, nBytes, &error) < 0) { printf("Pulse pa_simple_read() failed: %s\n", ppa_strerror(error)); return 0; } return nSamples; } int pulse_write(short * ptr, int len) { int k; int error; if (spp == 0) return 0; k = ppa_simple_write(spp, ptr, len * 4, &error); if (k < 0) { printf("Pulse pa_simple_write() failed: %s\n", ppa_strerror(error)); return -1; } return 0; } void pulse_flush() { int error; if (spp == 0) return; if (ppa_simple_flush(spp, &error) < 0) printf("Pulse pa_simple_flush() failed: %s\n", ppa_strerror(error)); }