static void packet_test(unsigned npackets, size_t plength, pa_mainloop *ml, pa_pstream *p1, pa_pstream *p2) { pa_packet *packet = pa_packet_new(plength); unsigned i; unsigned psum = 0, totalsum = 0; uint8_t *pdata; size_t plen; pa_log_info("Sending %d packets of length %zd", npackets, plength); packets_received = 0; packets_checksum = 0; packets_length = plength; pa_pstream_set_receive_packet_callback(p2, packet_received, NULL); pdata = (uint8_t *) pa_packet_data(packet, &plen); for (i = 0; i < plen; i++) { pdata[i] = i; psum += pdata[i]; } for (i = 0; i < npackets; i++) { pa_pstream_send_packet(p1, packet, NULL); totalsum += psum; pa_mainloop_iterate(ml, 0, NULL); } while (packets_received < npackets) pa_mainloop_iterate(ml, 1, NULL); fail_unless(packets_checksum == totalsum); pa_log_debug("Correct checksum received (%d)", packets_checksum); pa_packet_unref(packet); }
static int pa_get_devicelist(AudioDeviceInfoList& input) { pa_mainloop *pa_ml; pa_mainloop_api *pa_mlapi; pa_operation *pa_op; pa_context *pa_ctx; int state = 0; int pa_ready = 0; pa_ml = pa_mainloop_new(); pa_mlapi = pa_mainloop_get_api(pa_ml); pa_ctx = pa_context_new(pa_mlapi, "USBqemu-devicelist"); pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); pa_context_set_state_callback(pa_ctx, pa_context_state_cb, &pa_ready); for (;;) { if (pa_ready == 0) { pa_mainloop_iterate(pa_ml, 1, NULL); continue; } // Connection failed if (pa_ready == 2) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return -1; } switch (state) { case 0: pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, &input); state++; break; case 1: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 0; } break; default: return -1; } pa_mainloop_iterate(pa_ml, 1, NULL); } }
void AudioDriverPulseAudio::detect_channels(bool capture) { pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map); String device = capture ? capture_device_name : device_name; if (device == "Default") { // Get the default output device name pa_status = 0; pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this); if (pa_op) { while (pa_status == 0) { int ret = pa_mainloop_iterate(pa_ml, 1, NULL); if (ret < 0) { ERR_PRINT("pa_mainloop_iterate error"); } } pa_operation_unref(pa_op); } else { ERR_PRINT("pa_context_get_server_info error"); } } char dev[1024]; if (device == "Default") { strcpy(dev, capture ? capture_default_device.utf8().get_data() : default_device.utf8().get_data()); } else { strcpy(dev, device.utf8().get_data()); } // Now using the device name get the amount of channels pa_status = 0; pa_operation *pa_op; if (capture) { pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this); } else { pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); } if (pa_op) { while (pa_status == 0) { int ret = pa_mainloop_iterate(pa_ml, 1, NULL); if (ret < 0) { ERR_PRINT("pa_mainloop_iterate error"); } } pa_operation_unref(pa_op); } else { if (capture) { ERR_PRINT("pa_context_get_source_info_by_name error"); } else { ERR_PRINT("pa_context_get_sink_info_by_name error"); } } }
int pa_get_devicelist(pa_devicelist_t *output) { pa_mainloop *pa_ml = NULL; pa_mainloop_api *pa_mlapi = NULL; pa_operation *pa_op = NULL; pa_context *pa_ctx = NULL; uint8_t state = 0; int pa_ready = 0; memset(output, 0, sizeof(pa_devicelist_t) * 16); if ( (pa_ml = pa_mainloop_new()) == NULL) return -1; if ( (pa_mlapi = pa_mainloop_get_api(pa_ml)) == NULL ) return -2; if ( (pa_ctx = pa_context_new(pa_mlapi, "test")) == NULL) return -3; pa_context_connect(pa_ctx, NULL, 0, NULL); pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); while (1) { if (pa_ready == 0) { pa_mainloop_iterate(pa_ml, 1, NULL); continue; } if (pa_ready == 2) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return -1; } switch (state) { case 0: pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, output); state++; break; case 1: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 0; } break; default: return -1; } pa_mainloop_iterate(pa_ml, 1, NULL); } }
int main(int argc, const char *argv[]) { pa_mainloop *pa_ml = NULL; pa_mainloop_api *pa_mlapi = NULL; pa_operation *pa_op = NULL; pa_context *pa_ctx = NULL; int pa_ready = 0; int state = 0; pa_ml = pa_mainloop_new(); pa_mlapi = pa_mainloop_get_api(pa_ml); pa_ctx = pa_context_new(pa_mlapi, "deepin"); pa_context_connect(pa_ctx, NULL, 0, NULL); pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); for (;;) { if (pa_ready == 0) { pa_mainloop_iterate(pa_ml, 1, NULL); continue; } if (pa_ready == 2) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return -1; } switch (state) { case 0: pa_op = pa_context_get_source_output_info_list(pa_ctx, pa_source_output_cb, NULL); state++; break; case 1: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 0; } break; default: fprintf(stderr, "in state %d\n", state); return -1; } pa_mainloop_iterate(pa_ml, 1, NULL); } return 0; }
bool PulseHandler::Init(void) { if (m_initialised) return m_valid; m_initialised = true; // Initialse our connection to the server m_loop = pa_mainloop_new(); if (!m_loop) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get PulseAudio mainloop"); return m_valid; } pa_mainloop_api *api = pa_mainloop_get_api(m_loop); if (!api) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get PulseAudio api"); return m_valid; } if (pa_signal_init(api) != 0) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise signaling"); return m_valid; } const char *client = "mythtv"; m_ctx = pa_context_new(api, client); if (!m_ctx) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create context"); return m_valid; } // remember which thread created this object for later sanity debugging m_thread = QThread::currentThread(); // we set the callback, connect and then run the main loop 'by hand' // until we've successfully connected (or not) pa_context_set_state_callback(m_ctx, StatusCallback, this); pa_context_connect(m_ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); int ret = 0; int tries = 0; while ((tries++ < 100) && !IS_READY(m_ctx_state)) { pa_mainloop_iterate(m_loop, 0, &ret); usleep(10000); } if (PA_CONTEXT_READY != m_ctx_state) { LOG(VB_GENERAL, LOG_ERR, LOC + "Context not ready after 1000ms"); return m_valid; } LOG(VB_AUDIO, LOG_INFO, LOC + "Initialised handler"); m_valid = true; return m_valid; }
QList<DeviceFinder::Device> PulseDeviceFinder::ListDevices() { if (!context_ || pa_context_get_state(context_) != PA_CONTEXT_READY) { return QList<Device>(); } retry: ListDevicesState state; pa_context_get_sink_info_list( context_, &PulseDeviceFinder::GetSinkInfoCallback, &state); forever { if (state.finished) { return state.devices; } switch (pa_context_get_state(context_)) { case PA_CONTEXT_READY: break; case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: // Maybe pulseaudio died. Try reconnecting. if (Reconnect()) { goto retry; } return state.devices; default: return state.devices; } pa_mainloop_iterate(mainloop_, true, nullptr); } }
/* * Initialize pulseAudio */ static int init_pa(recorder_context_t *rctx) { pa_mainloop_api *pa_mlapi; pa_proplist *ctx_properties = pa_proplist_new(); pa_proplist *stream_properties = pa_proplist_new(); int retval = 0; rctx->pa_ml = pa_mainloop_new(); pa_mlapi = pa_mainloop_get_api(rctx->pa_ml); rctx->pa_ctx = pa_context_new_with_proplist(pa_mlapi, "NoApp recorder", ctx_properties); pa_context_connect(rctx->pa_ctx, NULL, 0, NULL); rctx->pa_ready = 0; pa_context_set_state_callback(rctx->pa_ctx, pa_state_cb, rctx); while (rctx->pa_ready == 0){ pa_mainloop_iterate(rctx->pa_ml, 1, NULL); } if (rctx->pa_ready == 2){ retval = -1; goto exit; } rctx->recording_stream = pa_stream_new_with_proplist(rctx->pa_ctx, "NoApp recorder", &rctx->pa_ss, NULL, stream_properties); retval = pa_stream_connect_record(rctx->recording_stream, NULL, NULL, 0); if (retval < 0){ Log(LOG_ERR, "pa_stream_connect_playback failed\n"); goto exit; } exit: return retval; }
bool PulseDeviceFinder::Reconnect() { if (context_) { pa_context_disconnect(context_); pa_context_unref(context_); } context_ = pa_context_new(pa_mainloop_get_api(mainloop_), "Clementine device finder"); if (!context_) { qLog(Warning) << "Failed to create pulseaudio context"; return false; } if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) { qLog(Warning) << "Failed to connect pulseaudio context"; return false; } // Wait for the context to be connected. forever { const pa_context_state state = pa_context_get_state(context_); if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) { qLog(Warning) << "Connection to pulseaudio failed"; return false; } if (state == PA_CONTEXT_READY) { return true; } pa_mainloop_iterate(mainloop_, true, nullptr); } }
Error AudioDriverPulseAudio::init() { active = false; thread_exited = false; exit_thread = false; mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); pa_ml = pa_mainloop_new(); ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN); pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot"); ERR_FAIL_COND_V(pa_ctx == NULL, ERR_CANT_OPEN); pa_ready = 0; pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this); int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); if (ret < 0) { if (pa_ctx) { pa_context_unref(pa_ctx); pa_ctx = NULL; } if (pa_ml) { pa_mainloop_free(pa_ml); pa_ml = NULL; } return ERR_CANT_OPEN; } while (pa_ready == 0) { pa_mainloop_iterate(pa_ml, 1, NULL); } if (pa_ready < 0) { if (pa_ctx) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_ctx = NULL; } if (pa_ml) { pa_mainloop_free(pa_ml); pa_ml = NULL; } return ERR_CANT_OPEN; } Error err = init_device(); if (err == OK) { mutex = Mutex::create(); thread = Thread::create(AudioDriverPulseAudio::thread_func, this); } return OK; }
/** Wait until the specified operation completes */ static void wait_for_operation(struct pa_operation *o) { assert(o && context && mainloop); while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) pa_mainloop_iterate(mainloop, 1, NULL); pa_operation_unref(o); }
void* PulseAudio::paLoop(void* data) { PulseAudio* pa = (PulseAudio*)data; pa->runState = 2; while (pa->runState == 2) pa_mainloop_iterate(pa->pa_ml, 1, NULL); pa->runState = 0; return 0; }
bool PulseAudio::init(bool) { pa_ml = pa_mainloop_new(); pa_mainloop_api* pa_mlapi = pa_mainloop_get_api(pa_ml); pa_context* pa_ctx = pa_context_new(pa_mlapi, "MuseScore"); if (pa_context_connect(pa_ctx, NULL, pa_context_flags_t(0), NULL) != 0) qDebug("PulseAudio Context Connect Failed with Error: %s", pa_strerror(pa_context_errno(pa_ctx))); int pa_ready = 0; pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); while (pa_ready == 0) pa_mainloop_iterate(pa_ml, 1, NULL); if (pa_ready == 2) return false; ss.rate = _sampleRate; ss.channels = 2; ss.format = PA_SAMPLE_FLOAT32LE; pa_stream* playstream = pa_stream_new(pa_ctx, "Playback", &ss, NULL); if (!playstream) { qDebug("pa_stream_new failed"); return false; } pa_stream_set_write_callback(playstream, paCallback, this); bufattr.fragsize = (uint32_t)-1; bufattr.maxlength = FRAMES * 2 * sizeof(float); bufattr.minreq = FRAMES * 1 * sizeof(float); // pa_usec_to_bytes(0, &ss); bufattr.prebuf = (uint32_t)-1; bufattr.tlength = bufattr.maxlength; int r = pa_stream_connect_playback(playstream, NULL, &bufattr, pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL); if (r < 0) { // Old pulse audio servers don't like the ADJUST_LATENCY flag, so retry without that r = pa_stream_connect_playback(playstream, NULL, &bufattr, pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL); } if (r < 0) { qDebug("pa_stream_connect_playback failed"); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); pa_ml = 0; return false; } return true; }
bool PulseHandler::SuspendInternal(bool suspend) { // set everything up... if (!Init()) return false; // just in case it all goes pete tong if (!is_current_thread(m_thread)) LOG(VB_AUDIO, LOG_WARNING, LOC + "PulseHandler called from a different thread"); QString action = suspend ? "suspend" : "resume"; // don't bother to suspend a networked server if (!pa_context_is_local(m_ctx)) { LOG(VB_GENERAL, LOG_ERR, LOC + "PulseAudio server is remote. No need to " + action); return false; } // create and dispatch 2 operations to suspend or resume all current sinks // and all current sources m_pending_operations = 2; pa_operation *operation_sink = pa_context_suspend_sink_by_index( m_ctx, PA_INVALID_INDEX, suspend, OperationCallback, this); pa_operation_unref(operation_sink); pa_operation *operation_source = pa_context_suspend_source_by_index( m_ctx, PA_INVALID_INDEX, suspend, OperationCallback, this); pa_operation_unref(operation_source); // run the loop manually and wait for the callbacks int count = 0; int ret = 0; while (m_pending_operations && count++ < 100) { pa_mainloop_iterate(m_loop, 0, &ret); usleep(10000); } // a failure isn't necessarily disastrous if (m_pending_operations) { m_pending_operations = 0; LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to " + action); return false; } // rejoice LOG(VB_GENERAL, LOG_INFO, LOC + "PulseAudio " + action + " OK"); return true; }
bool PulseAudio::PulseInit() { m_pa_error = 0; m_pa_connected = 0; // create pulseaudio main loop and context // also register the async state callback which is called when the connection to the pa server has changed m_pa_ml = pa_mainloop_new(); m_pa_mlapi = pa_mainloop_get_api(m_pa_ml); m_pa_ctx = pa_context_new(m_pa_mlapi, "dolphin-emu"); m_pa_error = pa_context_connect(m_pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr); pa_context_set_state_callback(m_pa_ctx, StateCallback, this); // wait until we're connected to the pulseaudio server while (m_pa_connected == 0 && m_pa_error >= 0) m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr); if (m_pa_connected == 2 || m_pa_error < 0) { ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error)); return false; } // create a new audio stream with our sample format // also connect the callbacks for this stream pa_sample_spec ss; ss.format = PA_SAMPLE_S16LE; ss.channels = 2; ss.rate = m_mixer->GetSampleRate(); m_pa_s = pa_stream_new(m_pa_ctx, "Playback", &ss, nullptr); pa_stream_set_write_callback(m_pa_s, WriteCallback, this); pa_stream_set_underflow_callback(m_pa_s, UnderflowCallback, this); // connect this audio stream to the default audio playback // limit buffersize to reduce latency m_pa_ba.fragsize = -1; m_pa_ba.maxlength = -1; // max buffer, so also max latency m_pa_ba.minreq = -1; // don't read every byte, try to group them _a bit_ m_pa_ba.prebuf = -1; // start as early as possible m_pa_ba.tlength = BUFFER_SIZE; // designed latency, only change this flag for low latency output pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); m_pa_error = pa_stream_connect_playback(m_pa_s, nullptr, &m_pa_ba, flags, nullptr, nullptr); if (m_pa_error < 0) { ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error)); return false; } INFO_LOG(AUDIO, "Pulse successfully initialized"); return true; }
void m1sdr_PlayStop(void) { #if 0 pa_operation *op; #endif #ifdef USE_SDL if (lnxdrv_apimode == 0) { SDL_PauseAudio(1); } #endif if (lnxdrv_apimode == 1) { // snd_pcm_pause(pHandle, 1); snd_pcm_drop(pHandle); } #if PULSE_USE_SIMPLE if ((lnxdrv_apimode == 3) && (my_simple)) { pa_simple_flush(my_simple, NULL); pa_simple_free(my_simple); my_simple = NULL; } #else #if 0 if (lnxdrv_apimode == 3) { op = pa_stream_drain(my_pa_stream, &pa_stream_drain_complete, NULL); if (op) { while (pa_operation_get_state(op) != PA_OPERATION_DONE) { if (pa_context_get_state(my_pa_context) != PA_CONTEXT_READY || pa_stream_get_state(my_pa_stream) != PA_STREAM_READY || pa_mainloop_iterate(my_pa_mainloop, 0, NULL) < 0) { pa_operation_cancel(op); break; } } } } #endif #endif waveLogStop(); oss_playing = 0; }
int start_recording(recorder_context_t *rctx) { int retval = 0; time_t current_time; Log(LOG_INFO, "Starting recorder.\n"); if ((retval = init_filenames(rctx)) < 0){ Log(LOG_ERR, "Failed in the initialization of the recording file.\n"); goto exit; } Log(LOG_INFO, "Filenames created.\n"); if ((retval = init_pa(rctx)) < 0){ Log(LOG_ERR, "Failed in the initializaion of pulse audio.\n"); goto exit; } Log(LOG_INFO, "PulseAudio connected.\n"); Log(LOG_INFO, "Calibrating threshold.\n"); printf("***** ATTENTION *****\n"); printf("Keep quiet for the next %d seconds please.\n", QUIET_TIME); pa_stream_set_read_callback(rctx->recording_stream, detect_threshold_cb, rctx); rctx->timestamp = time(NULL); current_time = rctx->timestamp; #ifdef DEBUG threshold_file = fopen("/tmp/threshold.pcm", "wb"); #endif while (difftime(current_time, rctx->timestamp) < QUIET_TIME){ pa_mainloop_iterate(rctx->pa_ml, 0, &retval); current_time = time(NULL); if (retval < 0){ Log(LOG_ERR, "There was a problem calculating the threshold power.\n"); goto exit; } } Log(LOG_DEBUG, "Threshold: %f\n", rctx->threshold); Log(LOG_INFO, "Entering into the mainloop.\n"); printf("\nNow you can start talking.\n"); rctx->timestamp = time(NULL); pa_stream_set_read_callback(rctx->recording_stream, stream_request_cb, rctx); pa_mainloop_run(rctx->pa_ml, &retval); exit: stop_recording(rctx, false); return retval; }
void *kradpulse_loop_thread(void *arg) { krad_pulse_t *kradpulse = (krad_pulse_t *)arg; // 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. while (kradpulse->shutdown == 0) { pa_mainloop_iterate(kradpulse->pa_ml, 1, NULL); } return NULL; }
// Called on audio thread. void PulseAudio::SoundLoop() { Common::SetCurrentThreadName("Audio thread - pulse"); if (PulseInit()) { while (m_run_thread.load() && m_pa_connected == 1 && m_pa_error >= 0) m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr); if (m_pa_error < 0) ERROR_LOG(AUDIO, "PulseAudio error: %s", pa_strerror(m_pa_error)); PulseShutdown(); } }
bool PulseAudio::init(bool) { pa_ml = pa_mainloop_new(); pa_mainloop_api* pa_mlapi = pa_mainloop_get_api(pa_ml); pa_context* pa_ctx = pa_context_new(pa_mlapi, "MuseScore"); if (pa_context_connect(pa_ctx, NULL, pa_context_flags_t(0), NULL) != 0) { qDebug("PulseAudio Context Connect Failed with Error: %s", pa_strerror(pa_context_errno(pa_ctx))); return false; } int pa_ready = 0; pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); while (pa_ready == 0) pa_mainloop_iterate(pa_ml, 1, NULL); if (pa_ready == 2) return false; ss.rate = _sampleRate; ss.channels = 2; ss.format = PA_SAMPLE_FLOAT32LE; pa_stream* playstream = pa_stream_new(pa_ctx, "Playback", &ss, NULL); if (!playstream) { qDebug("pa_stream_new failed: %s", pa_strerror(pa_context_errno(pa_ctx))); return false; } pa_stream_set_write_callback(playstream, paCallback, this); bufattr.fragsize = (uint32_t)-1; bufattr.maxlength = FRAMES * 2 * sizeof(float); bufattr.minreq = FRAMES * 1 * sizeof(float); // pa_usec_to_bytes(0, &ss); bufattr.prebuf = (uint32_t)-1; bufattr.tlength = bufattr.maxlength; int r = pa_stream_connect_playback(playstream, nullptr, &bufattr, PA_STREAM_NOFLAGS, nullptr, nullptr); if (r < 0) { qDebug("pa_stream_connect_playback failed"); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); pa_ml = 0; return false; } return true; }
APULSE_EXPORT int pa_mainloop_run(pa_mainloop *m, int *retval) { trace_info("F %s\n", __func__); int ret; do { ret = pa_mainloop_iterate(m, 1, retval); } while (ret >= 0 && !m->terminate); if (m->terminate) { *retval = m->retval; return 0; } return ret; }
int pulse_init(struct pulseaudio_t *pulse) { enum pa_context_state state = PA_CONTEXT_CONNECTING; pulse->mainloop = pa_mainloop_new(); pulse->cxt = pa_context_new(pa_mainloop_get_api(pulse->mainloop), "pavol"); pulse->default_sink = NULL; pulse->muted = 0; pulse->volume = 0; pa_context_set_state_callback(pulse->cxt, pulse_connect_state_cb, &state); pa_context_connect(pulse->cxt, NULL, PA_CONTEXT_NOFLAGS, NULL); while (state != PA_CONTEXT_READY && state != PA_CONTEXT_FAILED) pa_mainloop_iterate(pulse->mainloop, 1, NULL); if (state != PA_CONTEXT_READY) { fprintf(stderr, "failed to connect to pulse daemon: %s\n", pa_strerror(pa_context_errno(pulse->cxt))); return 1; } return 0; }
/* The implementation of create_output_device_name_list() is based on the * example code in * http://www.ypass.net/blog/2009/10/ * pulseaudio-an-async-example-to-get-device-lists/ */ mbx_error_code mbx_create_output_device_name_list(char ***dev_names, size_t *n_devs) { pa_mainloop *pa_ml = pa_mainloop_new(); pa_mainloop_api *pa_mlapi = pa_mainloop_get_api(pa_ml); pa_context *pa_ctx = pa_context_new(pa_mlapi, "music box (listing output " "devices)"); pa_operation *pa_op; pa_context_state_t pa_context_state = PA_CONTEXT_UNCONNECTED; int do_iterate = 1; int error = 0; pa_context_connect(pa_ctx, NULL, 0, NULL); /* The state callback will update the state when we are connected to the * PulseAudio server, or if an error occurs. */ pa_context_set_state_callback(pa_ctx, pa_context_state_cb, &pa_context_state); while ( do_iterate ) { switch ( pa_context_state ) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: pa_mainloop_iterate(pa_ml, 1, NULL); // we must wait. break; case PA_CONTEXT_READY: do_iterate = 0; break; case PA_CONTEXT_FAILED: mbx_log_error(MBX_LOG_AUDIO_OUTPUT, "Connection to PulseAudio server failed: " "%s", pa_strerror(pa_context_errno(pa_ctx))); error = 1; break; case PA_CONTEXT_TERMINATED: mbx_log_error(MBX_LOG_AUDIO_OUTPUT, "Connection to PulseAudio server " "terminated unexpectedly."); error = 1; break; default: mbx_log_error(MBX_LOG_AUDIO_OUTPUT, "The PulseAudio context has an unexpected " "state: %d", pa_context_state); error = 1; break; } if ( error ) { do_iterate = 0; } } if ( ! error ) { struct list_of_strings result = { NULL, 0, 0 }; pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, &result); while ( pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING ) { pa_mainloop_iterate(pa_ml, 1, NULL); // wait. } pa_operation_unref(pa_op); *dev_names = result.strings; *n_devs = result.n_strings; } pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return error ? MBX_PULSEAUDIO_ERROR : MBX_SUCCESS; }
int set_active_port(pa_devicelist_t device, pa_portlist_t port) { pa_mainloop *pa_ml; pa_mainloop_api *pa_mlapi; pa_operation *pa_op; pa_context *pa_ctx; int pa_ready = 0; int state = 0; pa_ml = pa_mainloop_new(); pa_mlapi = pa_mainloop_get_api(pa_ml); pa_ctx = pa_context_new(pa_mlapi, "test"); pa_context_connect(pa_ctx, NULL, 0, NULL); pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); pa_device_port_t dev_port_set; dev_port_set.device = device; dev_port_set.port = port; pa_clientlist_t clientlist[30]; int i = 0; 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; } switch(state) { case 0: // Set source or sink switch(device.type) { case JOAPA_SOURCE: pa_op = pa_context_set_source_port_by_index( pa_ctx, device.index, port.name, set_active_port_cb, &dev_port_set); break; case JOAPA_SINK: pa_op = pa_context_set_sink_port_by_index( pa_ctx, device.index, port.name, set_active_port_cb, &dev_port_set); break; } state++; break; case 1: // get clients using a source or sink if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); switch(device.type) { case JOAPA_SOURCE: pa_op = pa_context_get_source_output_info_list(pa_ctx, pa_source_info_cb, &clientlist); break; case JOAPA_SINK: pa_op = pa_context_get_sink_input_info_list(pa_ctx, pa_sink_info_cb, &clientlist); break; } state++; } break; case 2: // move the clients to the new source or sink if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); if(!clientlist[i].initialized) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 0; } //printf("CLIENT: %d\n", clientlist[i].index); switch(device.type) { case JOAPA_SOURCE: pa_op = pa_context_move_source_output_by_index( pa_ctx, clientlist[i].index, device.index, set_active_port_cb, NULL); break; case JOAPA_SINK: pa_op = pa_context_move_sink_input_by_index( pa_ctx, clientlist[i].index, device.index, set_active_port_cb, NULL); break; } i++; } break; default: fprintf(stderr, "in state %d\n", state); return -1; } pa_mainloop_iterate(pa_ml, 1, NULL); } }
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); } }
krad_pulse_t *kradpulse_create(krad_audio_t *kradaudio) { krad_pulse_t *kradpulse; if ((kradpulse = calloc (1, sizeof (krad_pulse_t))) == NULL) { fprintf(stderr, "mem alloc fail\n"); exit (1); } kradpulse->kradaudio = kradaudio; kradpulse->samples[0] = malloc(24 * 8192); kradpulse->samples[1] = malloc(24 * 8192); kradpulse->interleaved_samples = malloc(48 * 8192); kradpulse->capture_samples[0] = malloc(24 * 8192); kradpulse->capture_samples[1] = malloc(24 * 8192); kradpulse->capture_interleaved_samples = malloc(48 * 8192); kradpulse->latency = 20000; // start latency in micro seconds kradpulse->underflows = 0; kradpulse->pa_ready = 0; kradpulse->retval = 0; // Create a mainloop API and connection to the default server kradpulse->pa_ml = pa_mainloop_new(); kradpulse->pa_mlapi = pa_mainloop_get_api(kradpulse->pa_ml); kradpulse->pa_ctx = pa_context_new(kradpulse->pa_mlapi, kradpulse->kradaudio->name); pa_context_connect(kradpulse->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(kradpulse->pa_ctx, kradpulse_state_cb, kradpulse); // We can't do anything until PA is ready, so just iterate the mainloop // and continue while (kradpulse->pa_ready == 0) { pa_mainloop_iterate(kradpulse->pa_ml, 1, NULL); } if (kradpulse->pa_ready == 2) { kradpulse->retval = -1; printf("pulseaudio fail\n"); exit(1); } kradpulse->ss.rate = 44100; kradpulse->ss.channels = 2; kradpulse->ss.format = PA_SAMPLE_FLOAT32LE; kradpulse->bufattr.fragsize = (uint32_t)-1; kradpulse->bufattr.maxlength = pa_usec_to_bytes(kradpulse->latency, &kradpulse->ss); kradpulse->bufattr.minreq = pa_usec_to_bytes(0, &kradpulse->ss); kradpulse->bufattr.prebuf = (uint32_t)-1; kradpulse->bufattr.tlength = pa_usec_to_bytes(kradpulse->latency, &kradpulse->ss); if ((kradaudio->direction == KOUTPUT) || (kradaudio->direction == KDUPLEX)) { kradpulse->playstream = pa_stream_new(kradpulse->pa_ctx, "Playback", &kradpulse->ss, NULL); if (!kradpulse->playstream) { printf("playback pa_stream_new failed\n"); exit(1); } pa_stream_set_write_callback(kradpulse->playstream, kradpulse_playback_cb, kradpulse); pa_stream_set_underflow_callback(kradpulse->playstream, kradpulse_stream_underflow_cb, kradpulse); kradpulse->r = pa_stream_connect_playback(kradpulse->playstream, NULL, &kradpulse->bufattr, PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); if (kradpulse->r < 0) { printf("pa_stream_connect_playback failed\n"); kradpulse->retval = -1; printf("pulseaudio fail\n"); exit(1); } } if ((kradaudio->direction == KINPUT) || (kradaudio->direction == KDUPLEX)) { kradpulse->capturestream = pa_stream_new(kradpulse->pa_ctx, "Capture", &kradpulse->ss, NULL); if (!kradpulse->capturestream) { printf("capture pa_stream_new failed\n"); exit(1); } pa_stream_set_read_callback(kradpulse->capturestream, kradpulse_capture_cb, kradpulse); pa_stream_set_underflow_callback(kradpulse->capturestream, kradpulse_stream_underflow_cb, kradpulse); kradpulse->r = pa_stream_connect_record(kradpulse->capturestream, NULL, &kradpulse->bufattr, PA_STREAM_NOFLAGS ); if (kradpulse->r < 0) { printf("pa_stream_connect_capture failed\n"); kradpulse->retval = -1; printf("pulseaudio fail\n"); exit(1); } } kradaudio->sample_rate = kradpulse->ss.rate; pthread_create( &kradpulse->loop_thread, NULL, kradpulse_loop_thread, kradpulse); return kradpulse; }
int main(int argc, const char *argv[]) { pa_mainloop *pa_ml = NULL; pa_mainloop_api *pa_mlapi = NULL; pa_operation *pa_op = NULL; pa_context *pa_ctx = NULL; int pa_ready = 0; int state = 0; pa_ml = pa_mainloop_new(); pa_mlapi = pa_mainloop_get_api(pa_ml); pa_ctx = pa_context_new(pa_mlapi, "deepin"); pa_context_connect(pa_ctx, NULL, 0, NULL); pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); for (;;) { if (0 == pa_ready) { pa_mainloop_iterate(pa_ml, 1, NULL); continue; } if (2 == pa_ready) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return -1; } switch (state) { case 0: if (pa_context_get_server_protocol_version (pa_ctx) < 13) { return -1; } printf("server version: %d\n", pa_context_get_server_protocol_version(pa_ctx)); pa_stream *s = NULL; pa_proplist *proplist; pa_buffer_attr attr; pa_sample_spec ss; int res; char dev_name[40]; // pa_sample_spec ss.channels = 1; ss.format = PA_SAMPLE_FLOAT32; ss.rate = 25; // pa_buffer_attr memset(&attr, 0, sizeof(attr)); attr.fragsize = sizeof(float); attr.maxlength = (uint32_t) -1; // pa_proplist proplist = pa_proplist_new (); pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, "deepin.sound"); // create new stream if (!(s = pa_stream_new_with_proplist(pa_ctx, "Peak detect", &ss, NULL, proplist))) { fprintf(stderr, "pa_stream_new error\n"); return -2; } pa_proplist_free(proplist); /*pa_stream_set_monitor_stream(s, 26);*/ pa_stream_set_read_callback(s, on_monitor_read_callback, NULL); pa_stream_set_suspended_callback(s, on_monitor_suspended_callback, NULL); res = pa_stream_connect_record(s, NULL, &attr, (pa_stream_flags_t) (PA_STREAM_DONT_MOVE |PA_STREAM_PEAK_DETECT |PA_STREAM_ADJUST_LATENCY)); if (res < 0) { fprintf(stderr, "Failed to connect monitoring stream\n"); return -3; } state++; break; case 1: usleep(100); break; case 2: return 0; break; default: return -1; } pa_mainloop_iterate(pa_ml, 1, NULL); } return 0; }
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { // We'll need these state variables to keep track of our requests int pa_ready = 0; int pa_conn = 0; int fork_id = 0; // Create a mainloop API and connection to the default server pa_ptrs.pa_ss.format = PA_SAMPLE_S16NE; pa_ptrs.pa_ss.rate = 22050; pa_ptrs.pa_ss.channels = 1; pa_ptrs.pa_ml = pa_mainloop_new(); pa_ptrs.pa_mlapi = pa_mainloop_get_api(pa_ptrs.pa_ml); pa_ptrs.pa_ctx = pa_context_new(pa_ptrs.pa_mlapi, "test"); // This function connects to the pulse server pa_context_connect(pa_ptrs.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_ptrs.pa_ctx, pa_state_cb, &pa_ready); for (int i=0;pa_ready == 0 && i<1000;i++) { pa_mainloop_iterate(pa_ptrs.pa_ml, 1, NULL); } if (pa_ready == 2 || pa_ready == 0) { pa_context_disconnect(pa_ptrs.pa_ctx); pa_context_unref(pa_ptrs.pa_ctx); pa_mainloop_free(pa_ptrs.pa_ml); printf("Error"); return; } // At this point, we're connected to the server and ready to make // requests printf("Context connected to PA-daemon\n"); pa_ptrs.pa_s = pa_stream_new(pa_ptrs.pa_ctx,"FooStream",&pa_ptrs.pa_ss,NULL); pa_stream_connect_playback(pa_ptrs.pa_s, // The stream to connect to a sink NULL, // Name of the sink to connect to, or NULL for default NULL, // Buffering attributes, or NULL for default PA_STREAM_NOFLAGS, // Additional flags, or 0 for default NULL, // Initial volume, or NULL for default NULL // Synchronize this stream with the specified one, or NULL for a standalone stream ); pa_stream_set_state_callback(pa_ptrs.pa_s,pa_stream_cb,&pa_conn); /* Wait for connection or timeout */ for (int i=0;pa_conn == 0 && i<1000;i++) { pa_mainloop_iterate(pa_ptrs.pa_ml,1,NULL); } if (pa_conn == 2 || pa_conn == 0) { pa_stream_disconnect(pa_ptrs.pa_s); pa_context_disconnect(pa_ptrs.pa_ctx); pa_context_unref(pa_ptrs.pa_ctx); pa_mainloop_free(pa_ptrs.pa_ml); printf("Error"); return; } mexPrintf("Stream connected to PA-daemon\n"); plhs[0]=mxCreateNumericMatrix(1,1,mxUINT64_CLASS,mxREAL); uint64_t *ptr = (uint64_t)mxGetData(plhs[0]); *ptr = &pa_ptrs; }
int main(int argc, char *const *argv) { static const char *context_name = "i3blocks-pulse-volume"; static const char *usage_str = "Usage: %s [-h] [-d] [-s INDEX] [-m FUNC]\n" "Options:\n" " -s INDEX: pulseaudio sink index on which to wait for " "changes (default: 0)\n" " -m FUNC : function used to compute the displayed volume value\n" " in there are multiple channels (eg. left/right):\n" " * avg: use average volume of all channels (default)\n" " * min: use minimum volume of all channels\n" " * max: use maximum volume of all channels\n" " -d : use decibel notation instead of 0-100 percentage; " "the sink may\n" " not support this feature\n"; options_t options; options.calculator = pa_cvolume_avg; options.use_decibel = 0; options.observed_index = 0; sink_info_data_t sink_info_data; sink_info_data.sink_ready = 0; sink_info_data.sink_changed = 0; pa_mainloop *pa_ml = NULL; pa_mainloop_api *pa_ml_api = NULL; pa_operation *pa_op = NULL; pa_context *pa_ctx = NULL; enum state_t state = FIRST_SINK_INFO; int pa_ready = CONN_WAIT; int opt; while ((opt = getopt(argc, argv, "m:s:dh")) != -1) { if (opt == 'm') { if (strcmp(optarg, "min") == 0) { options.calculator = pa_cvolume_min; } else if (strcmp(optarg, "max") == 0) { options.calculator = pa_cvolume_max; } else if (strcmp(optarg, "avg") == 0) { options.calculator = pa_cvolume_avg; } else { fprintf(stderr, usage_str, argv[0]); return 1; } } else if (opt == 's') { // Parse observed sink index errno = 0; char *endptr; uint32_t *oind = &options.observed_index; *oind = strtoul(optarg, &endptr, 10); if ((errno == ERANGE) || (errno != 0 && *oind == 0) || endptr == optarg) { fprintf(stderr, "%s: invalid sink index: %s\n", argv[0], optarg); fprintf(stderr, usage_str, argv[0]); return 1; } } else if (opt == 'd') { options.use_decibel = 1; } else if (opt == 'h') { fprintf(stderr, usage_str, argv[0]); return 0; } else { fprintf(stderr, usage_str, argv[0]); return 1; } } // Needed to filter out sink in callbacks sink_info_data.observed_index = options.observed_index; if ((pa_ml = pa_mainloop_new()) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio mainloop.\n"); return 2; } if ((pa_ml_api = pa_mainloop_get_api(pa_ml)) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio mainloop API.\n"); return 3; } if ((pa_ctx = pa_context_new(pa_ml_api, context_name)) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio context.\n"); return 4; } if (pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFAIL, NULL) < 0) { fprintf(stderr, "error: failed to connect to pulseaudio context: %s\n", pa_strerror(pa_context_errno(pa_ctx))); return 5; } pa_context_set_state_callback(pa_ctx, state_cb, &pa_ready); pa_context_set_subscribe_callback(pa_ctx, subscribe_cb, &sink_info_data); while (1) { if (pa_ready == CONN_WAIT) { pa_mainloop_iterate(pa_ml, 1, NULL); continue; } if (pa_ready == CONN_FAILED) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); fprintf(stderr, "error: failed to connect to pulseaudio context.\n"); return 6; } // Main loop automaton switch (state) { case FIRST_SINK_INFO: // First run pa_op = pa_context_get_sink_info_by_index(pa_ctx, options.observed_index, sink_info_cb, &sink_info_data); state = SUBSCRIBE; break; case SUBSCRIBE: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); if (!sink_info_data.sink_ready) { fprintf(stderr, "error: sink %u does not exist.\n", options.observed_index); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 7; } if (options.use_decibel && !sink_info_data.sink.can_decibel) { fprintf(stderr, "error: sink %u does not support decibel; " "try without `-d`.\n", options.observed_index); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 8; } // Show volume once at start show_volume(&sink_info_data.sink, &options); // Subsequent runs: wait for changes pa_op = pa_context_subscribe(pa_ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, &sink_info_data); state = SUBSCRIBED; } break; case SUBSCRIBED: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); state = WAIT_FOR_CHANGE; } break; case WAIT_FOR_CHANGE: if (sink_info_data.sink_changed) { sink_info_data.sink_changed = 0; pa_op = pa_context_get_sink_info_by_index(pa_ctx, options.observed_index, sink_info_cb, &sink_info_data); state = SHOW_CHANGE; } break; case SHOW_CHANGE: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); show_volume(&sink_info_data.sink, &options); state = WAIT_FOR_CHANGE; } break; default: return 7; } pa_mainloop_iterate(pa_ml, 1, NULL); } }
static void thread_func(void *userdata) { struct userdata *u = userdata; pa_proplist *proplist; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(u->thread_mq); proplist = tunnel_new_proplist(u); u->context = pa_context_new_with_proplist(u->thread_mainloop_api, "PulseAudio", proplist); pa_proplist_free(proplist); if (!u->context) { pa_log("Failed to create libpulse context"); goto fail; } if (u->cookie_file && pa_context_load_cookie_from_file(u->context, u->cookie_file) != 0) { pa_log_error("Can not load cookie file!"); goto fail; } pa_context_set_state_callback(u->context, context_state_cb, u); if (pa_context_connect(u->context, u->remote_server, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { pa_log("Failed to connect libpulse context"); goto fail; } for (;;) { int ret; if (pa_mainloop_iterate(u->thread_mainloop, 1, &ret) < 0) { if (ret == 0) goto finish; else goto fail; } if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); if (u->connected && pa_stream_get_state(u->stream) == PA_STREAM_READY && PA_SINK_IS_LINKED(u->sink->thread_info.state)) { size_t writable; writable = pa_stream_writable_size(u->stream); if (writable > 0) { if(u->transcode.encoding != -1) { pa_memchunk memchunk; const void *p; size_t nbBytes; unsigned char *cbits; pa_sink_render_full(u->sink, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size, &memchunk); pa_assert(memchunk.length > 0); pa_assert(memchunk.length >= u->transcode.frame_size*u->transcode.channels); pa_log_debug("received memchunk length: %zu bytes", memchunk.length ); /* we have new data to write */ p = pa_memblock_acquire(memchunk.memblock); nbBytes = pa_transcode_encode(&u->transcode, (uint8_t*) p + memchunk.index, &cbits); pa_log_debug("encoded length: %zu bytes", nbBytes); /* TODO: Use pa_stream_begin_write() to reduce copying. */ ret = pa_stream_write_compressed(u->stream, (uint8_t*) cbits, nbBytes, NULL, /**< A cleanup routine for the data or NULL to request an internal copy */ 0, /** offset */ PA_SEEK_RELATIVE, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size); pa_memblock_release(memchunk.memblock); pa_memblock_unref(memchunk.memblock); if(nbBytes > 0) free(cbits); if (ret != 0) { pa_log_error("Could not write data into the stream ... ret = %i", ret); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); } } else { pa_memchunk memchunk; const void *p; pa_sink_render_full(u->sink, writable, &memchunk); pa_assert(memchunk.length > 0); /* we have new data to write */ p = pa_memblock_acquire(memchunk.memblock); /* TODO: Use pa_stream_begin_write() to reduce copying. */ ret = pa_stream_write(u->stream, (uint8_t*) p + memchunk.index, memchunk.length, NULL, /**< A cleanup routine for the data or NULL to request an internal copy */ 0, /** offset */ PA_SEEK_RELATIVE); pa_memblock_release(memchunk.memblock); pa_memblock_unref(memchunk.memblock); if (ret != 0) { pa_log_error("Could not write data into the stream ... ret = %i", ret); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); } } } } } fail: pa_asyncmsgq_post(u->thread_mq->outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq->inq, PA_MESSAGE_SHUTDOWN); finish: if (u->stream) { pa_stream_disconnect(u->stream); pa_stream_unref(u->stream); u->stream = NULL; } if (u->context) { pa_context_disconnect(u->context); pa_context_unref(u->context); u->context = NULL; } pa_log_debug("Thread shutting down"); }