ref: 03b9b306d48df8ecf170f8597f8bd0a79a90d341
parent: afef4abe4d35daf2ea4f14b9a5cd76e3450018cc
author: Maido Käära <maido@producement.com>
date: Thu Mar 9 13:22:12 EST 2023
Add support for audio over USB - Add possibility to choose an audio output device - Use async libusb API so that it works better with audio
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,5 @@
compile_commands.json
.clangd
result*
-cmake-build-debug
+cmake-build-*
.idea
--- a/audio.c
+++ b/audio.c
@@ -1,6 +1,6 @@
// Copyright 2021 Jonne Kokkonen
// Released under the MIT licence, https://opensource.org/licenses/MIT
-
+#ifndef USE_LIBUSB
#include "audio.h"
#include <SDL.h>
#include <stdint.h>
@@ -12,7 +12,7 @@
SDL_QueueAudio(devid_out, stream, len);
}
-int audio_init(int audio_buffer_size) {+int audio_init(int audio_buffer_size, const char* output_device_name) {int i = 0;
int m8_device_id = -1;
@@ -46,8 +46,7 @@
want_out.format = AUDIO_S16;
want_out.channels = 2;
want_out.samples = audio_buffer_size;
- // Opening device NULL specifies that we want to use a reasonable system default device
- devid_out = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out,
+ devid_out = SDL_OpenAudioDevice(output_device_name, 0, &want_out, &have_out,
SDL_AUDIO_ALLOW_ANY_CHANGE);
if (devid_out == 0) { SDL_Log("Failed to open output: %s", SDL_GetError());@@ -84,3 +83,4 @@
SDL_CloseAudioDevice(devid_in);
SDL_CloseAudioDevice(devid_out);
}
+#endif
--- a/audio.h
+++ b/audio.h
@@ -1,13 +1,9 @@
// Copyright 2021 Jonne Kokkonen
// Released under the MIT licence, https://opensource.org/licenses/MIT
-
#ifndef AUDIO_H
#define AUDIO_H
-#include <stdint.h>
-
-void audio_cb_in(void *userdata, uint8_t *stream, int len);
-int audio_init(int audio_buffer_size);
+int audio_init(int audio_buffer_size, const char *output_device_name);
void audio_destroy();
#endif
--- a/config.c
+++ b/config.c
@@ -29,9 +29,10 @@
c.idle_ms = 10; // default to high performance
c.wait_for_device = 1; // default to exit if device disconnected
c.wait_packets = 1024; // default zero-byte attempts to disconnect (about 2
- // sec for default idle_ms)
+ // sec for default idle_ms)
c.audio_enabled = 0; // route M8 audio to default output
c.audio_buffer_size = 1024; // requested audio buffer size in samples
+ c.audio_device_name = NULL; // Use this device, leave NULL to use the default output device
c.key_up = SDL_SCANCODE_UP;
c.key_left = SDL_SCANCODE_LEFT;
@@ -82,7 +83,7 @@
SDL_Log("Writing config file to %s", config_path);- const unsigned int INI_LINE_COUNT = 43;
+ const unsigned int INI_LINE_COUNT = 44;
const unsigned int LINELEN = 50;
// Entries for the config file
@@ -103,6 +104,8 @@
conf->audio_enabled ? "true" : "false");
snprintf(ini_values[initPointer++], LINELEN, "audio_buffer_size=%d\n",
conf->audio_buffer_size);
+ snprintf(ini_values[initPointer++], LINELEN, "audio_device_name=%s\n",
+ conf->audio_device_name ? conf->audio_device_name : "Default");
snprintf(ini_values[initPointer++], LINELEN, "[keyboard]\n");
snprintf(ini_values[initPointer++], LINELEN, "key_up=%d\n", conf->key_up);
snprintf(ini_values[initPointer++], LINELEN, "key_left=%d\n", conf->key_left);
@@ -216,7 +219,9 @@
void read_audio_config(ini_t *ini, config_params_s *conf) {const char *param_audio_enabled = ini_get(ini, "audio", "audio_enabled");
const char *param_audio_buffer_size =
- ini_get(ini, "audio", "audio_buffer_size");
+ ini_get(ini, "audio", "audio_buffer_size");
+ const char *param_audio_device_name =
+ ini_get(ini, "audio", "audio_device_name");
if (param_audio_enabled != NULL) { if (strcmpci(param_audio_enabled, "true") == 0) {@@ -226,6 +231,10 @@
}
}
+ if (param_audio_device_name != NULL && SDL_strcmp(param_audio_device_name, "Default") != 0) {+ conf->audio_device_name = SDL_strdup(param_audio_device_name);
+ }
+
if (param_audio_buffer_size != NULL) {conf->audio_buffer_size = SDL_atoi(param_audio_buffer_size);
}
@@ -326,21 +335,21 @@
const char *gamepad_quit = ini_get(ini, "gamepad", "gamepad_quit");
const char *gamepad_reset = ini_get(ini, "gamepad", "gamepad_reset");
const char *gamepad_analog_threshold =
- ini_get(ini, "gamepad", "gamepad_analog_threshold");
+ ini_get(ini, "gamepad", "gamepad_analog_threshold");
const char *gamepad_analog_invert =
- ini_get(ini, "gamepad", "gamepad_analog_invert");
+ ini_get(ini, "gamepad", "gamepad_analog_invert");
const char *gamepad_analog_axis_updown =
- ini_get(ini, "gamepad", "gamepad_analog_axis_updown");
+ ini_get(ini, "gamepad", "gamepad_analog_axis_updown");
const char *gamepad_analog_axis_leftright =
- ini_get(ini, "gamepad", "gamepad_analog_axis_leftright");
+ ini_get(ini, "gamepad", "gamepad_analog_axis_leftright");
const char *gamepad_analog_axis_select =
- ini_get(ini, "gamepad", "gamepad_analog_axis_select");
+ ini_get(ini, "gamepad", "gamepad_analog_axis_select");
const char *gamepad_analog_axis_start =
- ini_get(ini, "gamepad", "gamepad_analog_axis_start");
+ ini_get(ini, "gamepad", "gamepad_analog_axis_start");
const char *gamepad_analog_axis_opt =
- ini_get(ini, "gamepad", "gamepad_analog_axis_opt");
+ ini_get(ini, "gamepad", "gamepad_analog_axis_opt");
const char *gamepad_analog_axis_edit =
- ini_get(ini, "gamepad", "gamepad_analog_axis_edit");
+ ini_get(ini, "gamepad", "gamepad_analog_axis_edit");
if (gamepad_up)
conf->gamepad_up = SDL_atoi(gamepad_up);
@@ -374,7 +383,7 @@
conf->gamepad_analog_axis_updown = SDL_atoi(gamepad_analog_axis_updown);
if (gamepad_analog_axis_leftright)
conf->gamepad_analog_axis_leftright =
- SDL_atoi(gamepad_analog_axis_leftright);
+ SDL_atoi(gamepad_analog_axis_leftright);
if (gamepad_analog_axis_select)
conf->gamepad_analog_axis_select = SDL_atoi(gamepad_analog_axis_select);
if (gamepad_analog_axis_start)
--- a/config.h
+++ b/config.h
@@ -15,6 +15,7 @@
int wait_packets;
int audio_enabled;
int audio_buffer_size;
+ const char *audio_device_name;
int key_up;
int key_left;
--- a/main.c
+++ b/main.c
@@ -91,7 +91,7 @@
if (port_inited == 1 && enable_and_reset_display() == 1) {// if audio routing is enabled, try to initialize audio devices
if (conf.audio_enabled == 1) {- audio_init(conf.audio_buffer_size);
+ audio_init(conf.audio_buffer_size, conf.audio_device_name);
}
run = RUN;
} else {@@ -133,7 +133,7 @@
if (run == WAIT_FOR_DEVICE && init_serial(0) == 1) { if (conf.audio_enabled == 1) {- if (audio_init(conf.audio_buffer_size) == 0) {+ if (audio_init(conf.audio_buffer_size, conf.audio_device_name) == 0) { SDL_Log("Cannot initialize audio, exiting.");run = QUIT;
}
--- /dev/null
+++ b/ringbuffer.c
@@ -1,0 +1,62 @@
+#include "ringbuffer.h"
+#include <stdlib.h>
+#include <SDL.h>
+
+RingBuffer *ring_buffer_create(uint32_t size) {+ RingBuffer *rb = SDL_malloc(sizeof(*rb));
+ rb->buffer = SDL_malloc(sizeof(*(rb->buffer)) * size);
+ rb->head = 0;
+ rb->tail = 0;
+ rb->max_size = size;
+ rb->size = 0;
+ return rb;
+}
+
+void ring_buffer_free(RingBuffer *rb) {+ free(rb->buffer);
+ free(rb);
+}
+
+uint32_t ring_buffer_empty(RingBuffer *rb) {+ return (rb->size == 0);
+}
+
+uint32_t ring_buffer_full(RingBuffer *rb) {+ return (rb->size == rb->max_size);
+}
+
+uint32_t ring_buffer_push(RingBuffer *rb, const uint8_t *data, uint32_t length) {+ if (ring_buffer_full(rb)) {+ return -1; // buffer full, push fails
+ } else {+ uint32_t space1 = rb->max_size - rb->tail;
+ uint32_t n = (length <= rb->max_size - rb->size) ? length : (rb->max_size - rb->size);
+ if (n <= space1) {+ SDL_memcpy(rb->buffer + rb->tail, data, n);
+ } else {+ SDL_memcpy(rb->buffer + rb->tail, data, space1);
+ SDL_memcpy(rb->buffer, data + space1, n - space1);
+ }
+ rb->tail = (rb->tail + n) % rb->max_size;
+ rb->size += n;
+ return n; // push successful, returns number of bytes pushed
+ }
+}
+
+uint32_t ring_buffer_pop(RingBuffer *rb, uint8_t *data, uint32_t length) {+ if (ring_buffer_empty(rb)) {+ return -1; // buffer empty, pop fails
+ } else {+ uint32_t space1 = rb->max_size - rb->head;
+ uint32_t n = (length <= rb->size) ? length : rb->size;
+ if (n <= space1) {+ SDL_memcpy(data, rb->buffer + rb->head, n);
+ } else {+ SDL_memcpy(data, rb->buffer + rb->head, space1);
+ SDL_memcpy(data + space1, rb->buffer, n - space1);
+ }
+ rb->head = (rb->head + n) % rb->max_size;
+ rb->size -= n;
+ return n; // pop successful, returns number of bytes popped
+ }
+}
--- /dev/null
+++ b/ringbuffer.h
@@ -1,0 +1,24 @@
+#ifndef M8C_RINGBUFFER_H
+#define M8C_RINGBUFFER_H
+
+#include <stdlib.h>
+
+typedef struct {+ uint8_t *buffer;
+ uint32_t head;
+ uint32_t tail;
+ uint32_t max_size;
+ uint32_t size;
+} RingBuffer;
+
+RingBuffer *ring_buffer_create(uint32_t size);
+
+uint32_t ring_buffer_empty(RingBuffer *rb);
+
+uint32_t ring_buffer_pop(RingBuffer *rb, uint8_t *data, uint32_t length);
+
+uint32_t ring_buffer_push(RingBuffer *rb, const uint8_t *data, uint32_t length);
+
+void ring_buffer_free(RingBuffer *rb);
+
+#endif //M8C_RINGBUFFER_H
--- a/serial.h
+++ b/serial.h
@@ -5,12 +5,8 @@
#define _SERIAL_H_
#ifdef USE_LIBUSB
-// Not sure about this value but it seems to work
+// Max packet length of the USB endpoint
#define serial_read_size 512
-#include <libusb.h>
-typedef int (*usb_callback_t)(libusb_device_handle *devh);
-void set_usb_init_callback(usb_callback_t callback);
-void set_usb_destroy_callback(usb_callback_t callback);
int init_serial_with_file_descriptor(int file_descriptor);
#else
// maximum amount of bytes to read from the serial in one read()
--- a/usb.c
+++ b/usb.c
@@ -6,15 +6,12 @@
#ifdef USE_LIBUSB
#include <SDL.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <libusb.h>
-#include "serial.h"
+#include "usb.h"
-
static int ep_out_addr = 0x03;
static int ep_in_addr = 0x83;
@@ -21,47 +18,74 @@
#define ACM_CTRL_DTR 0x01
#define ACM_CTRL_RTS 0x02
-usb_callback_t init_callback = NULL;
-usb_callback_t destroy_callback = NULL;
+libusb_context *ctx = NULL;
libusb_device_handle *devh = NULL;
-void set_usb_init_callback(usb_callback_t callback) {- init_callback = callback;
+static int do_exit = 0;
+
+int usb_loop(void *data) {+ SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
+ while (!do_exit) {+ int rc = libusb_handle_events(ctx);
+ if (rc != LIBUSB_SUCCESS) {+ SDL_Log("Audio loop error: %s\n", libusb_error_name(rc));+ break;
+ }
+ }
+ return 0;
}
-void set_usb_destroy_callback(usb_callback_t callback) {- destroy_callback = callback;
+static SDL_Thread *usb_thread;
+
+static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer) {+ int *completed = transfer->user_data;
+ *completed = 1;
}
-int blocking_write(void *buf,
- int count, unsigned int timeout_ms) {- int actual_length;
- int rc;
- rc = libusb_bulk_transfer(devh, ep_out_addr, buf, count,
- &actual_length, timeout_ms);
- if (rc < 0) {- SDL_Log("Error while sending char: %s", libusb_error_name(rc));- return -1;
+int bulk_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms) {+ int completed = 0;
+
+ struct libusb_transfer *transfer;
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, devh, endpoint, serial_buf, count,
+ xfr_cb_in, &completed, timeout_ms);
+ int r = libusb_submit_transfer(transfer);
+
+ if (r < 0) {+ SDL_Log("Error");+ libusb_free_transfer(transfer);
+ return r;
}
- return actual_length;
-}
-int serial_read(uint8_t *serial_buf, int count) {- int actual_length;
- int rc = libusb_bulk_transfer(devh, ep_in_addr, serial_buf, count, &actual_length,
- 10);
- if (rc == LIBUSB_ERROR_TIMEOUT) {- return 0;
- } else if (rc < 0) {- SDL_Log("Error while waiting for char: %s", libusb_error_name(rc));- return -1;
+ retry:
+ libusb_lock_event_waiters(ctx);
+ while (!completed) {+ if (!libusb_event_handler_active(ctx)) {+ libusb_unlock_event_waiters(ctx);
+ goto retry;
+ }
+ libusb_wait_for_event(ctx, NULL);
}
+ libusb_unlock_event_waiters(ctx);
+ int actual_length = transfer->actual_length;
+
+ libusb_free_transfer(transfer);
+
return actual_length;
}
+int blocking_write(void *buf,
+ int count, unsigned int timeout_ms) {+ return bulk_transfer(ep_out_addr, buf, count, timeout_ms);
+}
+
+int serial_read(uint8_t *serial_buf, int count) {+ return bulk_transfer(ep_in_addr, serial_buf, count, 1);
+}
+
int check_serial_port() {- // Reading will fail anyway when the device is not present anymore
+ // Reading will fail anyway when the device is not present anymore
return 1;
}
@@ -86,29 +110,31 @@
}
}
- /* Start configuring the device:
- * - set line state
- */
+ /* Start configuring the device:
+ * - set line state
+ */
SDL_Log("Setting line state");rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS,
- 0, NULL, 0, 0);
+ 0, NULL, 0, 0);
if (rc < 0) { SDL_Log("Error during control transfer: %s", libusb_error_name(rc));return 0;
}
- /* - set line encoding: here 115200 8N1
- * 115200 = 0x01C200 ~> 0x00, 0xC2, 0x01, 0x00 in little endian
- */
+ /* - set line encoding: here 115200 8N1
+ * 115200 = 0x01C200 ~> 0x00, 0xC2, 0x01, 0x00 in little endian
+ */
SDL_Log("Set line encoding"); unsigned char encoding[] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding,
- sizeof(encoding), 0);
+ sizeof(encoding), 0);
if (rc < 0) { SDL_Log("Error during control transfer: %s", libusb_error_name(rc));return 0;
}
+ usb_thread = SDL_CreateThread(&usb_loop, "USB", NULL);
+
return 1;
}
@@ -126,12 +152,12 @@
SDL_Log("libusb_set_option failed: %s", libusb_error_name(r));return 0;
}
- r = libusb_init(NULL);
+ r = libusb_init(&ctx);
if (r < 0) { SDL_Log("libusb_init failed: %s", libusb_error_name(r));return 0;
}
- r = libusb_wrap_sys_device(NULL, (intptr_t) file_descriptor, &devh);
+ r = libusb_wrap_sys_device(ctx, (intptr_t) file_descriptor, &devh);
if (r < 0) { SDL_Log("libusb_wrap_sys_device failed: %s", libusb_error_name(r));return 0;
@@ -141,15 +167,6 @@
}
SDL_Log("USB device init success");- if (init_callback != NULL) {- r = init_callback(devh);
-
- if (r < 0) {- SDL_Log("Init callback failed: %d", r);- return 0;
- }
- }
-
return init_interface();
}
@@ -160,12 +177,12 @@
}
int r;
- r = libusb_init(NULL);
+ r = libusb_init(&ctx);
if (r < 0) { SDL_Log("libusb_init failed: %s", libusb_error_name(r));return 0;
}
- devh = libusb_open_device_with_vid_pid(NULL, 0x16c0, 0x048a);
+ devh = libusb_open_device_with_vid_pid(ctx, 0x16c0, 0x048a);
if (devh == NULL) { SDL_Log("libusb_open_device_with_vid_pid returned invalid handle");return 0;
@@ -172,15 +189,6 @@
}
SDL_Log("USB device init success");- if (init_callback != NULL) {- r = init_callback(devh);
-
- if (r < 0) {- SDL_Log("Init callback failed: %d", r);- return 0;
- }
- }
-
return init_interface();
}
@@ -194,7 +202,7 @@
result = blocking_write(buf, 1, 5);
if (result != 1) {SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d",
- result);
+ result);
return 0;
}
return 1;
@@ -223,21 +231,12 @@
char buf[1] = {'D'};int result;
- if (destroy_callback != NULL) {- result = destroy_callback(devh);
-
- if (result < 0) {- SDL_Log("Destroy callback failed: %d", result);- return result;
- }
- }
-
SDL_Log("Disconnecting M8\n");result = blocking_write(buf, 1, 5);
if (result != 1) {SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d",
- result);
+ result);
return -1;
}
@@ -251,12 +250,16 @@
}
}
+ do_exit = 1;
+
if (devh != NULL) {libusb_close(devh);
}
- libusb_exit(NULL);
+ SDL_WaitThread(usb_thread, NULL);
+ libusb_exit(ctx);
+
return 1;
}
@@ -267,7 +270,7 @@
result = blocking_write(buf, nbytes, 5);
if (result != nbytes) {SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d",
- result);
+ result);
return -1;
}
return 1;
@@ -282,7 +285,7 @@
result = blocking_write(buf, nbytes, 5);
if (result != nbytes) {SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d",
- result);
+ result);
return -1;
}
--- /dev/null
+++ b/usb.h
@@ -1,0 +1,9 @@
+#ifdef USE_LIBUSB
+#ifndef M8C_USB_H
+#define M8C_USB_H
+
+#include <libusb.h>
+extern libusb_device_handle *devh;
+
+#endif //M8C_USB_H
+#endif
--- /dev/null
+++ b/usb_audio.c
@@ -1,0 +1,194 @@
+#ifdef USE_LIBUSB
+
+#include <libusb.h>
+#include <errno.h>
+#include <SDL.h>
+#include "ringbuffer.h"
+#include "usb.h"
+
+#define EP_ISO_IN 0x85
+#define IFACE_NUM 4
+
+#define NUM_TRANSFERS 10
+#define PACKET_SIZE 180
+#define NUM_PACKETS 64
+
+SDL_AudioDeviceID sdl_audio_device_id = 0;
+RingBuffer *audio_buffer = NULL;
+
+static void audio_callback(void *userdata, Uint8 *stream,
+ int len) {+ uint32_t read_len = ring_buffer_pop(audio_buffer, stream, len);
+
+ if (read_len == -1) {+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,"Buffer underflow!");
+ }
+
+ // If we didn't read the full len bytes, fill the rest with zeros
+ if (read_len < len) {+ SDL_memset(&stream[read_len], 0, len - read_len);
+ }
+}
+
+static void cb_xfr(struct libusb_transfer *xfr) {+ unsigned int i;
+
+ for (i = 0; i < xfr->num_iso_packets; i++) {+ struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
+
+ if (pack->status != LIBUSB_TRANSFER_COMPLETED) {+ SDL_Log("XFR callback error (status %d: %s)", pack->status,+ libusb_error_name(pack->status));
+ /* This doesn't happen, so bail out if it does. */
+ return;
+ }
+
+ const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
+ if (sdl_audio_device_id != 0) {+ uint32_t actual = ring_buffer_push(audio_buffer, data, pack->actual_length);
+ if (actual == -1) {+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Buffer overflow!");
+ }
+ }
+ }
+
+ if (libusb_submit_transfer(xfr) < 0) {+ SDL_Log("error re-submitting URB\n");+ }
+}
+
+static struct libusb_transfer *xfr[NUM_TRANSFERS];
+
+static int benchmark_in(uint8_t ep) {+ static uint8_t buf[PACKET_SIZE * NUM_PACKETS];
+ int num_iso_pack = NUM_PACKETS;
+ int i;
+
+ for (i = 0; i < NUM_TRANSFERS; i++) {+ xfr[i] = libusb_alloc_transfer(num_iso_pack);
+ if (!xfr[i]) {+ SDL_Log("Could not allocate transfer");+ return -ENOMEM;
+ }
+
+ libusb_fill_iso_transfer(xfr[i], devh, ep, buf,
+ sizeof(buf), num_iso_pack, cb_xfr, NULL, 1000);
+ libusb_set_iso_packet_lengths(xfr[i], sizeof(buf) / num_iso_pack);
+
+ libusb_submit_transfer(xfr[i]);
+ }
+
+ return 1;
+}
+
+int audio_init(int audio_buffer_size, const char *output_device_name) {+ SDL_Log("USB audio setup");+
+ int rc;
+
+ rc = libusb_kernel_driver_active(devh, IFACE_NUM);
+ if (rc == 1) {+ SDL_Log("Detaching kernel driver");+ rc = libusb_detach_kernel_driver(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_Log("Could not detach kernel driver: %s\n",+ libusb_error_name(rc));
+ return rc;
+ }
+ }
+
+ rc = libusb_claim_interface(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_Log("Error claiming interface: %s\n", libusb_error_name(rc));+ return rc;
+ }
+
+ rc = libusb_set_interface_alt_setting(devh, IFACE_NUM, 1);
+ if (rc < 0) {+ SDL_Log("Error setting alt setting: %s\n", libusb_error_name(rc));+ return rc;
+ }
+
+ if (!SDL_WasInit(SDL_INIT_AUDIO)) {+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {+ SDL_Log("Init audio failed %s", SDL_GetError());+ return -1;
+ }
+ } else {+ SDL_Log("Audio was already initialised");+ }
+
+ static SDL_AudioSpec audio_spec;
+ audio_spec.format = AUDIO_S16;
+ audio_spec.channels = 2;
+ audio_spec.freq = 44100;
+ audio_spec.samples = audio_buffer_size;
+ audio_spec.callback = audio_callback;
+
+ SDL_AudioSpec _obtained;
+ SDL_zero(_obtained);
+
+ SDL_Log("Current audio driver is %s and device %s", SDL_GetCurrentAudioDriver(),+ output_device_name);
+
+ if (SDL_strcasecmp(SDL_GetCurrentAudioDriver(), "openslES") == 0 || output_device_name == NULL) {+ SDL_Log("Using default audio device");+ sdl_audio_device_id = SDL_OpenAudioDevice(NULL, 0, &audio_spec, &_obtained, 0);
+ } else {+ sdl_audio_device_id = SDL_OpenAudioDevice(output_device_name, 0, &audio_spec, &_obtained, 0);
+ }
+
+ audio_buffer = ring_buffer_create(4 * _obtained.size);
+
+ SDL_Log("Obtained audio spec. Sample rate: %d, channels: %d, samples: %d, size: %d",+ _obtained.freq,
+ _obtained.channels,
+ _obtained.samples, +_obtained.size);
+
+ SDL_PauseAudioDevice(sdl_audio_device_id, 0);
+
+ // Good to go
+ SDL_Log("Starting capture");+ if ((rc = benchmark_in(EP_ISO_IN)) < 0) {+ SDL_Log("Capture failed to start: %d", rc);+ return rc;
+ }
+
+ SDL_Log("Successful init");+ return 1;
+}
+
+int audio_destroy() {+ SDL_Log("Closing audio");+
+ int i, rc;
+
+ for (i = 0; i < NUM_TRANSFERS; i++) {+ rc = libusb_cancel_transfer(xfr[i]);
+ if (rc < 0) {+ SDL_Log("Error cancelling transfer: %s\n", libusb_error_name(rc));+ }
+ }
+
+ SDL_Log("Freeing interface %d", IFACE_NUM);+
+ rc = libusb_release_interface(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_Log("Error releasing interface: %s\n", libusb_error_name(rc));+ return rc;
+ }
+
+ if (sdl_audio_device_id != 0) {+ SDL_Log("Closing audio device %d", sdl_audio_device_id);+ SDL_AudioDeviceID device = sdl_audio_device_id;
+ sdl_audio_device_id = 0;
+ SDL_CloseAudioDevice(device);
+ }
+
+ SDL_Log("Audio closed");+
+ ring_buffer_free(audio_buffer);
+ return 1;
+}
+
+#endif
--
⑨