Пример #1
0
void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
    if (!devices_info)
        return;

    for (int i = 0; i < devices_info->input_devices.length; i += 1)
        soundio_device_unref(devices_info->input_devices.at(i));
    for (int i = 0; i < devices_info->output_devices.length; i += 1)
        soundio_device_unref(devices_info->output_devices.at(i));

    devices_info->input_devices.deinit();
    devices_info->output_devices.deinit();

    free(devices_info);
}
Пример #2
0
static int init_playback_node(AudioGraph *ag) {
    MixerLine *master_mixer_line = ag->project->mixer_line_list.at(0);
    Effect *first_effect = master_mixer_line->effects.at(0);

    assert(first_effect->effect_type == EffectTypeSend);
    EffectSend *effect_send = &first_effect->effect.send;

    assert(effect_send->send_type == EffectSendTypeDevice);
    EffectSendDevice *send_device = &effect_send->send.device;

    SoundIoDevice *audio_device = get_device_for_id(ag, (DeviceId)send_device->device_id);
    if (!audio_device) {
        return GenesisErrorDeviceNotFound;
    }

    GenesisNodeDescriptor *playback_node_descr;
    int err;
    if ((err = genesis_audio_device_create_node_descriptor(ag->pipeline,
                    audio_device, &playback_node_descr)))
    {
        return err;
    }

    assert(!ag->master_node);
    ag->master_node = ok_mem(genesis_node_descriptor_create_node(playback_node_descr));

    soundio_device_unref(audio_device);

    return 0;
}
Пример #3
0
void soundio_instream_destroy(struct SoundIoInStream *instream) {
    if (!instream)
        return;

    SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
    SoundIo *soundio = instream->device->soundio;
    SoundIoPrivate *si = (SoundIoPrivate *)soundio;

    if (si->instream_destroy)
        si->instream_destroy(si, is);

    soundio_device_unref(instream->device);
    free(is);
}
Пример #4
0
void soundio_outstream_destroy(SoundIoOutStream *outstream) {
    if (!outstream)
        return;

    SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
    SoundIo *soundio = outstream->device->soundio;
    SoundIoPrivate *si = (SoundIoPrivate *)soundio;

    if (si->outstream_destroy)
        si->outstream_destroy(si, os);

    soundio_device_unref(outstream->device);
    free(os);
}
Пример #5
0
static SoundIoDevice *get_device_for_id(AudioGraph *ag, DeviceId device_id) {
    assert(device_id >= 1);
    assert(device_id < device_id_count());
    SettingsFileDeviceId *sf_device_id = &ag->settings_file->device_designations.at(device_id);

    if (sf_device_id->backend == SoundIoBackendNone)
        return genesis_get_default_output_device(ag->pipeline->context);

    SoundIoDevice *device = genesis_find_output_device(ag->pipeline->context,
            sf_device_id->backend, sf_device_id->device_id.raw(), sf_device_id->is_raw);

    if (device) {
        if (device->probe_error) {
            soundio_device_unref(device);
            return genesis_get_default_output_device(ag->pipeline->context);
        }
        return device;
    }

    return genesis_get_default_output_device(ag->pipeline->context);
}
Пример #6
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;
}
Пример #7
0
AudioSoundIo::AudioSoundIo( bool & outSuccessful, Mixer * _mixer ) :
	AudioDevice( tLimit<ch_cnt_t>(
		ConfigManager::inst()->value( "audiosoundio", "channels" ).toInt(), DEFAULT_CHANNELS, SURROUND_CHANNELS ),
								_mixer )
{
	outSuccessful = false;
	m_soundio = NULL;
	m_outstream = NULL;
	m_disconnectErr = 0;
	m_outBufFrameIndex = 0;
	m_outBufFramesTotal = 0;

	m_soundio = soundio_create();
	if (!m_soundio)
	{
		fprintf(stderr, "Unable to initialize soundio: out of memory\n");
		return;
	}

	m_soundio->app_name = "LMMS";
	m_soundio->userdata = this;
	m_soundio->on_backend_disconnect = staticOnBackendDisconnect;

	const QString& configBackend = ConfigManager::inst()->value( "audiosoundio", "backend" );
	const QString& configDeviceId = ConfigManager::inst()->value( "audiosoundio", "out_device_id" );
	const QString& configDeviceRaw = ConfigManager::inst()->value( "audiosoundio", "out_device_raw" );

	int err;
	int outDeviceCount = 0;
	int backendCount = soundio_backend_count(m_soundio);
	for (int i = 0; i < backendCount; i += 1)
	{
		SoundIoBackend backend = soundio_get_backend(m_soundio, i);
		if (configBackend == soundio_backend_name(backend))
		{
			if ((err = soundio_connect_backend(m_soundio, backend)))
			{
				// error occurred, leave outDeviceCount 0
			}
			else
			{
				soundio_flush_events(m_soundio);
				if (m_disconnectErr)
				{
					fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(m_disconnectErr));
					return;
				}
				outDeviceCount = soundio_output_device_count(m_soundio);
			}
			break;
		}
	}

	if (outDeviceCount <= 0)
	{
		// try connecting to the default backend
		if ((err = soundio_connect(m_soundio)))
		{
			fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(err));
			return;
		}

		soundio_flush_events(m_soundio);
		if (m_disconnectErr)
		{
			fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(m_disconnectErr));
			return;
		}

		outDeviceCount = soundio_output_device_count(m_soundio);

		if (outDeviceCount <= 0)
		{
			fprintf(stderr, "Unable to initialize soundio: no devices found\n");
			return;
		}
	}

	int selected_device_index = soundio_default_output_device_index(m_soundio);

	bool wantRaw = (configDeviceRaw == "yes");
	for (int i = 0; i < outDeviceCount; i += 1)
	{
		SoundIoDevice *device = soundio_get_output_device(m_soundio, i);
		bool isThisOne = (configDeviceId == device->id && wantRaw == device->is_raw);
		soundio_device_unref(device);
		if (isThisOne)
		{
			selected_device_index = i;
			break;
		}
	}

	SoundIoDevice *device = soundio_get_output_device(m_soundio, selected_device_index);
	m_outstream = soundio_outstream_create(device);
	soundio_device_unref(device);

	if (!m_outstream)
	{
		fprintf(stderr, "Unable to initialize soundio: out of memory\n");
		return;
	}

	int currentSampleRate = sampleRate();
	int closestSupportedSampleRate = -1;

	for (int i = 0; i < device->sample_rate_count; i += 1)
	{
		SoundIoSampleRateRange *range = &device->sample_rates[i];
		if (range->min <= currentSampleRate && currentSampleRate <= range->max)
		{
			closestSupportedSampleRate = currentSampleRate;
			break;
		}
		if (closestSupportedSampleRate == -1 ||
			abs(range->max - currentSampleRate) < abs(closestSupportedSampleRate - currentSampleRate))
		{
			closestSupportedSampleRate = range->max;
		}
	}

	if (closestSupportedSampleRate != currentSampleRate)
	{
		setSampleRate(closestSupportedSampleRate);
		currentSampleRate = closestSupportedSampleRate;
	}

	m_outstream->name = "LMMS";
	m_outstream->software_latency = (double)mixer()->framesPerPeriod() / (double)currentSampleRate;
	m_outstream->userdata = this;
	m_outstream->write_callback = staticWriteCallback;
	m_outstream->error_callback = staticErrorCallback;
	m_outstream->underflow_callback = staticUnderflowCallback;
	m_outstream->sample_rate = currentSampleRate;
	m_outstream->layout = *soundio_channel_layout_get_default(channels());
	m_outstream->format = SoundIoFormatFloat32NE;

	if ((err = soundio_outstream_open(m_outstream)))
	{
		fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(err));
		return;
	}

	fprintf(stderr, "Output device: '%s' backend: '%s'\n",
			device->name, soundio_backend_name(m_soundio->current_backend));

	outSuccessful = true;
}
Пример #8
0
static void source_info_callback(pa_context *pulse_context, const pa_source_info *info, int eol, void *userdata) {
    struct SoundIoPrivate *si = (struct SoundIoPrivate *)userdata;
    struct SoundIo *soundio = &si->pub;
    struct SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
    int err;

    if (eol) {
        pa_threaded_mainloop_signal(sipa->main_loop, 0);
        return;
    }
    if (sipa->device_query_err)
        return;

    struct SoundIoDevicePrivate *dev = ALLOCATE(struct SoundIoDevicePrivate, 1);
    if (!dev) {
        sipa->device_query_err = SoundIoErrorNoMem;
        return;
    }
    struct SoundIoDevice *device = &dev->pub;

    device->ref_count = 1;
    device->soundio = soundio;
    device->id = strdup(info->name);
    device->name = strdup(info->description);
    if (!device->id || !device->name) {
        soundio_device_unref(device);
        sipa->device_query_err = SoundIoErrorNoMem;
        return;
    }

    device->sample_rate_current = info->sample_spec.rate;
    // PulseAudio performs resampling, so any value is valid. Let's pick
    // some reasonable min and max values.
    device->sample_rate_count = 1;
    device->sample_rates = &dev->prealloc_sample_rate_range;
    device->sample_rates[0].min = soundio_int_min(SOUNDIO_MIN_SAMPLE_RATE, device->sample_rate_current);
    device->sample_rates[0].max = soundio_int_max(SOUNDIO_MAX_SAMPLE_RATE, device->sample_rate_current);

    device->current_format = from_pulseaudio_format(info->sample_spec);
    // PulseAudio performs sample format conversion, so any PulseAudio
    // value is valid.
    if ((err = set_all_device_formats(device))) {
        soundio_device_unref(device);
        sipa->device_query_err = SoundIoErrorNoMem;
        return;
    }

    set_from_pulseaudio_channel_map(info->channel_map, &device->current_layout);
    // PulseAudio does channel layout remapping, so any channel layout is valid.
    if ((err = set_all_device_channel_layouts(device))) {
        soundio_device_unref(device);
        sipa->device_query_err = SoundIoErrorNoMem;
        return;
    }

    device->aim = SoundIoDeviceAimInput;

    if (SoundIoListDevicePtr_append(&sipa->current_devices_info->input_devices, device)) {
        soundio_device_unref(device);
        sipa->device_query_err = SoundIoErrorNoMem;
        return;
    }
}
Пример #9
0
int main(int argc, char **argv) {
    char *exe = argv[0];
    enum SoundIoBackend backend = SoundIoBackendNone;
    bool is_raw = false;
    char *device_id = NULL;
    for (int i = 1; i < argc; i += 1) {
        char *arg = argv[i];
        if (arg[0] == '-' && arg[1] == '-') {
            if (strcmp(arg, "--raw") == 0) {
                is_raw = true;
            } else if (++i >= argc) {
                return usage(exe);
            } else if (strcmp(arg, "--device") == 0) {
                device_id = argv[i];
            } else if (strcmp(arg, "--backend") == 0) {
                if (strcmp("dummy", argv[i]) == 0) {
                    backend = SoundIoBackendDummy;
                } else if (strcmp("alsa", argv[i]) == 0) {
                    backend = SoundIoBackendAlsa;
                } else if (strcmp("pulseaudio", argv[i]) == 0) {
                    backend = SoundIoBackendPulseAudio;
                } else if (strcmp("jack", argv[i]) == 0) {
                    backend = SoundIoBackendJack;
                } else if (strcmp("coreaudio", argv[i]) == 0) {
                    backend = SoundIoBackendCoreAudio;
                } else if (strcmp("wasapi", argv[i]) == 0) {
                    backend = SoundIoBackendWasapi;
                } else {
                    fprintf(stderr, "Invalid backend: %s\n", argv[i]);
                    return 1;
                }
            } else {
                return usage(exe);
            }
        } else {
            return usage(exe);
        }
    }

    fprintf(stderr,
            "Records for 3 seconds, sleeps for 3 seconds, then you should see at least\n"
            "one buffer overflow message, then records for 3 seconds.\n"
            "PulseAudio is not expected to pass this test.\n"
            "CoreAudio is not expected to pass this test.\n"
            "WASAPI is not expected to pass this test.\n");

    if (!(soundio = soundio_create()))
        panic("out of memory");

    int err = (backend == SoundIoBackendNone) ?
        soundio_connect(soundio) : soundio_connect_backend(soundio, backend);

    if (err)
        panic("error connecting: %s", soundio_strerror(err));

    soundio_flush_events(soundio);

    int selected_device_index = -1;
    if (device_id) {
        int device_count = soundio_input_device_count(soundio);
        for (int i = 0; i < device_count; i += 1) {
            struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
            if (strcmp(device->id, device_id) == 0 && device->is_raw == is_raw) {
                selected_device_index = i;
                break;
            }
        }
    } else {
        selected_device_index = soundio_default_input_device_index(soundio);
    }

    if (selected_device_index < 0) {
        fprintf(stderr, "input device not found\n");
        return 1;
    }

    struct SoundIoDevice *device = soundio_get_input_device(soundio, selected_device_index);
    if (!device) {
        fprintf(stderr, "out of memory\n");
        return 1;
    }

    fprintf(stderr, "Input device: %s\n", device->name);

    enum SoundIoFormat *fmt;
    for (fmt = prioritized_formats; *fmt != SoundIoFormatInvalid; fmt += 1) {
        if (soundio_device_supports_format(device, *fmt))
            break;
    }
    if (*fmt == SoundIoFormatInvalid)
        panic("incompatible sample format");

    struct SoundIoInStream *instream = soundio_instream_create(device);
    instream->format = *fmt;
    instream->read_callback = read_callback;
    instream->overflow_callback = overflow_callback;

    if ((err = soundio_instream_open(instream)))
        panic("unable to open device: %s", soundio_strerror(err));

    fprintf(stderr, "OK format: %s\n", soundio_format_string(instream->format));

    if ((err = soundio_instream_start(instream)))
        panic("unable to start device: %s", soundio_strerror(err));

    while (seconds_offset < seconds_end)
        soundio_wait_events(soundio);

    soundio_instream_destroy(instream);
    soundio_device_unref(device);
    soundio_destroy(soundio);

    if (overflow_count > 0) {
        fprintf(stderr, "OK test passed with %d overflow callbacks\n", overflow_count);
        return 0;
    } else {
        fprintf(stderr, "FAIL no overflow callbacks received\n");
        return 1;
    }
}
Пример #10
0
//==============================================================================
//   MAIN
//==============================================================================
int main(int argc,char **argv)
{
	pthread_t test;

	printf("SFemtoZ!\n");

	int c;
	while ((c = getopt (argc, argv, "t:v")) != -1)
    		switch (c)
		{
			case 't':
				if(pthread_create(&test,NULL,thread_test,optarg))
				{
					printf("error thread test\n");
				}
			break;

			case 'v':
				verbose=TRUE;
			break;


		}

	char sfzfile[50];
	strcpy(sfzfile,USBPATH);
	strcat(sfzfile,argv[optind]);
	printf("Load SFZ: %s\n",sfzfile);
	loadsfz(sfzfile);
	//printsfz();
	loadsounds();

	int fd;
	if(( fd = serialOpen("/dev/ttyAMA0",57600))<0)
	{
		printf("serialOpen ERROR:\n",strerror(errno));
		return 1;
	}
	if(configuresoundio()!=0) return 1;

	signal(SIGINT,exit_cli);
	signal(SIGTERM,exit_cli);

	run = TRUE;

	while(run)
	{
		if(serialDataAvail(fd)>2)
		{
			int type=serialGetchar(fd);
			while(type!=0x99 && type!= 0xB9) type=serialGetchar(fd);

			int note=serialGetchar(fd);
			int vel=serialGetchar(fd);
			//printf("MIDI%i(%i,%i)\n",type,note,vel);
			if(type==0x99) noteOn(note,vel);
			else midiCC(note,vel);
		}

//		soundio_wait_events(soundio);
	}

	//FINISH
	pthread_cancel(test);

	soundio_outstream_destroy(outstream);
	soundio_device_unref(device);
	soundio_destroy(soundio);

	serialClose(fd);

	freesounds();
	freesfz();

	return 0;
}
Пример #11
0
int main(int argc, char **argv) {
    char *exe = argv[0];
    enum SoundIoBackend backend = SoundIoBackendNone;
    char *device_id = NULL;
    bool raw = false;
    char *stream_name = NULL;
    double latency = 0.0;
    int sample_rate = 0;
    for (int i = 1; i < argc; i += 1) {
        char *arg = argv[i];
        if (arg[0] == '-' && arg[1] == '-') {
            if (strcmp(arg, "--raw") == 0) {
                raw = true;
            } else {
                i += 1;
                if (i >= argc) {
                    return usage(exe);
                } else if (strcmp(arg, "--backend") == 0) {
                    if (strcmp(argv[i], "dummy") == 0) {
                        backend = SoundIoBackendDummy;
                    } else if (strcmp(argv[i], "alsa") == 0) {
                        backend = SoundIoBackendAlsa;
                    } else if (strcmp(argv[i], "pulseaudio") == 0) {
                        backend = SoundIoBackendPulseAudio;
                    } else if (strcmp(argv[i], "jack") == 0) {
                        backend = SoundIoBackendJack;
                    } else if (strcmp(argv[i], "coreaudio") == 0) {
                        backend = SoundIoBackendCoreAudio;
                    } else if (strcmp(argv[i], "wasapi") == 0) {
                        backend = SoundIoBackendWasapi;
                    } else {
                        fprintf(stderr, "Invalid backend: %s\n", argv[i]);
                        return 1;
                    }
                } else if (strcmp(arg, "--device") == 0) {
                    device_id = argv[i];
                } else if (strcmp(arg, "--name") == 0) {
                    stream_name = argv[i];
                } else if (strcmp(arg, "--latency") == 0) {
                    latency = atof(argv[i]);
                } else if (strcmp(arg, "--sample-rate") == 0) {
                    sample_rate = atoi(argv[i]);
                } else {
                    return usage(exe);
                }
            }
        } else {
            return usage(exe);
        }
    }

    struct SoundIo *soundio = soundio_create();
    if (!soundio) {
        fprintf(stderr, "out of memory\n");
        return 1;
    }

    int err = (backend == SoundIoBackendNone) ?
        soundio_connect(soundio) : soundio_connect_backend(soundio, backend);

    if (err) {
        fprintf(stderr, "Unable to connect to backend: %s\n", soundio_strerror(err));
        return 1;
    }

    fprintf(stderr, "Backend: %s\n", soundio_backend_name(soundio->current_backend));

    soundio_flush_events(soundio);

    int selected_device_index = -1;
    if (device_id) {
        int device_count = soundio_output_device_count(soundio);
        for (int i = 0; i < device_count; i += 1) {
            struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
            bool select_this_one = strcmp(device->id, device_id) == 0 && device->is_raw == raw;
            soundio_device_unref(device);
            if (select_this_one) {
                selected_device_index = i;
                break;
            }
        }
    } else {
        selected_device_index = soundio_default_output_device_index(soundio);
    }

    if (selected_device_index < 0) {
        fprintf(stderr, "Output device not found\n");
        return 1;
    }

    struct SoundIoDevice *device = soundio_get_output_device(soundio, selected_device_index);
    if (!device) {
        fprintf(stderr, "out of memory\n");
        return 1;
    }

    fprintf(stderr, "Output device: %s\n", device->name);

    if (device->probe_error) {
        fprintf(stderr, "Cannot probe device: %s\n", soundio_strerror(device->probe_error));
        return 1;
    }

    struct SoundIoOutStream *outstream = soundio_outstream_create(device);
    if (!outstream) {
        fprintf(stderr, "out of memory\n");
        return 1;
    }

    outstream->write_callback = write_callback;
    outstream->underflow_callback = underflow_callback;
    outstream->name = stream_name;
    outstream->software_latency = latency;
    outstream->sample_rate = sample_rate;

    if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) {
        outstream->format = SoundIoFormatFloat32NE;
        write_sample = write_sample_float32ne;
    } else if (soundio_device_supports_format(device, SoundIoFormatFloat64NE)) {
        outstream->format = SoundIoFormatFloat64NE;
        write_sample = write_sample_float64ne;
    } else if (soundio_device_supports_format(device, SoundIoFormatS32NE)) {
        outstream->format = SoundIoFormatS32NE;
        write_sample = write_sample_s32ne;
    } else if (soundio_device_supports_format(device, SoundIoFormatS16NE)) {
        outstream->format = SoundIoFormatS16NE;
        write_sample = write_sample_s16ne;
    } else {
        fprintf(stderr, "No suitable device format available.\n");
        return 1;
    }

    if ((err = soundio_outstream_open(outstream))) {
        fprintf(stderr, "unable to open device: %s", soundio_strerror(err));
        return 1;
    }

    fprintf(stderr, "Software latency: %f\n", outstream->software_latency);
    fprintf(stderr,
            "'p\\n' - pause\n"
            "'u\\n' - unpause\n"
            "'P\\n' - pause from within callback\n"
            "'c\\n' - clear buffer\n"
            "'q\\n' - quit\n");

    if (outstream->layout_error)
        fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));

    if ((err = soundio_outstream_start(outstream))) {
        fprintf(stderr, "unable to start device: %s\n", soundio_strerror(err));
        return 1;
    }

    for (;;) {
        soundio_flush_events(soundio);
        int c = getc(stdin);
        if (c == 'p') {
            fprintf(stderr, "pausing result: %s\n",
                    soundio_strerror(soundio_outstream_pause(outstream, true)));
        } else if (c == 'P') {
            want_pause = true;
        } else if (c == 'u') {
            want_pause = false;
            fprintf(stderr, "unpausing result: %s\n",
                    soundio_strerror(soundio_outstream_pause(outstream, false)));
        } else if (c == 'c') {
            fprintf(stderr, "clear buffer result: %s\n",
                    soundio_strerror(soundio_outstream_clear_buffer(outstream)));
        } else if (c == 'q') {
            break;
        } else if (c == '\r' || c == '\n') {
            // ignore
        } else {
            fprintf(stderr, "Unrecognized command: %c\n", c);
        }
    }

    soundio_outstream_destroy(outstream);
    soundio_device_unref(device);
    soundio_destroy(soundio);
    return 0;
}