/* * Copyright (c) 2015 Andrew Kelley * * This file is part of libsoundio, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include #include #include #include #include #include static enum SoundIoFormat prioritized_formats[] = { SoundIoFormatFloat32NE, SoundIoFormatFloat32FE, SoundIoFormatS32NE, SoundIoFormatS32FE, SoundIoFormatS24NE, SoundIoFormatS24FE, SoundIoFormatS16NE, SoundIoFormatS16FE, SoundIoFormatFloat64NE, SoundIoFormatFloat64FE, SoundIoFormatU32NE, SoundIoFormatU32FE, SoundIoFormatU24NE, SoundIoFormatU24FE, SoundIoFormatU16NE, SoundIoFormatU16FE, SoundIoFormatS8, SoundIoFormatU8, SoundIoFormatInvalid, }; static int prioritized_sample_rates[] = { 48000, 44100, 96000, 24000, 0, }; static FILE *out_f = NULL; static void read_callback(struct SoundIoInStream *instream, int frame_count_min, int frame_count_max) { struct SoundIoChannelArea *areas; int err; int frames_left = frame_count_max; for (;;) { int frame_count = frames_left; if ((err = soundio_instream_begin_read(instream, &areas, &frame_count))) { fprintf(stderr, "begin read error: %s", soundio_strerror(err)); exit(1); } if (!frame_count) break; if (!areas) { fprintf(stderr, "hole\n"); exit(1); } else { for (int frame = 0; frame < frame_count; frame += 1) { for (int ch = 0; ch < instream->layout.channel_count; ch += 1) { fwrite(areas[ch].ptr, 1, instream->bytes_per_sample, out_f); areas[ch].ptr += areas[ch].step; } } } if ((err = soundio_instream_end_read(instream))) { fprintf(stderr, "end read error: %s", soundio_strerror(err)); exit(1); } frames_left -= frame_count; if (frames_left <= 0) break; } } static void overflow_callback(struct SoundIoInStream *instream) { static int count = 0; fprintf(stderr, "overflow %d\n", ++count); } static int usage(char *exe) { fprintf(stderr, "Usage: %s [options] outfile.wav\n" "Options:\n" " [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n" " [--device id]\n" " [--raw]\n" , exe); return 1; } int main(int argc, char **argv) { char *exe = argv[0]; enum SoundIoBackend backend = SoundIoBackendNone; char *device_id = NULL; bool is_raw = false; char *outfile = 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, "--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 if (strcmp(arg, "--device") == 0) { device_id = argv[i]; } else { return usage(exe); } } else if (!outfile) { outfile = argv[i]; } else { return usage(exe); } } if (!outfile) 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, "error connecting: %s", soundio_strerror(err)); return 1; } soundio_flush_events(soundio); struct SoundIoDevice *selected_device = NULL; if (device_id) { for (int i = 0; i < soundio_input_device_count(soundio); i += 1) { struct SoundIoDevice *device = soundio_get_input_device(soundio, i); if (device->is_raw == is_raw && strcmp(device->id, device_id) == 0) { selected_device = device; break; } soundio_device_unref(device); } if (!selected_device) { fprintf(stderr, "Invalid device id: %s\n", device_id); return 1; } } else { int device_index = soundio_default_input_device_index(soundio); selected_device = soundio_get_input_device(soundio, device_index); } fprintf(stderr, "Device: %s\n", selected_device->name); soundio_device_sort_channel_layouts(selected_device); struct SoundIoChannelLayout *layout = &selected_device->layouts[0]; int sample_rate = 0; int *sample_rate_ptr; for (sample_rate_ptr = prioritized_sample_rates; *sample_rate_ptr; sample_rate_ptr += 1) { if (soundio_device_supports_sample_rate(selected_device, *sample_rate_ptr)) { sample_rate = *sample_rate_ptr; break; } } if (!sample_rate) sample_rate = selected_device->sample_rates[0].max; enum SoundIoFormat fmt = SoundIoFormatInvalid; enum SoundIoFormat *fmt_ptr; for (fmt_ptr = prioritized_formats; *fmt_ptr != SoundIoFormatInvalid; fmt_ptr += 1) { if (soundio_device_supports_format(selected_device, *fmt_ptr)) { fmt = *fmt_ptr; break; } } if (fmt == SoundIoFormatInvalid) fmt = selected_device->formats[0]; fprintf(stderr, "%s %dHz %s interleaved\n", layout->name, sample_rate, soundio_format_string(fmt)); out_f = fopen(outfile, "wb"); if (!out_f) { fprintf(stderr, "unable to open %s: %s\n", outfile, strerror(errno)); return 1; } struct SoundIoInStream *instream = soundio_instream_create(selected_device); if (!instream) { fprintf(stderr, "out of memory\n"); return 1; } instream->format = fmt; instream->sample_rate = sample_rate; instream->layout = *layout; instream->read_callback = read_callback; instream->overflow_callback = overflow_callback; if ((err = soundio_instream_open(instream))) { fprintf(stderr, "unable to open input stream: %s", soundio_strerror(err)); return 1; } if ((err = soundio_instream_start(instream))) { fprintf(stderr, "unable to start input device: %s", soundio_strerror(err)); return 1; } for (;;) soundio_wait_events(soundio); soundio_instream_destroy(instream); soundio_device_unref(selected_device); soundio_destroy(soundio); return 0; }