shithub: m8c

Download patch

ref: 1fb43347e61f11fe9e8b079409f727c1f3450fbd
parent: f80e89b8eccdea0815e9f93be667c5aa95d01db8
author: Maido Käära <maido@producement.com>
date: Mon Dec 12 10:58:59 EST 2022

Add audio support

--- /dev/null
+++ b/audio.c
@@ -1,0 +1,158 @@
+#include <libusb.h>
+#include <errno.h>
+#include <android/log.h>
+#include <jni.h>
+#include <SDL_audio.h>
+#include <SDL_log.h>
+#include "audio.h"
+
+#define EP_ISO_IN    0x85
+#define IFACE_NUM   4
+
+#define NUM_TRANSFERS 10
+#define PACKET_SIZE 180
+#define NUM_PACKETS 10
+
+static unsigned long num_bytes = 0, num_xfer = 0;
+static struct timeval tv_start;
+
+static int do_exit = 1;
+
+static void cb_xfr(struct libusb_transfer *xfr) {
+    unsigned int i;
+
+    int len = 0;
+
+    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("Error (status %d: %s) :", pack->status,
+                    libusb_error_name(pack->status));
+            /* This doesn't happen, so bail out if it does. */
+            exit(EXIT_FAILURE);
+        }
+
+        const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
+
+        SDL_QueueAudio(1, data, pack->actual_length);
+
+        len += pack->length;
+    }
+
+    num_bytes += len;
+    num_xfer++;
+
+    if (libusb_submit_transfer(xfr) < 0) {
+        SDL_Log("error re-submitting URB\n");
+        exit(1);
+    }
+
+}
+
+static int benchmark_in(libusb_device_handle *devh, uint8_t ep) {
+    static uint8_t buf[PACKET_SIZE * NUM_PACKETS];
+    static struct libusb_transfer *xfr[NUM_TRANSFERS];
+    int num_iso_pack = NUM_PACKETS;
+    int i;
+
+    /* NOTE: To reach maximum possible performance the program must
+     * submit *multiple* transfers here, not just one.
+     *
+     * When only one transfer is submitted there is a gap in the bus
+     * schedule from when the transfer completes until a new transfer
+     * is submitted by the callback. This causes some jitter for
+     * isochronous transfers and loss of throughput for bulk transfers.
+     *
+     * This is avoided by queueing multiple transfers in advance, so
+     * that the host controller is always kept busy, and will schedule
+     * more transfers on the bus while the callback is running for
+     * transfers which have completed on the bus.
+     */
+    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]);
+    }
+
+    gettimeofday(&tv_start, NULL);
+
+    return 1;
+}
+
+int audio_setup(libusb_device_handle *devh) {
+    SDL_Log("UsbAudio 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));
+            libusb_close(devh);
+            libusb_exit(NULL);
+            return rc;
+        }
+    }
+
+    rc = libusb_claim_interface(devh, IFACE_NUM);
+    if (rc < 0) {
+        SDL_Log("Error claiming interface: %s\n", libusb_error_name(rc));
+        libusb_close(devh);
+        libusb_exit(NULL);
+        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));
+        libusb_close(devh);
+        libusb_exit(NULL);
+        return rc;
+    }
+
+
+// Good to go
+    do_exit = 0;
+    SDL_Log("Starting capture");
+    if ((rc = benchmark_in(devh, EP_ISO_IN)) < 0) {
+        SDL_Log("Capture failed to start: %d", rc);
+        return rc;
+    }
+
+    static SDL_AudioSpec audio_spec;
+    audio_spec.format = AUDIO_S16;
+    audio_spec.channels = 2;
+    audio_spec.freq = 44100;
+
+    if (SDL_OpenAudio(&audio_spec, NULL) < 0) {
+        SDL_Log("Couldn't open audio: %s\n", SDL_GetError());
+        exit(-1);
+    }
+
+    SDL_PauseAudio(0);
+
+    SDL_Log("Successful init");
+    return 1;
+}
+
+JNIEXPORT void JNICALL
+Java_io_maido_m8client_M8SDLActivity_loop(JNIEnv *env, jobject thiz) {
+    while (!do_exit) {
+        int rc = libusb_handle_events(NULL);
+        if (rc != LIBUSB_SUCCESS) {
+            break;
+        }
+    }
+}
--- /dev/null
+++ b/audio.h
@@ -1,0 +1,6 @@
+#ifndef _AUDIO_H_
+#define _AUDIO_H_
+
+int audio_setup(libusb_device_handle *devh);
+
+#endif
--- a/serial.c
+++ b/serial.c
@@ -16,7 +16,10 @@
 #ifdef USE_LIBUSB
 
 #include <libusb.h>
+#include "audio.h"
 
+#define UNUSED __attribute__((unused))
+
 static int ep_out_addr = 0x03;
 static int ep_in_addr = 0x83;
 
@@ -149,7 +152,7 @@
         return 0;
     }
 
-    return 1;
+    return audio_setup(devh);
 }
 
 int reset_display() {
@@ -235,6 +238,7 @@
     return 1;
 }
 
+
 #else
 #include <libserialport.h>
 
@@ -481,4 +485,5 @@
   return 1;
 }
 #endif
+
 
--