pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { pa_assert(m); pa_channel_map_init(m); m->channels = 1; m->map[0] = PA_CHANNEL_POSITION_MONO; return m; }
SinkInfoStruct() { list = NULL; isHWDevice = false; device_found = true; mainloop = NULL; samplerate = 0; pa_channel_map_init(&map); }
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { pa_assert(m); pa_channel_map_init(m); m->channels = 2; m->map[0] = PA_CHANNEL_POSITION_LEFT; m->map[1] = PA_CHANNEL_POSITION_RIGHT; return m; }
static pa_channel_map * xmms_pulse_backend_default_channel_map (pa_channel_map *m, int channels) { assert(m); assert(channels > 0); assert(channels <= PA_CHANNELS_MAX); pa_channel_map_init(m); m->channels = (uint8_t) channels; switch (channels) { case 4: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; return m; case 5: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; return m; case 7: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_LFE; m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; m->map[6] = PA_CHANNEL_POSITION_REAR_CENTER; return m; case 8: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_LFE; m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; return m; default: return pa_channel_map_init_auto (m, channels, PA_CHANNEL_MAP_WAVEEX); } }
static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { pa_scache_entry *e; pa_assert(c); pa_assert(name); if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) { if (e->memchunk.memblock) pa_memblock_unref(e->memchunk.memblock); pa_xfree(e->filename); pa_proplist_clear(e->proplist); pa_assert(e->core == c); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); } else { e = pa_xnew(pa_scache_entry, 1); if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, TRUE)) { pa_xfree(e); return NULL; } e->name = pa_xstrdup(name); e->core = c; e->proplist = pa_proplist_new(); pa_idxset_put(c->scache, e, &e->index); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index); } e->last_used_time = 0; pa_memchunk_reset(&e->memchunk); e->filename = NULL; e->lazy = FALSE; e->last_used_time = 0; pa_sample_spec_init(&e->sample_spec); pa_channel_map_init(&e->channel_map); pa_cvolume_init(&e->volume); e->volume_is_set = FALSE; pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event"); return e; }
static pa_channel_map AEChannelMapToPAChannel(const CAEChannelInfo& info) { pa_channel_map map; pa_channel_map_init(&map); pa_channel_position_t pos; for (unsigned int i = 0; i < info.Count(); ++i) { pos = AEChannelToPAChannel(info[i]); if(pos != PA_CHANNEL_POSITION_INVALID) { // remember channel name and increase channel count map.map[map.channels++] = pos; } } return map; }
/* For PCM streams */ int pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { char *sf = NULL, *m = NULL; int rate, channels; int ret = -PA_ERR_INVALID; pa_assert(f); pa_assert(ss); if (!pa_format_info_is_pcm(f)) return pa_format_info_to_sample_spec_fake(f, ss); if (pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf)) goto out; if (pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate)) goto out; if (pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels)) goto out; if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID) goto out; ss->rate = (uint32_t) rate; ss->channels = (uint8_t) channels; if (map) { pa_channel_map_init(map); if (pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m) == 0) if (pa_channel_map_parse(map, m) == NULL) goto out; } ret = 0; out: if (sf) pa_xfree(sf); if (m) pa_xfree(m); return ret; }
/* For PCM streams */ pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { char *sf = NULL, *m = NULL; int rate, channels; pa_bool_t ret = FALSE; pa_assert(f); pa_assert(ss); pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE); if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf)) goto out; if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate)) goto out; if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels)) goto out; if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID) goto out; ss->rate = (uint32_t) rate; ss->channels = (uint8_t) channels; if (map) { pa_channel_map_init(map); if (pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m)) if (pa_channel_map_parse(map, m) == NULL) goto out; } ret = TRUE; out: if (sf) pa_xfree(sf); if (m) pa_xfree(m); return ret; }
pa_channel_map * gst_pulse_gst_to_channel_map (pa_channel_map * map, const GstAudioRingBufferSpec * spec) { gint i, j; gint channels; const GstAudioChannelPosition *pos; pa_channel_map_init (map); channels = GST_AUDIO_INFO_CHANNELS (&spec->info); pos = spec->info.position; for (j = 0; j < channels; j++) { for (i = 0; i < G_N_ELEMENTS (gst_pa_pos_table); i++) { if (pos[j] == gst_pa_pos_table[i].gst_pos) { map->map[j] = gst_pa_pos_table[i].pa_pos; break; } } if (i == G_N_ELEMENTS (gst_pa_pos_table)) return NULL; } if (j != spec->info.channels) { return NULL; } map->channels = spec->info.channels; if (!pa_channel_map_valid (map)) { return NULL; } return map; }
FXbool PulseOutput::configure(const AudioFormat & fmt){ const pa_sample_spec * config=nullptr; pa_operation *operation=nullptr; if (!open()) return false; if (stream && fmt==af) return true; if (stream) { pa_stream_disconnect(stream); pa_stream_unref(stream); stream=nullptr; } pa_sample_spec spec; pa_channel_map cmap; if (!to_pulse_format(fmt,spec.format)) goto failed; spec.rate = fmt.rate; spec.channels = fmt.channels; // setup channel map pa_channel_map_init(&cmap); cmap.channels = fmt.channels; for (FXint i=0;i<fmt.channels;i++) { switch(fmt.channeltype(i)) { case Channel::None : cmap.map[i] = PA_CHANNEL_POSITION_INVALID; break; case Channel::Mono : cmap.map[i] = PA_CHANNEL_POSITION_MONO; break; case Channel::FrontLeft : cmap.map[i] = PA_CHANNEL_POSITION_FRONT_LEFT; break; case Channel::FrontRight : cmap.map[i] = PA_CHANNEL_POSITION_FRONT_RIGHT; break; case Channel::FrontCenter : cmap.map[i] = PA_CHANNEL_POSITION_FRONT_CENTER; break; case Channel::BackLeft : cmap.map[i] = PA_CHANNEL_POSITION_REAR_LEFT; break; case Channel::BackRight : cmap.map[i] = PA_CHANNEL_POSITION_REAR_RIGHT; break; case Channel::BackCenter : cmap.map[i] = PA_CHANNEL_POSITION_REAR_CENTER; break; case Channel::SideLeft : cmap.map[i] = PA_CHANNEL_POSITION_SIDE_LEFT; break; case Channel::SideRight : cmap.map[i] = PA_CHANNEL_POSITION_SIDE_RIGHT; break; case Channel::LFE : cmap.map[i] = PA_CHANNEL_POSITION_LFE; break; default: goto failed; } } stream = pa_stream_new(pulse_context,"Goggles Music Manager",&spec,&cmap); if (stream == nullptr) goto failed; #ifdef DEBUG pa_stream_set_state_callback(stream,stream_state_callback,this); #endif //pa_stream_set_write_callback(stream,stream_write_callback,this); if (pa_stream_connect_playback(stream,nullptr,nullptr,PA_STREAM_NOFLAGS,nullptr,nullptr)<0) goto failed; /// Wait until stream is ready pa_stream_state_t state; while((state=pa_stream_get_state(stream))!=PA_STREAM_READY) { if (state==PA_STREAM_FAILED || state==PA_STREAM_TERMINATED){ goto failed; } context->wait_plugin_events(); } /// Get Actual Format config = pa_stream_get_sample_spec(stream); if (!to_gap_format(config->format,af)) goto failed; af.channels=config->channels; af.rate=config->rate; af.channelmap=fmt.channelmap; /// Get Current Volume operation = pa_context_get_sink_input_info(pulse_context,pa_stream_get_index(stream),sink_info_callback,this); if (operation) pa_operation_unref(operation); return true; failed: GM_DEBUG_PRINT("[pulse] Unsupported pulse configuration:\n"); fmt.debug(); return false; }
int ao_plugin_open(ao_device *device, ao_sample_format *format) { char *p=NULL, t[256], t2[256]; const char *fn = NULL; ao_pulse_internal *internal; struct pa_sample_spec ss; struct pa_channel_map map; struct pa_buffer_attr battr; size_t allocated = 128; assert(device && device->internal && format); internal = (ao_pulse_internal *) device->internal; if (format->bits == 8) ss.format = PA_SAMPLE_U8; else if (format->bits == 16) ss.format = PA_SAMPLE_S16NE; #ifdef PA_SAMPLE_S24NE else if (format->bits == 24) ss.format = PA_SAMPLE_S24NE; #endif else return 0; if (device->output_channels <= 0 || device->output_channels > PA_CHANNELS_MAX) return 0; ss.channels = device->output_channels; ss.rate = format->rate; disable_sigpipe(); if (internal->client_name) { snprintf(t, sizeof(t), "libao[%s]", internal->client_name); snprintf(t2, sizeof(t2), "libao[%s] playback stream", internal->client_name); } else { while (1) { p = pa_xmalloc(allocated); if (!(fn = pa_get_binary_name(p, allocated))) { break; } if (fn != p || strlen(p) < allocated - 1) { fn = pa_path_get_filename(fn); snprintf(t, sizeof(t), "libao[%s]", fn); snprintf(t2, sizeof(t2), "libao[%s] playback stream", fn); break; } pa_xfree(p); allocated *= 2; } pa_xfree(p); p = NULL; if (!fn) { strcpy(t, "libao"); strcpy(t2, "libao playback stream"); } } if(device->input_map){ int i; pa_channel_map_init(&map); map.channels=device->output_channels; for(i=0;i<device->output_channels;i++){ if(device->input_map[i]==-1){ map.map[i] = PA_CHANNEL_POSITION_INVALID; }else{ map.map[i] = device->input_map[i]; } } } /* buffering attributes */ battr.prebuf = battr.minreq = battr.fragsize = -1; battr.tlength = (int)(internal->buffer_time * format->rate) / 1000000 * ((format->bits+7)/8) + device->output_channels; battr.minreq = battr.tlength/4; battr.maxlength = battr.tlength+battr.minreq; internal->simple = pa_simple_new(internal->server, t, PA_STREAM_PLAYBACK, internal->sink, t2, &ss, (device->input_map ? &map : NULL), &battr, NULL); if (!internal->simple) return 0; device->driver_byte_format = AO_FMT_NATIVE; internal->static_delay = pa_simple_get_latency(internal->simple, NULL); if(internal->static_delay<0) internal->static_delay = 0; return 1; }
bool CAESinkPULSE::Initialize(AEAudioFormat &format, std::string &device) { { CSingleLock lock(m_sec); m_IsAllocated = false; } m_passthrough = false; m_BytesPerSecond = 0; m_BufferSize = 0; m_filled_bytes = 0; m_lastPackageStamp = 0; m_Channels = 0; m_Stream = NULL; m_Context = NULL; m_periodSize = 0; if (!SetupContext(NULL, &m_Context, &m_MainLoop)) { CLog::Log(LOGNOTICE, "PulseAudio might not be running. Context was not created."); Deinitialize(); return false; } pa_threaded_mainloop_lock(m_MainLoop); struct pa_channel_map map; pa_channel_map_init(&map); // PULSE cannot cope with e.g. planar formats so we fallback to FLOAT // when we receive an invalid pulse format if (AEFormatToPulseFormat(format.m_dataFormat) == PA_SAMPLE_INVALID) { CLog::Log(LOGDEBUG, "PULSE does not support format: %s - will fallback to AE_FMT_FLOAT", CAEUtil::DataFormatToStr(format.m_dataFormat)); format.m_dataFormat = AE_FMT_FLOAT; } m_passthrough = AE_IS_RAW(format.m_dataFormat); if(m_passthrough) { map.channels = 2; format.m_channelLayout = AE_CH_LAYOUT_2_0; } else { map = AEChannelMapToPAChannel(format.m_channelLayout); // if count has changed we need to fit the AE Map if(map.channels != format.m_channelLayout.Count()) format.m_channelLayout = PAChannelToAEChannelMap(map); } m_Channels = format.m_channelLayout.Count(); // store information about current sink SinkInfoStruct sinkStruct; sinkStruct.mainloop = m_MainLoop; sinkStruct.device_found = false; // get real sample rate of the device we want to open - to avoid resampling bool isDefaultDevice = (device == "Default"); WaitForOperation(pa_context_get_sink_info_by_name(m_Context, isDefaultDevice ? NULL : device.c_str(), SinkInfoCallback, &sinkStruct), m_MainLoop, "Get Sink Info"); // only check if the device is existing - don't alter the sample rate if (!sinkStruct.device_found) { CLog::Log(LOGERROR, "PulseAudio: Sink %s not found", device.c_str()); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } // Pulse can resample everything between 1 hz and 192000 hz // Make sure we are in the range that we originally added format.m_sampleRate = std::max(5512U, std::min(format.m_sampleRate, 192000U)); pa_format_info *info[1]; info[0] = pa_format_info_new(); info[0]->encoding = AEFormatToPulseEncoding(format.m_dataFormat); if(!m_passthrough) { pa_format_info_set_sample_format(info[0], AEFormatToPulseFormat(format.m_dataFormat)); pa_format_info_set_channel_map(info[0], &map); } pa_format_info_set_channels(info[0], m_Channels); // PA requires m_encodedRate in order to do EAC3 unsigned int samplerate = format.m_sampleRate; if (m_passthrough && (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937)) { // this is only used internally for PA to use EAC3 samplerate = format.m_encodedRate; } pa_format_info_set_rate(info[0], samplerate); if (!pa_format_info_valid(info[0])) { CLog::Log(LOGERROR, "PulseAudio: Invalid format info"); pa_format_info_free(info[0]); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } pa_sample_spec spec; #if PA_CHECK_VERSION(2,0,0) pa_format_info_to_sample_spec(info[0], &spec, NULL); #else spec.rate = (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937) ? 4 * samplerate : samplerate; spec.format = AEFormatToPulseFormat(format.m_dataFormat); spec.channels = m_Channels; #endif if (!pa_sample_spec_valid(&spec)) { CLog::Log(LOGERROR, "PulseAudio: Invalid sample spec"); pa_format_info_free(info[0]); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } m_BytesPerSecond = pa_bytes_per_second(&spec); unsigned int frameSize = pa_frame_size(&spec); m_Stream = pa_stream_new_extended(m_Context, "kodi audio stream", info, 1, NULL); pa_format_info_free(info[0]); if (m_Stream == NULL) { CLog::Log(LOGERROR, "PulseAudio: Could not create a stream"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } pa_stream_set_state_callback(m_Stream, StreamStateCallback, m_MainLoop); pa_stream_set_write_callback(m_Stream, StreamRequestCallback, m_MainLoop); pa_stream_set_latency_update_callback(m_Stream, StreamLatencyUpdateCallback, m_MainLoop); // default buffer construction // align with AE's max buffer unsigned int latency = m_BytesPerSecond / 2.5; // 400 ms unsigned int process_time = latency / 4; // 100 ms if(sinkStruct.isHWDevice) { // on hw devices buffers can be further reduced // 200ms max latency // 50ms min packet size latency = m_BytesPerSecond / 5; process_time = latency / 4; } pa_buffer_attr buffer_attr; buffer_attr.fragsize = latency; buffer_attr.maxlength = (uint32_t) -1; buffer_attr.minreq = process_time; buffer_attr.prebuf = (uint32_t) -1; buffer_attr.tlength = latency; if (pa_stream_connect_playback(m_Stream, isDefaultDevice ? NULL : device.c_str(), &buffer_attr, ((pa_stream_flags)(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY)), NULL, NULL) < 0) { CLog::Log(LOGERROR, "PulseAudio: Failed to connect stream to output"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } /* Wait until the stream is ready */ do { pa_threaded_mainloop_wait(m_MainLoop); CLog::Log(LOGDEBUG, "PulseAudio: Stream %s", StreamStateToString(pa_stream_get_state(m_Stream))); } while (pa_stream_get_state(m_Stream) != PA_STREAM_READY && pa_stream_get_state(m_Stream) != PA_STREAM_FAILED); if (pa_stream_get_state(m_Stream) == PA_STREAM_FAILED) { CLog::Log(LOGERROR, "PulseAudio: Waited for the stream but it failed"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } const pa_buffer_attr *a; if (!(a = pa_stream_get_buffer_attr(m_Stream))) { CLog::Log(LOGERROR, "PulseAudio: %s", pa_strerror(pa_context_errno(m_Context))); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } else { unsigned int packetSize = a->minreq; m_BufferSize = a->tlength; m_periodSize = a->minreq; format.m_frames = packetSize / frameSize; } { CSingleLock lock(m_sec); // Register Callback for Sink changes pa_context_set_subscribe_callback(m_Context, SinkChangedCallback, this); const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SINK; pa_operation *op = pa_context_subscribe(m_Context, mask, NULL, this); if (op != NULL) pa_operation_unref(op); // Register Callback for Sink Info changes - this handles volume pa_context_set_subscribe_callback(m_Context, SinkInputInfoChangedCallback, this); const pa_subscription_mask_t mask_input = PA_SUBSCRIPTION_MASK_SINK_INPUT; pa_operation* op_sinfo = pa_context_subscribe(m_Context, mask_input, NULL, this); if (op_sinfo != NULL) pa_operation_unref(op_sinfo); } pa_threaded_mainloop_unlock(m_MainLoop); format.m_frameSize = frameSize; format.m_frameSamples = format.m_frames * format.m_channelLayout.Count(); m_format = format; format.m_dataFormat = m_passthrough ? AE_FMT_S16NE : format.m_dataFormat; CLog::Log(LOGNOTICE, "PulseAudio: Opened device %s in %s mode with Buffersize %u ms", device.c_str(), m_passthrough ? "passthrough" : "pcm", (unsigned int) ((m_BufferSize / (float) m_BytesPerSecond) * 1000)); // Cork stream will resume when adding first package Pause(true); { CSingleLock lock(m_sec); m_IsAllocated = true; } return true; }
/** * Setup a new stream based on the properties of the given audio_buf */ static void stream_setup(pa_audio_mode_t *pam, audio_buf_t *ab) { pa_stream *s; char buf[100]; int flags = 0; #if PA_API_VERSION >= 12 pa_proplist *pl; media_pipe_t *mp = ab->ab_mp; #endif pa_channel_map map; pa_cvolume cv; memset(&pam->ss, 0, sizeof(pa_sample_spec)); pam->ss.format = ab->ab_isfloat ? PA_SAMPLE_FLOAT32NE : PA_SAMPLE_S16NE; pam->ss.rate = ab->ab_samplerate; switch(ab->ab_format) { case AM_FORMAT_PCM_STEREO: pam->ss.channels = 2; pa_channel_map_init_stereo(&map); break; case AM_FORMAT_PCM_5DOT0: pam->ss.channels = 5; pa_channel_map_init(&map); map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT; break; case AM_FORMAT_PCM_5DOT1: pam->ss.channels = 6; pa_channel_map_init(&map); map.channels = 6; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_LFE; map.map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; break; case AM_FORMAT_PCM_7DOT1: pam->ss.channels = 8; pa_channel_map_init(&map); map.channels = 8; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_LFE; map.map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; map.map[6] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[7] = PA_CHANNEL_POSITION_REAR_RIGHT; break; case AM_FORMAT_PCM_6DOT1: pam->ss.channels = 7; pa_channel_map_init(&map); map.channels = 7; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_LFE; map.map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; map.map[6] = PA_CHANNEL_POSITION_REAR_CENTER; break; default: abort(); } TRACE(TRACE_DEBUG, "PA", "Created stream %s", pa_sample_spec_snprint(buf, sizeof(buf), &pam->ss)); #if PA_API_VERSION >= 12 pl = pa_proplist_new(); if(mp->mp_flags & MP_VIDEO) pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "video"); else pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "music"); s = pa_stream_new_with_proplist(pam->context, "Showtime playback", &pam->ss, &map, pl); pa_proplist_free(pl); #else s = pa_stream_new(pam->context, "Showtime playback", &pam->ss, &map); #endif pa_stream_set_state_callback(s, stream_state_callback, pam); pa_stream_set_write_callback(s, stream_write_callback, pam); flags |= PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING; memset(&cv, 0, sizeof(cv)); pa_cvolume_set(&cv, pam->ss.channels, pam->mastervol); #if 1 pa_buffer_attr pba = {0}; pba.fragsize = (uint32_t)-1; pba.maxlength = 16 * 1024; pba.minreq = 3 * 1024; pba.prebuf = 8 * 1024; pba.tlength = 12 * 1024; #endif pa_stream_connect_playback(s, NULL, &pba, flags, &cv, NULL); pam->stream = s; pam->cur_rate = ab->ab_samplerate; pam->cur_format = ab->ab_format; pam->cur_isfloat = ab->ab_isfloat; }
static int pulseaudio_audio_reconfig(audio_decoder_t *ad) { decoder_t *d = (decoder_t *)ad; int i; pa_threaded_mainloop_lock(mainloop); if(pulseaudio_make_context_ready()) { pa_threaded_mainloop_unlock(mainloop); return -1; } if(d->s) { pa_stream_disconnect(d->s); pa_stream_unref(d->s); } pa_channel_map map; ad->ad_out_sample_rate = ad->ad_in_sample_rate; d->ss.rate = ad->ad_in_sample_rate; switch(ad->ad_in_sample_format) { case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P: ad->ad_out_sample_format = AV_SAMPLE_FMT_S32; d->ss.format = PA_SAMPLE_S32NE; d->framesize = sizeof(int32_t); break; case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: ad->ad_out_sample_format = AV_SAMPLE_FMT_S16; d->ss.format = PA_SAMPLE_S16NE; d->framesize = sizeof(int16_t); break; default: ad->ad_out_sample_format = AV_SAMPLE_FMT_FLT; d->ss.format = PA_SAMPLE_FLOAT32NE; d->framesize = sizeof(float); break; } switch(ad->ad_in_channel_layout) { case AV_CH_LAYOUT_MONO: d->ss.channels = 1; ad->ad_out_channel_layout = AV_CH_LAYOUT_MONO; pa_channel_map_init_mono(&map); break; case AV_CH_LAYOUT_STEREO: d->ss.channels = 2; ad->ad_out_channel_layout = AV_CH_LAYOUT_STEREO; pa_channel_map_init_stereo(&map); default: pa_channel_map_init(&map); for(i = 0; i < sizeof(av2pa_map) / sizeof(av2pa_map[0]); i++) { if(ad->ad_in_channel_layout & av2pa_map[i].avmask) { ad->ad_out_channel_layout |= av2pa_map[i].avmask; map.map[map.channels++] = av2pa_map[i].papos; } } d->ss.channels = map.channels; break; } d->framesize *= d->ss.channels; ad->ad_tile_size = pa_context_get_tile_size(ctx, &d->ss) / d->framesize; char buf[100]; char buf2[PA_CHANNEL_MAP_SNPRINT_MAX]; TRACE(TRACE_DEBUG, "PA", "Created stream %s [%s] (tilesize=%d)", pa_sample_spec_snprint(buf, sizeof(buf), &d->ss), pa_channel_map_snprint(buf2, sizeof(buf2), &map), ad->ad_tile_size); #if PA_API_VERSION >= 12 pa_proplist *pl = pa_proplist_new(); media_pipe_t *mp = ad->ad_mp; if(mp->mp_flags & MP_VIDEO) pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "video"); else pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "music"); d->s = pa_stream_new_with_proplist(ctx, "Showtime playback", &d->ss, &map, pl); pa_proplist_free(pl); #else d->s = pa_stream_new(ctx, "Showtime playback", &ss, &map); #endif int flags = 0; pa_stream_set_state_callback(d->s, stream_state_callback, d); pa_stream_set_write_callback(d->s, stream_write_callback, d); flags |= PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING; pa_stream_connect_playback(d->s, NULL, NULL, flags, NULL, NULL); while(1) { switch(pa_stream_get_state(d->s)) { case PA_STREAM_UNCONNECTED: case PA_STREAM_CREATING: pa_threaded_mainloop_wait(mainloop); continue; case PA_STREAM_READY: pa_threaded_mainloop_unlock(mainloop); return 0; case PA_STREAM_TERMINATED: case PA_STREAM_FAILED: pa_threaded_mainloop_unlock(mainloop); return 1; } } }
pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { const char *state; pa_channel_map map; char *p; pa_assert(rmap); pa_assert(s); pa_channel_map_init(&map); /* We don't need to match against the well known channel mapping * "mono" here explicitly, because that can be understood as * listing with one channel called "mono". */ if (pa_streq(s, "stereo")) { map.channels = 2; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; goto finish; } else if (pa_streq(s, "surround-40")) { map.channels = 4; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; goto finish; } else if (pa_streq(s, "surround-41")) { map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_LFE; goto finish; } else if (pa_streq(s, "surround-50")) { map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; goto finish; } else if (pa_streq(s, "surround-51")) { map.channels = 6; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; map.map[5] = PA_CHANNEL_POSITION_LFE; goto finish; } else if (pa_streq(s, "surround-71")) { map.channels = 8; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; map.map[5] = PA_CHANNEL_POSITION_LFE; map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; goto finish; } state = NULL; map.channels = 0; while ((p = pa_split(s, ",", &state))) { pa_channel_position_t f; if (map.channels >= PA_CHANNELS_MAX) { pa_xfree(p); return NULL; } if ((f = pa_channel_position_from_string(p)) == PA_CHANNEL_POSITION_INVALID) { pa_xfree(p); return NULL; } map.map[map.channels++] = f; pa_xfree(p); } finish: if (!pa_channel_map_valid(&map)) return NULL; *rmap = map; return rmap; }
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { pa_assert(m); pa_assert(channels > 0); pa_assert(channels <= PA_CHANNELS_MAX); pa_assert(def < PA_CHANNEL_MAP_DEF_MAX); pa_channel_map_init(m); m->channels = (uint8_t) channels; switch (def) { case PA_CHANNEL_MAP_AIFF: /* This is somewhat compatible with RFC3551 */ switch (channels) { case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; case 6: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER; return m; case 5: m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; case 3: m->map[0] = PA_CHANNEL_POSITION_LEFT; m->map[1] = PA_CHANNEL_POSITION_RIGHT; m->map[2] = PA_CHANNEL_POSITION_CENTER; return m; case 4: m->map[0] = PA_CHANNEL_POSITION_LEFT; m->map[1] = PA_CHANNEL_POSITION_CENTER; m->map[2] = PA_CHANNEL_POSITION_RIGHT; m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER; return m; default: return NULL; } case PA_CHANNEL_MAP_ALSA: switch (channels) { case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; case 8: m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; /* Fall through */ case 6: m->map[5] = PA_CHANNEL_POSITION_LFE; /* Fall through */ case 5: m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; /* Fall through */ case 4: m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; default: return NULL; } case PA_CHANNEL_MAP_AUX: { unsigned i; for (i = 0; i < channels; i++) m->map[i] = PA_CHANNEL_POSITION_AUX0 + i; return m; } case PA_CHANNEL_MAP_WAVEEX: /* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */ switch (channels) { case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; case 18: m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT; m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER; m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT; /* Fall through */ case 15: m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT; m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER; m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; /* Fall through */ case 12: m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER; /* Fall through */ case 11: m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT; /* Fall through */ case 9: m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER; /* Fall through */ case 8: m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; /* Fall through */ case 6: m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ case 4: m->map[3] = PA_CHANNEL_POSITION_LFE; /* Fall through */ case 3: m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; /* Fall through */ case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; default: return NULL; } case PA_CHANNEL_MAP_OSS: switch (channels) { case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; case 8: m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ case 6: m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; /* Fall through */ case 4: m->map[3] = PA_CHANNEL_POSITION_LFE; /* Fall through */ case 3: m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; /* Fall through */ case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; default: return NULL; } default: pa_assert_not_reached(); } }