示例#1
0
int pa_scache_add_item(
        pa_core *c,
        const char *name,
        const pa_sample_spec *ss,
        const pa_channel_map *map,
        const pa_memchunk *chunk,
        pa_proplist *p,
        uint32_t *idx) {

    pa_scache_entry *e;
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
    pa_channel_map tmap;

    pa_assert(c);
    pa_assert(name);
    pa_assert(!ss || pa_sample_spec_valid(ss));
    pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss)));

    if (ss && !map) {
        pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
        map = &tmap;
    }

    if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
        return -1;

    if (!(e = scache_add_item(c, name)))
        return -1;

    pa_sample_spec_init(&e->sample_spec);
    pa_channel_map_init(&e->channel_map);
    pa_cvolume_init(&e->volume);
    e->volume_is_set = FALSE;

    if (ss) {
        e->sample_spec = *ss;
        pa_cvolume_reset(&e->volume, ss->channels);
    }

    if (map)
        e->channel_map = *map;

    if (chunk) {
        e->memchunk = *chunk;
        pa_memblock_ref(e->memchunk.memblock);
    }

    if (p)
        pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);

    if (idx)
        *idx = e->index;

    pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
                 name, e->index, (unsigned long) e->memchunk.length,
                 pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));

    return 0;
}
示例#2
0
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
    int r = -1;
    FILE *f = NULL;
    struct channel_conf_info ci;
    pa_config_item table[] = {
        { "daemonize",                  pa_config_parse_bool,     &c->daemonize, NULL },
        { "fail",                       pa_config_parse_bool,     &c->fail, NULL },
        { "high-priority",              pa_config_parse_bool,     &c->high_priority, NULL },
        { "realtime-scheduling",        pa_config_parse_bool,     &c->realtime_scheduling, NULL },
        { "disallow-module-loading",    pa_config_parse_bool,     &c->disallow_module_loading, NULL },
        { "allow-module-loading",       pa_config_parse_not_bool, &c->disallow_module_loading, NULL },
        { "disallow-exit",              pa_config_parse_bool,     &c->disallow_exit, NULL },
        { "allow-exit",                 pa_config_parse_not_bool, &c->disallow_exit, NULL },
        { "use-pid-file",               pa_config_parse_bool,     &c->use_pid_file, NULL },
        { "system-instance",            pa_config_parse_bool,     &c->system_instance, NULL },
#ifdef HAVE_DBUS
        { "local-server-type",          parse_server_type,        c, NULL },
#endif
        { "no-cpu-limit",               pa_config_parse_bool,     &c->no_cpu_limit, NULL },
        { "cpu-limit",                  pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
        { "disable-shm",                pa_config_parse_bool,     &c->disable_shm, NULL },
        { "enable-shm",                 pa_config_parse_not_bool, &c->disable_shm, NULL },
        { "enable-memfd",               pa_config_parse_not_bool, &c->disable_memfd, NULL },
        { "flat-volumes",               pa_config_parse_bool,     &c->flat_volumes, NULL },
        { "lock-memory",                pa_config_parse_bool,     &c->lock_memory, NULL },
        { "enable-deferred-volume",     pa_config_parse_bool,     &c->deferred_volume, NULL },
        { "exit-idle-time",             pa_config_parse_int,      &c->exit_idle_time, NULL },
        { "scache-idle-time",           pa_config_parse_int,      &c->scache_idle_time, NULL },
        { "realtime-priority",          parse_rtprio,             c, NULL },
        { "dl-search-path",             pa_config_parse_string,   &c->dl_search_path, NULL },
        { "default-script-file",        pa_config_parse_string,   &c->default_script_file, NULL },
        { "log-target",                 parse_log_target,         c, NULL },
        { "log-level",                  parse_log_level,          c, NULL },
        { "verbose",                    parse_log_level,          c, NULL },
        { "resample-method",            parse_resample_method,    c, NULL },
        { "default-sample-format",      parse_sample_format,      c, NULL },
        { "default-sample-rate",        parse_sample_rate,        c, NULL },
        { "alternate-sample-rate",      parse_alternate_sample_rate, c, NULL },
        { "default-sample-channels",    parse_sample_channels,    &ci,  NULL },
        { "default-channel-map",        parse_channel_map,        &ci,  NULL },
        { "default-fragments",          parse_fragments,          c, NULL },
        { "default-fragment-size-msec", parse_fragment_size_msec, c, NULL },
        { "deferred-volume-safety-margin-usec",
                                        pa_config_parse_unsigned, &c->deferred_volume_safety_margin_usec, NULL },
        { "deferred-volume-extra-delay-usec",
                                        pa_config_parse_int,      &c->deferred_volume_extra_delay_usec, NULL },
        { "nice-level",                 parse_nice_level,         c, NULL },
        { "avoid-resampling",           pa_config_parse_bool,     &c->avoid_resampling, NULL },
        { "disable-remixing",           pa_config_parse_bool,     &c->disable_remixing, NULL },
        { "enable-remixing",            pa_config_parse_not_bool, &c->disable_remixing, NULL },
        { "remixing-use-all-sink-channels",
                                        pa_config_parse_bool,     &c->remixing_use_all_sink_channels, NULL },
        { "disable-lfe-remixing",       pa_config_parse_bool,     &c->disable_lfe_remixing, NULL },
        { "enable-lfe-remixing",        pa_config_parse_not_bool, &c->disable_lfe_remixing, NULL },
        { "lfe-crossover-freq",         pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL },
        { "load-default-script-file",   pa_config_parse_bool,     &c->load_default_script_file, NULL },
        { "shm-size-bytes",             pa_config_parse_size,     &c->shm_size, NULL },
        { "log-meta",                   pa_config_parse_bool,     &c->log_meta, NULL },
        { "log-time",                   pa_config_parse_bool,     &c->log_time, NULL },
        { "log-backtrace",              pa_config_parse_unsigned, &c->log_backtrace, NULL },
#ifdef HAVE_SYS_RESOURCE_H
        { "rlimit-fsize",               parse_rlimit,             &c->rlimit_fsize, NULL },
        { "rlimit-data",                parse_rlimit,             &c->rlimit_data, NULL },
        { "rlimit-stack",               parse_rlimit,             &c->rlimit_stack, NULL },
        { "rlimit-core",                parse_rlimit,             &c->rlimit_core, NULL },
#ifdef RLIMIT_RSS
        { "rlimit-rss",                 parse_rlimit,             &c->rlimit_rss, NULL },
#endif
#ifdef RLIMIT_NOFILE
        { "rlimit-nofile",              parse_rlimit,             &c->rlimit_nofile, NULL },
#endif
#ifdef RLIMIT_AS
        { "rlimit-as",                  parse_rlimit,             &c->rlimit_as, NULL },
#endif
#ifdef RLIMIT_NPROC
        { "rlimit-nproc",               parse_rlimit,             &c->rlimit_nproc, NULL },
#endif
#ifdef RLIMIT_MEMLOCK
        { "rlimit-memlock",             parse_rlimit,             &c->rlimit_memlock, NULL },
#endif
#ifdef RLIMIT_LOCKS
        { "rlimit-locks",               parse_rlimit,             &c->rlimit_locks, NULL },
#endif
#ifdef RLIMIT_SIGPENDING
        { "rlimit-sigpending",          parse_rlimit,             &c->rlimit_sigpending, NULL },
#endif
#ifdef RLIMIT_MSGQUEUE
        { "rlimit-msgqueue",            parse_rlimit,             &c->rlimit_msgqueue, NULL },
#endif
#ifdef RLIMIT_NICE
        { "rlimit-nice",                parse_rlimit,             &c->rlimit_nice, NULL },
#endif
#ifdef RLIMIT_RTPRIO
        { "rlimit-rtprio",              parse_rlimit,             &c->rlimit_rtprio, NULL },
#endif
#ifdef RLIMIT_RTTIME
        { "rlimit-rttime",              parse_rlimit,             &c->rlimit_rttime, NULL },
#endif
#endif
        { NULL,                         NULL,                     NULL, NULL },
    };

    pa_xfree(c->config_file);
    c->config_file = NULL;

    f = filename ?
        pa_fopen_cloexec(c->config_file = pa_xstrdup(filename), "r") :
        pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);

    if (!f && errno != ENOENT) {
        pa_log_warn(_("Failed to open configuration file: %s"), pa_cstrerror(errno));
        goto finish;
    }

    ci.default_channel_map_set = ci.default_sample_spec_set = false;
    ci.conf = c;

    r = f ? pa_config_parse(c->config_file, f, table, NULL, true, NULL) : 0;

    if (r >= 0) {

        /* Make sure that channel map and sample spec fit together */

        if (ci.default_sample_spec_set &&
            ci.default_channel_map_set &&
            c->default_channel_map.channels != c->default_sample_spec.channels) {
            pa_log_error(_("The specified default channel map has a different number of channels than the specified default number of channels."));
            r = -1;
            goto finish;
        } else if (ci.default_sample_spec_set)
            pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
        else if (ci.default_channel_map_set)
            c->default_sample_spec.channels = c->default_channel_map.channels;
    }

