Created
          November 14, 2016 22:24 
        
      - 
      
- 
        Save Yepoleb/18f6daab39cc0f01652e65587c5d883d to your computer and use it in GitHub Desktop. 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | /* | |
| * Copyright (c) 2015 Andrew Kelley | |
| * | |
| * This file is part of libsoundio, which is MIT licensed. | |
| * See http://opensource.org/licenses/MIT | |
| */ | |
| #include <soundio/soundio.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdint.h> | |
| #include <math.h> | |
| #include <unistd.h> | |
| static int usage(char *exe) { | |
| fprintf(stderr, "Usage: %s [options]\n" | |
| "Options:\n" | |
| " [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n" | |
| " [--device id]\n" | |
| " [--raw]\n" | |
| " [--name stream_name]\n" | |
| , exe); | |
| return 1; | |
| } | |
| static void write_sample_s16ne(char *ptr, double sample) { | |
| int16_t *buf = (int16_t *)ptr; | |
| double range = (double)INT16_MAX - (double)INT16_MIN; | |
| double val = sample * range / 2.0; | |
| *buf = val; | |
| } | |
| static void write_sample_s32ne(char *ptr, double sample) { | |
| int32_t *buf = (int32_t *)ptr; | |
| double range = (double)INT32_MAX - (double)INT32_MIN; | |
| double val = sample * range / 2.0; | |
| *buf = val; | |
| } | |
| static void write_sample_float32ne(char *ptr, double sample) { | |
| float *buf = (float *)ptr; | |
| *buf = sample; | |
| } | |
| static void write_sample_float64ne(char *ptr, double sample) { | |
| double *buf = (double *)ptr; | |
| *buf = sample; | |
| } | |
| static void (*write_sample)(char *ptr, double sample); | |
| static const double PI = 3.14159265358979323846264338328; | |
| static double seconds_offset = 0.0; | |
| static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) { | |
| double float_sample_rate = outstream->sample_rate; | |
| double seconds_per_frame = 1.0f / float_sample_rate; | |
| struct SoundIoChannelArea *areas; | |
| int err; | |
| int frames_left = frame_count_max; | |
| for (;;) { | |
| int frame_count = frames_left; | |
| if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) { | |
| fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err)); | |
| exit(1); | |
| } | |
| if (!frame_count) | |
| break; | |
| const struct SoundIoChannelLayout *layout = &outstream->layout; | |
| size_t stdout_buf_size = frame_count * layout->channel_count * 2; | |
| char stdout_buf[stdout_buf_size]; | |
| char* stdout_buf_pos = stdout_buf; | |
| double pitch = 440.0; | |
| double radians_per_second = pitch * 2.0 * PI; | |
| for (int frame = 0; frame < frame_count; frame += 1) { | |
| double sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second); | |
| for (int channel = 0; channel < layout->channel_count; channel += 1) { | |
| write_sample(areas[channel].ptr, sample); | |
| write_sample_s16ne(stdout_buf_pos, sample); | |
| stdout_buf_pos += 2; | |
| areas[channel].ptr += areas[channel].step; | |
| } | |
| } | |
| seconds_offset += seconds_per_frame * frame_count; | |
| write(1, stdout_buf, stdout_buf_size); | |
| if ((err = soundio_outstream_end_write(outstream))) { | |
| if (err == SoundIoErrorUnderflow) | |
| return; | |
| fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err)); | |
| exit(1); | |
| } | |
| frames_left -= frame_count; | |
| if (frames_left <= 0) | |
| break; | |
| } | |
| } | |
| static void underflow_callback(struct SoundIoOutStream *outstream) { | |
| static int count = 0; | |
| fprintf(stderr, "underflow %d\n", count++); | |
| } | |
| 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; | |
| 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 { | |
| 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); | |
| if (strcmp(device->id, device_id) == 0 && device->is_raw == raw) { | |
| 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); | |
| fprintf(stderr, | |
| "'p\\n' - pause\n" | |
| "'u\\n' - unpause\n" | |
| "'c\\n' - clear buffer\n" | |
| "'q\\n' - quit\n"); | |
| 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); | |
| outstream->write_callback = write_callback; | |
| outstream->underflow_callback = underflow_callback; | |
| outstream->name = stream_name; | |
| 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; | |
| } | |
| 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 == 'u') { | |
| 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; | |
| } | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment