Exemplo n.º 1
0
static void destroy_pa(struct SoundIoPrivate *si) {
    struct SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;

    if (sipa->main_loop)
        pa_threaded_mainloop_stop(sipa->main_loop);

    pa_context_disconnect(sipa->pulse_context);
    pa_context_unref(sipa->pulse_context);

    soundio_destroy_devices_info(sipa->current_devices_info);
    soundio_destroy_devices_info(sipa->ready_devices_info);

    if (sipa->main_loop)
        pa_threaded_mainloop_free(sipa->main_loop);

    if (sipa->props)
        pa_proplist_free(sipa->props);

    free(sipa->default_sink_name);
    free(sipa->default_source_name);
}
Exemplo n.º 2
0
// always called even when refresh_devices succeeds
static void cleanup_refresh_devices(struct SoundIoPrivate *si) {
    struct SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;

    soundio_destroy_devices_info(sipa->current_devices_info);
    sipa->current_devices_info = NULL;

    free(sipa->default_sink_name);
    sipa->default_sink_name = NULL;

    free(sipa->default_source_name);
    sipa->default_source_name = NULL;
}
Exemplo n.º 3
0
static void my_flush_events(struct SoundIoPrivate *si, bool wait) {
    struct SoundIo *soundio = &si->pub;
    struct SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;

    bool change = false;
    bool cb_shutdown = false;
    struct SoundIoDevicesInfo *old_devices_info = NULL;

    pa_threaded_mainloop_lock(sipa->main_loop);

    if (wait)
        pa_threaded_mainloop_wait(sipa->main_loop);

    if (sipa->device_scan_queued && !sipa->connection_err) {
        sipa->device_scan_queued = false;
        sipa->connection_err = refresh_devices(si);
        cleanup_refresh_devices(si);
    }

    if (sipa->connection_err && !sipa->emitted_shutdown_cb) {
        sipa->emitted_shutdown_cb = true;
        cb_shutdown = true;
    } else if (sipa->ready_devices_info) {
        old_devices_info = si->safe_devices_info;
        si->safe_devices_info = sipa->ready_devices_info;
        sipa->ready_devices_info = NULL;
        change = true;
    }

    pa_threaded_mainloop_unlock(sipa->main_loop);

    if (cb_shutdown)
        soundio->on_backend_disconnect(soundio, sipa->connection_err);
    else if (change)
        soundio->on_devices_change(soundio);

    soundio_destroy_devices_info(old_devices_info);
}
Exemplo n.º 4
0
void soundio_disconnect(struct SoundIo *soundio) {
    SoundIoPrivate *si = (SoundIoPrivate *)soundio;

    if (!si)
        return;

    if (si->destroy)
        si->destroy(si);
    memset(&si->backend_data, 0, sizeof(SoundIoBackendData));

    soundio->current_backend = SoundIoBackendNone;

    soundio_destroy_devices_info(si->safe_devices_info);
    si->safe_devices_info = nullptr;

    si->destroy = nullptr;
    si->flush_events = nullptr;
    si->wait_events = nullptr;
    si->wakeup = nullptr;
    si->force_device_scan = nullptr;

    si->outstream_open = nullptr;
    si->outstream_destroy = nullptr;
    si->outstream_start = nullptr;
    si->outstream_begin_write = nullptr;
    si->outstream_end_write = nullptr;
    si->outstream_clear_buffer = nullptr;
    si->outstream_pause = nullptr;
    si->outstream_get_latency = nullptr;

    si->instream_open = nullptr;
    si->instream_destroy = nullptr;
    si->instream_start = nullptr;
    si->instream_begin_read = nullptr;
    si->instream_end_read = nullptr;
    si->instream_pause = nullptr;
    si->instream_get_latency = nullptr;
}
Exemplo n.º 5
0
static int refresh_devices_bare(SoundIoPrivate *si) {
    SoundIo *soundio = &si->pub;
    SoundIoJack *sij = &si->backend_data.jack;

    if (sij->is_shutdown)
        return SoundIoErrorBackendDisconnected;


    SoundIoDevicesInfo *devices_info = allocate<SoundIoDevicesInfo>(1);
    if (!devices_info)
        return SoundIoErrorNoMem;

    devices_info->default_output_index = -1;
    devices_info->default_input_index = -1;
    const char **port_names = jack_get_ports(sij->client, nullptr, nullptr, 0);
    if (!port_names) {
        soundio_destroy_devices_info(devices_info);
        return SoundIoErrorNoMem;
    }

    SoundIoList<SoundIoJackClient> clients = {0};
    const char **port_name_ptr = port_names;
    for (; *port_name_ptr; port_name_ptr += 1) {
        const char *client_and_port_name = *port_name_ptr;
        int client_and_port_name_len = strlen(client_and_port_name);
        jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name);
        if (!jport) {
            // This refresh devices scan is already outdated. Just give up and
            // let refresh_devices be called again.
            jack_free(port_names);
            soundio_destroy_devices_info(devices_info);
            return SoundIoErrorInterrupted;
        }

        int flags = jack_port_flags(jport);
        const char *port_type = jack_port_type(jport);
        if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) != 0) {
            // we don't know how to support such a port
            continue;
        }

        SoundIoDeviceAim aim = (flags & JackPortIsInput) ?
            SoundIoDeviceAimOutput : SoundIoDeviceAimInput;
        bool is_physical = flags & JackPortIsPhysical;

        const char *client_name = nullptr;
        const char *port_name = nullptr;
        int client_name_len;
        int port_name_len;
        split_str(client_and_port_name, client_and_port_name_len, ':',
                &client_name, &client_name_len, &port_name, &port_name_len);
        if (!client_name || !port_name) {
            // device does not have colon, skip it
            continue;
        }
        SoundIoJackClient *client = find_or_create_client(&clients, aim, is_physical,
                client_name, client_name_len);
        if (!client) {
            jack_free(port_names);
            soundio_destroy_devices_info(devices_info);
            return SoundIoErrorNoMem;
        }
        if (client->port_count >= SOUNDIO_MAX_CHANNELS) {
            // we hit the channel limit, skip the leftovers
            continue;
        }
        SoundIoJackPort *port = &client->ports[client->port_count++];
        port->full_name = client_and_port_name;
        port->full_name_len = client_and_port_name_len;
        port->name = port_name;
        port->name_len = port_name_len;
        port->channel_id = soundio_parse_channel_id(port_name, port_name_len);

        jack_latency_callback_mode_t latency_mode = (aim == SoundIoDeviceAimOutput) ?
            JackPlaybackLatency : JackCaptureLatency;
        jack_port_get_latency_range(jport, latency_mode, &port->latency_range);
    }

    for (int i = 0; i < clients.length; i += 1) {
        SoundIoJackClient *client = &clients.at(i);
        if (client->port_count <= 0)
            continue;

        SoundIoDevicePrivate *dev = allocate<SoundIoDevicePrivate>(1);
        if (!dev) {
            jack_free(port_names);
            soundio_destroy_devices_info(devices_info);
            return SoundIoErrorNoMem;
        }
        SoundIoDevice *device = &dev->pub;
        SoundIoDeviceJack *dj = &dev->backend_data.jack;
        int description_len = client->name_len + 3 + 2 * client->port_count;
        for (int port_index = 0; port_index < client->port_count; port_index += 1) {
            SoundIoJackPort *port = &client->ports[port_index];

            description_len += port->name_len;
        }

        dev->destruct = destruct_device;

        device->ref_count = 1;
        device->soundio = soundio;
        device->is_raw = false;
        device->aim = client->aim;
        device->id = soundio_str_dupe(client->name, client->name_len);
        device->name = allocate<char>(description_len);
        device->current_format = SoundIoFormatFloat32NE;
        device->sample_rate_count = 1;
        device->sample_rates = &dev->prealloc_sample_rate_range;
        device->sample_rates[0].min = sij->sample_rate;
        device->sample_rates[0].max = sij->sample_rate;
        device->sample_rate_current = sij->sample_rate;

        device->software_latency_current = sij->period_size / (double) sij->sample_rate;
        device->software_latency_min = sij->period_size / (double) sij->sample_rate;
        device->software_latency_max = sij->period_size / (double) sij->sample_rate;

        dj->port_count = client->port_count;
        dj->ports = allocate<SoundIoDeviceJackPort>(dj->port_count);

        if (!device->id || !device->name || !dj->ports) {
            jack_free(port_names);
            soundio_device_unref(device);
            soundio_destroy_devices_info(devices_info);
            return SoundIoErrorNoMem;
        }

        for (int port_index = 0; port_index < client->port_count; port_index += 1) {
            SoundIoJackPort *port = &client->ports[port_index];
            SoundIoDeviceJackPort *djp = &dj->ports[port_index];
            djp->full_name = soundio_str_dupe(port->full_name, port->full_name_len);
            djp->full_name_len = port->full_name_len;
            djp->channel_id = port->channel_id;
            djp->latency_range = port->latency_range;

            if (!djp->full_name) {
                jack_free(port_names);
                soundio_device_unref(device);
                soundio_destroy_devices_info(devices_info);
                return SoundIoErrorNoMem;
            }
        }

        memcpy(device->name, client->name, client->name_len);
        memcpy(&device->name[client->name_len], ": ", 2);
        int index = client->name_len + 2;
        for (int port_index = 0; port_index < client->port_count; port_index += 1) {
            SoundIoJackPort *port = &client->ports[port_index];
            memcpy(&device->name[index], port->name, port->name_len);
            index += port->name_len;
            if (port_index + 1 < client->port_count) {
                memcpy(&device->name[index], ", ", 2);
                index += 2;
            }
        }

        device->current_layout.channel_count = client->port_count;
        bool any_invalid = false;
        for (int port_index = 0; port_index < client->port_count; port_index += 1) {
            SoundIoJackPort *port = &client->ports[port_index];
            device->current_layout.channels[port_index] = port->channel_id;
            any_invalid = any_invalid || (port->channel_id == SoundIoChannelIdInvalid);
        }
        if (any_invalid) {
            const struct SoundIoChannelLayout *layout = soundio_channel_layout_get_default(client->port_count);
            if (layout)
                device->current_layout = *layout;
        } else {
            soundio_channel_layout_detect_builtin(&device->current_layout);
        }

        device->layout_count = 1;
        device->layouts = &device->current_layout;
        device->format_count = 1;
        device->formats = &dev->prealloc_format;
        device->formats[0] = device->current_format;

        SoundIoList<SoundIoDevice *> *device_list;
        if (device->aim == SoundIoDeviceAimOutput) {
            device_list = &devices_info->output_devices;
            if (devices_info->default_output_index < 0 && client->is_physical)
                devices_info->default_output_index = device_list->length;
        } else {
            assert(device->aim == SoundIoDeviceAimInput);
            device_list = &devices_info->input_devices;
            if (devices_info->default_input_index < 0 && client->is_physical)
                devices_info->default_input_index = device_list->length;
        }

        if (device_list->append(device)) {
            soundio_device_unref(device);
            soundio_destroy_devices_info(devices_info);
            return SoundIoErrorNoMem;
        }

    }
    jack_free(port_names);

    soundio_destroy_devices_info(si->safe_devices_info);
    si->safe_devices_info = devices_info;

    return 0;
}
Exemplo n.º 6
0
// call this while holding the main loop lock
static int refresh_devices(struct SoundIoPrivate *si) {
    struct SoundIo *soundio = &si->pub;
    struct SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;

    assert(!sipa->current_devices_info);
    sipa->current_devices_info = ALLOCATE(struct SoundIoDevicesInfo, 1);
    if (!sipa->current_devices_info)
        return SoundIoErrorNoMem;

    pa_operation *list_sink_op = pa_context_get_sink_info_list(sipa->pulse_context, sink_info_callback, si);
    pa_operation *list_source_op = pa_context_get_source_info_list(sipa->pulse_context, source_info_callback, si);
    pa_operation *server_info_op = pa_context_get_server_info(sipa->pulse_context, server_info_callback, si);

    int err;
    if ((err = perform_operation(si, list_sink_op))) {
        return err;
    }
    if ((err = perform_operation(si, list_source_op))) {
        return err;
    }
    if ((err = perform_operation(si, server_info_op))) {
        return err;
    }

    if (sipa->device_query_err) {
        return sipa->device_query_err;
    }

    // based on the default sink name, figure out the default output index
    // if the name doesn't match just pick the first one. if there are no
    // devices then we need to set it to -1.
    sipa->current_devices_info->default_output_index = -1;
    sipa->current_devices_info->default_input_index = -1;

    if (sipa->current_devices_info->input_devices.length > 0) {
        sipa->current_devices_info->default_input_index = 0;
        for (int i = 0; i < sipa->current_devices_info->input_devices.length; i += 1) {
            struct SoundIoDevice *device = SoundIoListDevicePtr_val_at(
                    &sipa->current_devices_info->input_devices, i);

            assert(device->aim == SoundIoDeviceAimInput);
            if (strcmp(device->id, sipa->default_source_name) == 0) {
                sipa->current_devices_info->default_input_index = i;
            }
        }
    }

    if (sipa->current_devices_info->output_devices.length > 0) {
        sipa->current_devices_info->default_output_index = 0;
        for (int i = 0; i < sipa->current_devices_info->output_devices.length; i += 1) {
            struct SoundIoDevice *device = SoundIoListDevicePtr_val_at(
                    &sipa->current_devices_info->output_devices, i);

            assert(device->aim == SoundIoDeviceAimOutput);
            if (strcmp(device->id, sipa->default_sink_name) == 0) {
                sipa->current_devices_info->default_output_index = i;
            }
        }
    }

    soundio_destroy_devices_info(sipa->ready_devices_info);
    sipa->ready_devices_info = sipa->current_devices_info;
    sipa->current_devices_info = NULL;
    pa_threaded_mainloop_signal(sipa->main_loop, 0);
    soundio->on_events_signal(soundio);

    return 0;
}