finish:
    if (f)
        fclose(f);

    return r;
}
示例#3
0
int main(int argc, char *argv[]) {
    pa_mainloop* m = NULL;
    int ret = 1, c;
    char *bn, *server = NULL;
    pa_time_event *time_event = NULL;
    const char *filename = NULL;

    static const struct option long_options[] = {
        {"record",       0, NULL, 'r'},
        {"playback",     0, NULL, 'p'},
        {"device",       1, NULL, 'd'},
        {"server",       1, NULL, 's'},
        {"client-name",  1, NULL, 'n'},
        {"stream-name",  1, NULL, ARG_STREAM_NAME},
        {"version",      0, NULL, ARG_VERSION},
        {"help",         0, NULL, 'h'},
        {"verbose",      0, NULL, 'v'},
        {"volume",       1, NULL, ARG_VOLUME},
        {"rate",         1, NULL, ARG_SAMPLERATE},
        {"format",       1, NULL, ARG_SAMPLEFORMAT},
        {"channels",     1, NULL, ARG_CHANNELS},
        {"channel-map",  1, NULL, ARG_CHANNELMAP},
        {"fix-format",   0, NULL, ARG_FIX_FORMAT},
        {"fix-rate",     0, NULL, ARG_FIX_RATE},
        {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
        {"no-remap",     0, NULL, ARG_NO_REMAP},
        {"no-remix",     0, NULL, ARG_NO_REMIX},
        {"latency",      1, NULL, ARG_LATENCY},
        {"process-time", 1, NULL, ARG_PROCESS_TIME},
        {"property",     1, NULL, ARG_PROPERTY},
        {"raw",          0, NULL, ARG_RAW},
        {"file-format",  2, NULL, ARG_FILE_FORMAT},
        {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
        {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
        {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
        {NULL,           0, NULL, 0}
    };

    setlocale(LC_ALL, "");
    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);

    bn = pa_path_get_filename(argv[0]);

    if (strstr(bn, "play")) {
        mode = PLAYBACK;
        raw = FALSE;
    } else if (strstr(bn, "record")) {
        mode = RECORD;
        raw = FALSE;
    } else if (strstr(bn, "cat")) {
        mode = PLAYBACK;
        raw = TRUE;
    } if (strstr(bn, "rec") || strstr(bn, "mon")) {
        mode = RECORD;
        raw = TRUE;
    }

    proplist = pa_proplist_new();

    while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {

        switch (c) {
            case 'h' :
                help(bn);
                ret = 0;
                goto quit;

            case ARG_VERSION:
                printf(_("pacat %s\n"
                         "Compiled with libpulse %s\n"
                         "Linked with libpulse %s\n"),
                       PACKAGE_VERSION,
                       pa_get_headers_version(),
                       pa_get_library_version());
                ret = 0;
                goto quit;

            case 'r':
                mode = RECORD;
                break;

            case 'p':
                mode = PLAYBACK;
                break;

            case 'd':
                pa_xfree(device);
                device = pa_xstrdup(optarg);
                break;

            case 's':
                pa_xfree(server);
                server = pa_xstrdup(optarg);
                break;

            case 'n': {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {

                    pa_log(_("Invalid client name '%s'"), t ? t : optarg);
                    pa_xfree(t);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case ARG_STREAM_NAME: {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {

                    pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
                    pa_xfree(t);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case 'v':
                verbose = 1;
                break;

            case ARG_VOLUME: {
                int v = atoi(optarg);
                volume = v < 0 ? 0U : (pa_volume_t) v;
                volume_is_set = TRUE;
                break;
            }

            case ARG_CHANNELS:
                sample_spec.channels = (uint8_t) atoi(optarg);
                sample_spec_set = TRUE;
                break;

            case ARG_SAMPLEFORMAT:
                sample_spec.format = pa_parse_sample_format(optarg);
                sample_spec_set = TRUE;
                break;

            case ARG_SAMPLERATE:
                sample_spec.rate = (uint32_t) atoi(optarg);
                sample_spec_set = TRUE;
                break;

            case ARG_CHANNELMAP:
                if (!pa_channel_map_parse(&channel_map, optarg)) {
                    pa_log(_("Invalid channel map '%s'"), optarg);
                    goto quit;
                }

                channel_map_set = TRUE;
                break;

            case ARG_FIX_CHANNELS:
                flags |= PA_STREAM_FIX_CHANNELS;
                break;

            case ARG_FIX_RATE:
                flags |= PA_STREAM_FIX_RATE;
                break;

            case ARG_FIX_FORMAT:
                flags |= PA_STREAM_FIX_FORMAT;
                break;

            case ARG_NO_REMIX:
                flags |= PA_STREAM_NO_REMIX_CHANNELS;
                break;

            case ARG_NO_REMAP:
                flags |= PA_STREAM_NO_REMAP_CHANNELS;
                break;

            case ARG_LATENCY:
                if (((latency = (size_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid latency specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROCESS_TIME:
                if (((process_time = (size_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid process time specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_LATENCY_MSEC:
                if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid latency specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROCESS_TIME_MSEC:
                if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid process time specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROPERTY: {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_setp(proplist, t) < 0) {

                    pa_xfree(t);
                    pa_log(_("Invalid property '%s'"), optarg);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case ARG_RAW:
                raw = TRUE;
                break;

            case ARG_FILE_FORMAT:
                raw = FALSE;

                if (optarg) {
                    if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
                        pa_log(_("Unknown file format %s."), optarg);
                        goto quit;
                    }
                }

                raw = FALSE;
                break;

            case ARG_LIST_FILE_FORMATS:
                pa_sndfile_dump_formats();
                ret = 0;
                goto quit;

            default:
                goto quit;
        }
    }

    if (!pa_sample_spec_valid(&sample_spec)) {
        pa_log(_("Invalid sample specification"));
        goto quit;
    }

    if (optind+1 == argc) {
        int fd;

        filename = argv[optind];

        if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
            pa_log(_("open(): %s"), strerror(errno));
            goto quit;
        }

        if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
            pa_log(_("dup2(): %s"), strerror(errno));
            goto quit;
        }

        pa_close(fd);

    } else if (optind+1 <= argc) {
        pa_log(_("Too many arguments."));
        goto quit;
    }

    if (!raw) {
        SF_INFO sfi;
        pa_zero(sfi);

        if (mode == RECORD) {
            /* This might patch up the sample spec */
            if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
                pa_log(_("Failed to generate sample specification for file."));
                goto quit;
            }

            /* Transparently upgrade classic .wav to wavex for multichannel audio */
            if (file_format <= 0) {
                if ((sample_spec.channels == 2 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_LEFT &&
                                                                        channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))) ||
                    (sample_spec.channels == 1 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_MONO))))
                    file_format = SF_FORMAT_WAV;
                else
                    file_format = SF_FORMAT_WAVEX;
            }

            sfi.format |= file_format;
        }

        if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
                                   mode == RECORD ? SFM_WRITE : SFM_READ,
                                   &sfi, 0))) {
            pa_log(_("Failed to open audio file."));
            goto quit;
        }

        if (mode == PLAYBACK) {
            if (sample_spec_set)
                pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));

            if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
                pa_log(_("Failed to determine sample specification from file."));
                goto quit;
            }
            sample_spec_set = TRUE;

            if (!channel_map_set) {
                /* Allow the user to overwrite the channel map on the command line */
                if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
                    if (sample_spec.channels > 2)
                        pa_log(_("Warning: Failed to determine channel map from file."));
                } else
                    channel_map_set = TRUE;
            }
        }
    }

    if (!channel_map_set)
        pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);

    if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
        pa_log(_("Channel map doesn't match sample specification"));
        goto quit;
    }

    if (!raw) {
        pa_proplist *sfp;

        if (mode == PLAYBACK)
            readf_function = pa_sndfile_readf_function(&sample_spec);
        else {
            if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
                pa_log(_("Warning: failed to write channel map to file."));

            writef_function = pa_sndfile_writef_function(&sample_spec);
        }

        /* Fill in libsndfile prop list data */
        sfp = pa_proplist_new();
        pa_sndfile_init_proplist(sndfile, sfp);
        pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
        pa_proplist_free(sfp);
    }

    if (verbose) {
        char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];

        pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
                mode == RECORD ? _("recording") : _("playback"),
                pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
                pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
    }

    /* Fill in client name if none was set */
    if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
        char *t;

        if ((t = pa_locale_to_utf8(bn))) {
            pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
            pa_xfree(t);
        }
    }

    /* Fill in media name if none was set */
    if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
        const char *t;

        if ((t = filename) ||
            (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
            pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);
    }

    /* Set up a new main loop */
    if (!(m = pa_mainloop_new())) {
        pa_log(_("pa_mainloop_new() failed."));
        goto quit;
    }

    mainloop_api = pa_mainloop_get_api(m);

    pa_assert_se(pa_signal_init(mainloop_api) == 0);
    pa_signal_new(SIGINT, exit_signal_callback, NULL);
    pa_signal_new(SIGTERM, exit_signal_callback, NULL);
#ifdef SIGUSR1
    pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
#endif
    pa_disable_sigpipe();

    if (raw) {
        if (!(stdio_event = mainloop_api->io_new(mainloop_api,
                                                 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
                                                 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
                                                 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
            pa_log(_("io_new() failed."));
            goto quit;
        }
    }

    /* Create a new connection context */
    if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
        pa_log(_("pa_context_new() failed."));
        goto quit;
    }

    pa_context_set_state_callback(context, context_state_callback, NULL);

    /* Connect the context */
    if (pa_context_connect(context, server, 0, NULL) < 0) {
        pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
        goto quit;
    }

    if (verbose) {
        if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
            pa_log(_("pa_context_rttime_new() failed."));
            goto quit;
        }
    }

    /* Run the main loop */
    if (pa_mainloop_run(m, &ret) < 0) {
        pa_log(_("pa_mainloop_run() failed."));
        goto quit;
    }

quit:
    if (stream)
        pa_stream_unref(stream);

    if (context)
        pa_context_unref(context);

    if (stdio_event) {
        pa_assert(mainloop_api);
        mainloop_api->io_free(stdio_event);
    }

    if (time_event) {
        pa_assert(mainloop_api);
        mainloop_api->time_free(time_event);
    }

    if (m) {
        pa_signal_done();
        pa_mainloop_free(m);
    }

    pa_xfree(buffer);

    pa_xfree(server);
    pa_xfree(device);

    if (sndfile)
        sf_close(sndfile);

    if (proplist)
        pa_proplist_free(proplist);

    return ret;
}
static void resolver_cb(
        AvahiServiceResolver *r,
        AvahiIfIndex interface, AvahiProtocol protocol,
        AvahiResolverEvent event,
        const char *name, const char *type, const char *domain,
        const char *host_name, const AvahiAddress *a, uint16_t port,
        AvahiStringList *txt,
        AvahiLookupResultFlags flags,
        void *userdata) {

    struct userdata *u = userdata;
    struct tunnel *tnl;

    pa_assert(u);

    tnl = tunnel_new(interface, protocol, name, type, domain);

    if (event != AVAHI_RESOLVER_FOUND)
        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
    else {
        char *device = NULL, *dname, *module_name, *args;
        const char *t;
        char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
        pa_sample_spec ss;
        pa_channel_map cm;
        AvahiStringList *l;
        pa_bool_t channel_map_set = FALSE;
        pa_module *m;

        ss = u->core->default_sample_spec;
        cm = u->core->default_channel_map;

        for (l = txt; l; l = l->next) {
            char *key, *value;
            pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);

            if (pa_streq(key, "device")) {
                pa_xfree(device);
                device = value;
                value = NULL;
            } else if (pa_streq(key, "rate"))
                ss.rate = (uint32_t) atoi(value);
            else if (pa_streq(key, "channels"))
                ss.channels = (uint8_t) atoi(value);
            else if (pa_streq(key, "format"))
                ss.format = pa_parse_sample_format(value);
            else if (pa_streq(key, "channel_map")) {
                pa_channel_map_parse(&cm, value);
                channel_map_set = TRUE;
            }

            avahi_free(key);
            avahi_free(value);
        }

        if (!channel_map_set && cm.channels != ss.channels)
            pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);

        if (!pa_sample_spec_valid(&ss)) {
            pa_log("Service '%s' contains an invalid sample specification.", name);
            avahi_free(device);
            goto finish;
        }

        if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
            pa_log("Service '%s' contains an invalid channel map.", name);
            avahi_free(device);
            goto finish;
        }

        if (device)
            dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device);
        else
            dname = pa_sprintf_malloc("tunnel.%s", host_name);

        if (!pa_namereg_is_valid_name(dname)) {
            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
            avahi_free(device);
            pa_xfree(dname);
            goto finish;
        }

        t = strstr(type, "sink") ? "sink" : "source";

        module_name = pa_sprintf_malloc("module-tunnel-%s", t);
        args = pa_sprintf_malloc("server=[%s]:%u "
                                 "%s=%s "
                                 "format=%s "
                                 "channels=%u "
                                 "rate=%u "
                                 "%s_name=%s "
                                 "channel_map=%s",
                                 avahi_address_snprint(at, sizeof(at), a), port,
                                 t, device,
                                 pa_sample_format_to_string(ss.format),
                                 ss.channels,
                                 ss.rate,
                                 t, dname,
                                 pa_channel_map_snprint(cmt, sizeof(cmt), &cm));

        pa_log_debug("Loading %s with arguments '%s'", module_name, args);

        if ((m = pa_module_load(u->core, module_name, args))) {
            tnl->module_index = m->index;
            pa_hashmap_put(u->tunnels, tnl, tnl);
            tnl = NULL;
        }

        pa_xfree(module_name);
        pa_xfree(dname);
        pa_xfree(args);
        avahi_free(device);
    }

finish:

    avahi_service_resolver_free(r);

    if (tnl)
        tunnel_free(tnl);
}
int pa__init(pa_module*m) {
    struct userdata *u = NULL;
    pa_sample_spec ss;
    pa_channel_map map;
    pa_modargs *ma = NULL;
    jack_status_t status;
    const char *server_name, *client_name;
    uint32_t channels = 0;
    pa_bool_t do_connect = TRUE;
    unsigned i;
    const char **ports = NULL, **p;
    pa_sink_new_data data;

    pa_assert(m);

    jack_set_error_function(jack_error_func);

    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
        pa_log("Failed to parse module arguments.");
        goto fail;
    }

    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
        pa_log("Failed to parse connect= argument.");
        goto fail;
    }

    server_name = pa_modargs_get_value(ma, "server_name", NULL);
    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink");

    m->userdata = u = pa_xnew0(struct userdata, 1);
    u->core = m->core;
    u->module = m;
    u->saved_frame_time_valid = FALSE;
    u->rtpoll = pa_rtpoll_new();
    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);

    /* The queue linking the JACK thread and our RT thread */
    u->jack_msgq = pa_asyncmsgq_new(0);

    /* The msgq from the JACK RT thread should have an even higher
     * priority than the normal message queues, to match the guarantee
     * all other drivers make: supplying the audio device with data is
     * the top priority -- and as long as that is possible we don't do
     * anything else */
    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);

    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
        pa_log("jack_client_open() failed.");
        goto fail;
    }

    ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput);

    channels = 0;
    for (p = ports; *p; p++)
        channels++;

    if (!channels)
        channels = m->core->default_sample_spec.channels;

    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
        channels <= 0 ||
        channels > PA_CHANNELS_MAX) {
        pa_log("Failed to parse channels= argument.");
        goto fail;
    }

    if (channels == m->core->default_channel_map.channels)
        map = m->core->default_channel_map;
    else
        pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA);

    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
        pa_log("Failed to parse channel_map= argument.");
        goto fail;
    }

    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));

    u->channels = ss.channels = (uint8_t) channels;
    ss.rate = jack_get_sample_rate(u->client);
    ss.format = PA_SAMPLE_FLOAT32NE;

    pa_assert(pa_sample_spec_valid(&ss));

    for (i = 0; i < ss.channels; i++) {
        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) {
            pa_log("jack_port_register() failed.");
            goto fail;
        }
    }

    pa_sink_new_data_init(&data);
    data.driver = __FILE__;
    data.module = m;
    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
    pa_sink_new_data_set_sample_spec(&data, &ss);
    pa_sink_new_data_set_channel_map(&data, &map);
    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
    if (server_name)
        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
    pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));

    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
        pa_log("Invalid properties");
        pa_sink_new_data_done(&data);
        goto fail;
    }

    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
    pa_sink_new_data_done(&data);

    if (!u->sink) {
        pa_log("Failed to create sink.");
        goto fail;
    }

    u->sink->parent.process_msg = sink_process_msg;
    u->sink->userdata = u;

    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
    pa_sink_set_rtpoll(u->sink, u->rtpoll);
    pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec));

    jack_set_process_callback(u->client, jack_process, u);
    jack_on_shutdown(u->client, jack_shutdown, u);
    jack_set_thread_init_callback(u->client, jack_init, u);
    jack_set_buffer_size_callback(u->client, jack_buffer_size, u);

    if (!(u->thread = pa_thread_new(thread_func, u))) {
        pa_log("Failed to create thread.");
        goto fail;
    }

    if (jack_activate(u->client)) {
        pa_log("jack_activate() failed");
        goto fail;
    }

    if (do_connect) {
        for (i = 0, p = ports; i < ss.channels; i++, p++) {

            if (!*p) {
                pa_log("Not enough physical output ports, leaving unconnected.");
                break;
            }

            pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p);

            if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) {
                pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
                break;
            }
        }
    }

    pa_sink_put(u->sink);

    free(ports);
    pa_modargs_free(ma);

    return 0;

fail:
    if (ma)
        pa_modargs_free(ma);

    free(ports);

    pa__done(m);

    return -1;
}
示例#6
0
int pa_sound_file_load(
        pa_mempool *pool,
        const char *fname,
        pa_sample_spec *ss,
        pa_channel_map *map,
        pa_memchunk *chunk,
        pa_proplist *p) {

    SNDFILE *sf = NULL;
    SF_INFO sfi;
    int ret = -1;
    size_t l;
    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
    void *ptr = NULL;
    int fd;

    pa_assert(fname);
    pa_assert(ss);
    pa_assert(chunk);

    pa_memchunk_reset(chunk);

    if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) {
        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
        goto finish;
    }

#ifdef HAVE_POSIX_FADVISE
    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
        goto finish;
    } else
        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
#endif

    pa_zero(sfi);
    if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
        pa_log("Failed to open file %s", fname);
        goto finish;
    }

    fd = -1;

    if (pa_sndfile_read_sample_spec(sf, ss) < 0) {
        pa_log("Failed to determine file sample format.");
        goto finish;
    }

    if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) {
        if (ss->channels > 2)
            pa_log("Failed to determine file channel map, synthesizing one.");
        pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
    }

    if (p)
        pa_sndfile_init_proplist(sf, p);

    if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
        pa_log("File too large");
        goto finish;
    }

    chunk->memblock = pa_memblock_new(pool, l);
    chunk->index = 0;
    chunk->length = l;

    readf_function = pa_sndfile_readf_function(ss);

    ptr = pa_memblock_acquire(chunk->memblock);

    if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) ||
        (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) {
        pa_log("Premature file end");
        goto finish;
    }

    ret = 0;

finish:

    if (sf)
        sf_close(sf);

    if (ptr)
        pa_memblock_release(chunk->memblock);

    if (ret != 0 && chunk->memblock)
        pa_memblock_unref(chunk->memblock);

    if (fd >= 0)
        pa_close(fd);

    return ret;
}
int pa__init(pa_module*m) {
    struct userdata *u = NULL;
    pa_sample_spec ss;
    pa_channel_map map;
    pa_modargs *ma = NULL;
    pa_sink_new_data data;
    size_t nbytes;

    pa_assert(m);

    pa_web_debug_func_t func = pa_get_user_data(PA_USER_WEB_FUNC);
    if (!func) {
        pa_log_error("Failed to get user web func from user data");
        goto fail;
    }

    ma = pa_modargs_new(m->argument, valid_modargs);
    if (!ma) {
        pa_log("Failed to parse module arguments.");
        goto fail;
    }

    ss = m->core->default_sample_spec;
    map = m->core->default_channel_map;
    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
        pa_log("Invalid sample format specification or channel map");
        goto fail;
    }

    pa_modargs_get_value_u32(ma, "fixed_rate", &ss.rate);
    uint32_t c = 0;
    pa_modargs_get_value_u32(ma, "fixed_channels", &c);
    if (c)
        ss.channels = c;
    const char* fixed_fmt = pa_modargs_get_value(ma, "fixed_format", NULL);
    if (fixed_fmt)
        ss.format = pa_parse_sample_format(fixed_fmt);

    if (ss.channels != map.channels) {
        pa_log_error("sample_spec channels[%d] != channel_map channels[%d]", ss.channels, map.channels);
        pa_channel_map_init_extend(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
        pa_log_debug("after init extend mmap.channels=%d", map.channels);
    }

    m->userdata = u = pa_xnew0(struct userdata, 1);
    u->core = m->core;
    u->module = m;
    u->rtpoll = pa_rtpoll_new();
    u->data_func = web_data_func;

    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);

    pa_sink_new_data_init(&data);
    data.driver = __FILE__;
    data.module = m;
    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
    pa_sink_new_data_set_sample_spec(&data, &ss);
    pa_sink_new_data_set_channel_map(&data, &map);
    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, _("Remote Output"));
    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");

    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
        pa_log("Invalid properties");
        pa_sink_new_data_done(&data);
        goto fail;
    }

    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
    pa_sink_new_data_done(&data);

    if (!u->sink) {
        pa_log("Failed to create sink object.");
        goto fail;
    }

    u->sink->parent.process_msg = sink_process_msg;
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
    u->sink->userdata = u;

    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
    pa_sink_set_rtpoll(u->sink, u->rtpoll);

    u->block_usec = BLOCK_USEC;
    nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
    pa_sink_set_max_rewind(u->sink, nbytes);
    pa_sink_set_max_request(u->sink, nbytes);

    // call websocket to start
    func(1, NULL, ss.rate, ss.format);

    u->thread = pa_thread_new("remote-sink", thread_func, u);
    if (!u->thread) {
        pa_log("Failed to create thread.");
        goto fail;
    }

    pa_sink_set_latency_range(u->sink, 0, BLOCK_USEC);

    pa_sink_put(u->sink);

    pa_modargs_free(ma);

    return 0;

fail:

    if (ma)
        pa_modargs_free(ma);

    pa__done(m);

    return -1;
}
示例#8
0
static int pulse_set_spec(ddb_waveformat_t *fmt)
{
    memcpy (&plugin.fmt, fmt, sizeof (ddb_waveformat_t));
    if (!plugin.fmt.channels) {
        // generic format
        plugin.fmt.bps = 16;
        plugin.fmt.is_float = 0;
        plugin.fmt.channels = 2;
        plugin.fmt.samplerate = 44100;
        plugin.fmt.channelmask = 3;
    }

    trace ("format %dbit %s %dch %dHz channelmask=%X\n", plugin.fmt.bps, plugin.fmt.is_float ? "float" : "int", plugin.fmt.channels, plugin.fmt.samplerate, plugin.fmt.channelmask);

    ss.channels = plugin.fmt.channels;
    // Try to auto-configure the channel map, see <pulse/channelmap.h> for details
    pa_channel_map channel_map;
    pa_channel_map_init_extend(&channel_map, ss.channels, PA_CHANNEL_MAP_WAVEEX);
    trace ("pulse: channels: %d\n", ss.channels);

    // Read samplerate from config
    //ss.rate = deadbeef->conf_get_int(CONFSTR_PULSE_SAMPLERATE, 44100);
    ss.rate = plugin.fmt.samplerate;
    trace ("pulse: samplerate: %d\n", ss.rate);

    switch (plugin.fmt.bps) {
    case 8:
        ss.format = PA_SAMPLE_U8;
        break;
    case 16:
        ss.format = PA_SAMPLE_S16LE;
        break;
    case 24:
        ss.format = PA_SAMPLE_S24LE;
        break;
    case 32:
        if (plugin.fmt.is_float) {
            ss.format = PA_SAMPLE_FLOAT32LE;
        }
        else {
            ss.format = PA_SAMPLE_S32LE;
        }
        break;
    default:
        return -1;
    };

    if (s) {
        pa_simple_free(s);
    }

    pa_buffer_attr * attr = NULL;
    //attr->maxlength = Maximum length of the buffer.
    //attr->tlength = Playback only: target length of the buffer.
    //attr->prebuf = Playback only: pre-buffering.
    //attr->minreq = Playback only: minimum request.
    //attr->fragsize = Recording only: fragment size.

    buffer_size = deadbeef->conf_get_int(CONFSTR_PULSE_BUFFERSIZE, PULSE_DEFAULT_BUFFERSIZE);

    // TODO: where list of all available devices? add this option to config too..
    char * dev = NULL;

    int error;

    // Read serveraddr from config
    deadbeef->conf_lock ();
    const char * server = deadbeef->conf_get_str_fast (CONFSTR_PULSE_SERVERADDR, NULL);

    if (server) {
        server = strcmp(server, "default") ? server : NULL;
    }

    s = pa_simple_new(server, "Deadbeef", PA_STREAM_PLAYBACK, dev, "Music", &ss, &channel_map, attr, &error);
    deadbeef->conf_unlock ();

    if (!s)
    {
        trace ("pulse_init failed (%d)\n", error);
        return -1;
    }

    return 0;
}
示例#9
0
bool QPulseAudioInput::open()
{
    if (m_opened)
        return true;

    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();

    if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
        setError(QAudio::FatalError);
        setState(QAudio::StoppedState);
        return false;
    }

    pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);

    if (!pa_sample_spec_valid(&spec)) {
        setError(QAudio::OpenError);
        setState(QAudio::StoppedState);
        return false;
    }

    m_spec = spec;

#ifdef DEBUG_PULSE
//    QTime now(QTime::currentTime());
//    qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
#endif

    if (m_streamName.isNull())
        m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();

#ifdef DEBUG_PULSE
        qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format);
        qDebug() << "Rate: " << spec.rate;
        qDebug() << "Channels: " << spec.channels;
        qDebug() << "Frame size: " << pa_frame_size(&spec);
#endif

    pulseEngine->lock();
    pa_channel_map channel_map;

    pa_channel_map_init_extend(&channel_map, spec.channels, PA_CHANNEL_MAP_DEFAULT);

    if (!pa_channel_map_compatible(&channel_map, &spec))
        qWarning() << "Channel map doesn't match sample specification!";

    m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, &channel_map);

    pa_stream_set_state_callback(m_stream, inputStreamStateCallback, this);
    pa_stream_set_read_callback(m_stream, inputStreamReadCallback, this);

    pa_stream_set_underflow_callback(m_stream, inputStreamUnderflowCallback, this);
    pa_stream_set_overflow_callback(m_stream, inputStreamOverflowCallback, this);

    m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec);

    int flags = 0;
    pa_buffer_attr buffer_attr;
    buffer_attr.maxlength = (uint32_t) -1;
    buffer_attr.prebuf = (uint32_t) -1;
    buffer_attr.tlength = (uint32_t) -1;
    buffer_attr.minreq = (uint32_t) -1;
    flags |= PA_STREAM_ADJUST_LATENCY;

    if (m_bufferSize > 0)
        buffer_attr.fragsize = (uint32_t) m_bufferSize;
    else
        buffer_attr.fragsize = (uint32_t) m_periodSize;

    if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) {
        qWarning() << "pa_stream_connect_record() failed!";
        pa_stream_unref(m_stream);
        m_stream = 0;
        pulseEngine->unlock();
        setError(QAudio::OpenError);
        setState(QAudio::StoppedState);
        return false;
    }

    while (pa_stream_get_state(m_stream) != PA_STREAM_READY)
        pa_threaded_mainloop_wait(pulseEngine->mainloop());

    const pa_buffer_attr *actualBufferAttr = pa_stream_get_buffer_attr(m_stream);
    m_periodSize = actualBufferAttr->fragsize;
    m_periodTime = pa_bytes_to_usec(m_periodSize, &spec) / 1000;
    if (actualBufferAttr->tlength != (uint32_t)-1)
        m_bufferSize = actualBufferAttr->tlength;

    pulseEngine->unlock();

    connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed);

    m_opened = true;
    m_timer->start(m_periodTime);

    m_clockStamp.restart();
    m_timeStamp.restart();
    m_elapsedTimeOffset = 0;
    m_totalTimeValue = 0;

    return true;
}
示例#10
0
void pulse_initialize_audio_step_2(pa_context *context)
{
    pa_proplist *stream_proplist;
    pa_sample_spec sample_specification;
    pa_channel_map channel_map;
    pa_buffer_attr buffer_attributes;
    pa_stream *stream;

    /* Now we need to add our mono audio channel to the connection */

    /* SAMPLE_SPEC:  we describe the data going into our channel */
    sample_specification.format = PA_SAMPLE_S16NE;
    sample_specification.rate = AUDIO_SAMPLE_RATE_IN_HZ;
    sample_specification.channels = 1;
    g_assert(pa_sample_spec_valid(&sample_specification));

    /* BUFFER_ATTRIBUTES: Here we set the buffering behavior of the
     * audio.  We want low latency.

     One wiki suggests that to set a specific latency,
     1. use pa_usec_to_bytes(&ss, ...) to convert the latency from a time unit to bytes
     2. use the PA_STREAM_ADJUST_LATENCY flag
     3. set pa_buffer_attr::tlength to latency in samples
     4. set rest of pa_buffer_attr to -1

     http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/LatencyControl
    */

    buffer_attributes.tlength = pa_usec_to_bytes (AUDIO_LATENCY_REQUESTED_IN_MILLISECONDS * MICROSECONDS_PER_MILLISECOND,
                                                  &sample_specification);
    buffer_attributes.maxlength = -1; /* -1 == default */
    buffer_attributes.prebuf = -1;
    buffer_attributes.minreq = -1;
    buffer_attributes.fragsize = -1;

    /* PROPERTY_LIST: Then we set up a structure to hold PulseAudio
     * properties for this. */
    stream_proplist = xpa_proplist_new();
    xpa_proplist_sets(stream_proplist, PA_PROP_MEDIA_NAME, "mono channel");
    xpa_proplist_sets(stream_proplist, PA_PROP_MEDIA_ROLE, "game");

    /* CHANNEL_MAP: Then we say which speakers we're using.  The game
       is mono, so - that makes it simple. */

    pa_channel_map_init_extend(&channel_map, 1, PA_CHANNEL_MAP_DEFAULT);

    if (!pa_channel_map_compatible(&channel_map, &sample_specification))
        g_error("Channel map doesn't match sample specification");

    {
        char tss[100], tcm[100];
        g_debug("Opening a stream with sample specification '%s' and channel map '%s'.",
                pa_sample_spec_snprint(tss, sizeof(tss), &sample_specification),
                pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
    }

    /* STREAM: Group everything together as a stream */
    g_assert (pulse.context == context);
    stream = xpa_stream_new_with_proplist(pulse.context,
                                          "mono channel", /* Stream name */
                                          &sample_specification,
                                          &channel_map,
                                          stream_proplist);
    xpa_proplist_free (stream_proplist);

    xpa_stream_set_started_callback(stream, cb_audio_stream_started, NULL);
    xpa_stream_set_write_callback(stream, cb_audio_stream_write, NULL);

    pulse.samples_written = 0U;

    /* Connect the stream to the audio loop */
    xpa_stream_connect_playback_to_default_device (stream, context,
                                                   &buffer_attributes,
                                                   /* PA_STREAM_ADJUST_LATENCY | */
                                                   PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
    /* Finally done! */
    g_debug("PulseAudio initialization complete");
}
示例#11
0
int pa_play_file(
        pa_sink *sink,
        const char *fname,
        const pa_cvolume *volume) {

    file_stream *u = NULL;
    pa_sample_spec ss;
    pa_channel_map cm;
    pa_sink_input_new_data data;
    int fd;
    SF_INFO sfi;
    pa_memchunk silence;

    pa_assert(sink);
    pa_assert(fname);

    u = pa_msgobject_new(file_stream);
    u->parent.parent.free = file_stream_free;
    u->parent.process_msg = file_stream_process_msg;
    u->core = sink->core;
    u->sink_input = NULL;
    u->sndfile = NULL;
    u->readf_function = NULL;
    u->memblockq = NULL;

    if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) {
        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
        goto fail;
    }

    /* FIXME: For now we just use posix_fadvise to avoid page faults
     * when accessing the file data. Eventually we should move the
     * file reader into the main event loop and pass the data over the
     * asyncmsgq. */

#ifdef HAVE_POSIX_FADVISE
    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
        goto fail;
    } else
        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");

    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
        pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
        goto fail;
    } else
        pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
#endif

    pa_zero(sfi);
    if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
        pa_log("Failed to open file %s", fname);
        goto fail;
    }

    fd = -1;

    if (pa_sndfile_read_sample_spec(u->sndfile, &ss) < 0) {
        pa_log("Failed to determine file sample format.");
        goto fail;
    }

    if (pa_sndfile_read_channel_map(u->sndfile, &cm) < 0) {
        if (ss.channels > 2)
            pa_log_info("Failed to determine file channel map, synthesizing one.");
        pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
    }

    u->readf_function = pa_sndfile_readf_function(&ss);

    pa_sink_input_new_data_init(&data);
    pa_sink_input_new_data_set_sink(&data, sink, FALSE);
    data.driver = __FILE__;
    pa_sink_input_new_data_set_sample_spec(&data, &ss);
    pa_sink_input_new_data_set_channel_map(&data, &cm);
    pa_sink_input_new_data_set_volume(&data, volume);
    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
    pa_sndfile_init_proplist(u->sndfile, data.proplist);

    pa_sink_input_new(&u->sink_input, sink->core, &data);
    pa_sink_input_new_data_done(&data);

    if (!u->sink_input)
        goto fail;

    u->sink_input->pop = sink_input_pop_cb;
    u->sink_input->process_rewind = sink_input_process_rewind_cb;
    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
    u->sink_input->kill = sink_input_kill_cb;
    u->sink_input->state_change = sink_input_state_change_cb;
    u->sink_input->userdata = u;

    pa_sink_input_get_silence(u->sink_input, &silence);
    u->memblockq = pa_memblockq_new("sound-file-stream memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence);
    pa_memblock_unref(silence.memblock);

    pa_sink_input_put(u->sink_input);

    /* The reference to u is dangling here, because we want to keep
     * this stream around until it is fully played. */

    return 0;

fail:
    file_stream_unref(u);

    if (fd >= 0)
        pa_close(fd);

    return -1;
}
示例#12
0
/* Called from main context */
int pa_source_output_new(
        pa_source_output**_o,
        pa_core *core,
        pa_source_output_new_data *data) {

    pa_source_output *o;
    pa_resampler *resampler = NULL;
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
    int r;
    char *pt;

    pa_assert(_o);
    pa_assert(core);
    pa_assert(data);
    pa_assert_ctl_context();

    if (data->client)
        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);

    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
        return r;

    pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);

    if (!data->source) {
        data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
        data->save_source = FALSE;
    }

    pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY);
    pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
    pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);

    if (!data->sample_spec_is_set)
        data->sample_spec = data->source->sample_spec;

    pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);

    if (!data->channel_map_is_set) {
        if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec))
            data->channel_map = data->source->channel_map;
        else
            pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
    }

    pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
    pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);

    if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
        data->sample_spec.format = data->source->sample_spec.format;

    if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE)
        data->sample_spec.rate = data->source->sample_spec.rate;

    if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
        data->sample_spec.channels = data->source->sample_spec.channels;
        data->channel_map = data->source->channel_map;
    }

    pa_assert(pa_sample_spec_valid(&data->sample_spec));
    pa_assert(pa_channel_map_valid(&data->channel_map));

    if (data->resample_method == PA_RESAMPLER_INVALID)
        data->resample_method = core->resample_method;

    pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);

    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
        return r;

    if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) &&
        pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) {
        pa_log("Failed to create source output: source is suspended.");
        return -PA_ERR_BADSTATE;
    }

    if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
        pa_log("Failed to create source output: too many outputs per source.");
        return -PA_ERR_TOOLARGE;
    }

    if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
        !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
        !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {

        if (!(resampler = pa_resampler_new(
                      core->mempool,
                      &data->source->sample_spec, &data->source->channel_map,
                      &data->sample_spec, &data->channel_map,
                      data->resample_method,
                      ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                      ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                      (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
                      (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
            pa_log_warn("Unsupported resampling operation.");
            return -PA_ERR_NOTSUPPORTED;
        }
    }

    o = pa_msgobject_new(pa_source_output);
    o->parent.parent.free = source_output_free;
    o->parent.process_msg = pa_source_output_process_msg;

    o->core = core;
    o->state = PA_SOURCE_OUTPUT_INIT;
    o->flags = data->flags;
    o->proplist = pa_proplist_copy(data->proplist);
    o->driver = pa_xstrdup(pa_path_get_filename(data->driver));
    o->module = data->module;
    o->source = data->source;
    o->destination_source = data->destination_source;
    o->client = data->client;

    o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
    o->requested_resample_method = data->resample_method;
    o->sample_spec = data->sample_spec;
    o->channel_map = data->channel_map;

    o->direct_on_input = data->direct_on_input;

    o->save_source = data->save_source;

    reset_callbacks(o);
    o->userdata = NULL;

    o->thread_info.state = o->state;
    o->thread_info.attached = FALSE;
    o->thread_info.sample_spec = o->sample_spec;
    o->thread_info.resampler = resampler;
    o->thread_info.requested_source_latency = (pa_usec_t) -1;
    o->thread_info.direct_on_input = o->direct_on_input;

    o->thread_info.delay_memblockq = pa_memblockq_new(
            0,
            MEMBLOCKQ_MAXLENGTH,
            0,
            pa_frame_size(&o->source->sample_spec),
            0,
            1,
            0,
            &o->source->silence);

    pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
    pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);

    if (o->client)
        pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0);

    if (o->direct_on_input)
        pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);

    pt = pa_proplist_to_string_sep(o->proplist, "\n    ");
    pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s\n    %s",
                o->index,
                pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
                o->source->name,
                pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
                pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
                pt);
    pa_xfree(pt);

    /* Don't forget to call pa_source_output_put! */

    *_o = o;
    return 0;
}
示例#13
0
文件: core.c 项目: morphis/pulseaudio
pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size) {
    pa_core* c;
    pa_mempool *pool;
    int j;

    pa_assert(m);

    if (shared) {
        if (!(pool = pa_mempool_new(shared, shm_size))) {
            pa_log_warn("Failed to allocate shared memory pool. Falling back to a normal memory pool.");
            shared = false;
        }
    }

    if (!shared) {
        if (!(pool = pa_mempool_new(shared, shm_size))) {
            pa_log("pa_mempool_new() failed.");
            return NULL;
        }
    }

    c = pa_msgobject_new(pa_core);
    c->parent.parent.free = core_free;
    c->parent.process_msg = core_process_msg;

    c->state = PA_CORE_STARTUP;
    c->mainloop = m;

    c->clients = pa_idxset_new(NULL, NULL);
    c->cards = pa_idxset_new(NULL, NULL);
    c->sinks = pa_idxset_new(NULL, NULL);
    c->sources = pa_idxset_new(NULL, NULL);
    c->sink_inputs = pa_idxset_new(NULL, NULL);
    c->source_outputs = pa_idxset_new(NULL, NULL);
    c->modules = pa_idxset_new(NULL, NULL);
    c->scache = pa_idxset_new(NULL, NULL);

    c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
    c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);

    c->default_source = NULL;
    c->default_sink = NULL;

    c->default_sample_spec.format = PA_SAMPLE_S16NE;
    c->default_sample_spec.rate = 44100;
    c->default_sample_spec.channels = 2;
    pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
    c->default_n_fragments = 4;
    c->default_fragment_size_msec = 25;

    c->deferred_volume_safety_margin_usec = 8000;
    c->deferred_volume_extra_delay_usec = 0;

    c->module_defer_unload_event = NULL;
    c->modules_pending_unload = pa_hashmap_new(NULL, NULL);

    c->subscription_defer_event = NULL;
    PA_LLIST_HEAD_INIT(pa_subscription, c->subscriptions);
    PA_LLIST_HEAD_INIT(pa_subscription_event, c->subscription_event_queue);
    c->subscription_event_last = NULL;

    c->mempool = pool;
    pa_silence_cache_init(&c->silence_cache);

    if (shared && !(c->rw_mempool = pa_mempool_new(shared, shm_size)))
        pa_log_warn("Failed to allocate shared writable memory pool.");
    if (c->rw_mempool)
        pa_mempool_set_is_remote_writable(c->rw_mempool, true);

    c->exit_event = NULL;
    c->scache_auto_unload_event = NULL;

    c->exit_idle_time = -1;
    c->scache_idle_time = 20;

    c->flat_volumes = true;
    c->disallow_module_loading = false;
    c->disallow_exit = false;
    c->running_as_daemon = false;
    c->realtime_scheduling = false;
    c->realtime_priority = 5;
    c->disable_remixing = false;
    c->disable_lfe_remixing = false;
    c->lfe_crossover_freq = 120;
    c->deferred_volume = true;
    c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;

    for (j = 0; j < PA_CORE_HOOK_MAX; j++)
        pa_hook_init(&c->hooks[j], c);

    for (j = 0; j < PA_ACCESS_HOOK_MAX; j++)
        pa_hook_init(&c->access[j], c);

    pa_random(&c->cookie, sizeof(c->cookie));

#ifdef SIGPIPE
    pa_check_signal_is_blocked(SIGPIPE);
#endif

    pa_core_check_idle(c);

    c->state = PA_CORE_RUNNING;

    return c;
}