ref: d963ae7f4ac11387959d6fd70d8abb6f1efb7b42
parent: 8917f16e1c96c573cf13e2887f61ebd13d8d87e3
parent: 0f9fece872802caf790a34387fdb2b761ae55ffb
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Wed Mar 8 17:34:42 EST 2023
Merge pull request #99 from laamaa/audio Experimental USB Audio monitoring support
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
#Set all your object files (the object files of all the .c files in your project, e.g. main.o my_sub_functions.o )
-OBJ = main.o serial.o slip.o command.o render.o ini.o config.o input.o font.o fx_cube.o usb.o
+OBJ = main.o serial.o slip.o command.o render.o ini.o config.o input.o font.o fx_cube.o usb.o audio.o
#Set any dependant header files so that if they are edited they cause a complete re-compile (e.g. main.h some_subfunctions.h some_definitions_file.h ), or leave blank
-DEPS = serial.h slip.h command.h render.h ini.h config.h input.h fx_cube.h
+DEPS = serial.h slip.h command.h render.h ini.h config.h input.h fx_cube.h audio.h
#Any special libraries you are using in your project (e.g. -lbcm2835 -lrt `pkg-config --libs gtk+-3.0` ), or leave blank
INCLUDES = $(shell pkg-config --libs sdl2 libserialport)
--- a/README.md
+++ b/README.md
@@ -10,9 +10,7 @@
*m8c* is a client for Dirtywave M8 tracker's headless mode. The application should be cross-platform ready and can be built in Linux, Windows (with MSYS2/MINGW64) and Mac OS.
-Please note that routing the headless M8 USB audio isn't in the scope of this program -- if this is needed, it can be achieved with tools like Pipewire, Pulseaudio, Jack w/ alsa\_in and alsa\_out just to name a few. The file AUDIOGUIDE.md contains some examples for routing the audio.
-
-If you want to route audio with the headless client you could try https://github.com/booss/rm8 which is a great native client with audio support (among other user features)!
+Experimental audio routing support can be enabled by setting the config value "audio_enabled" to "true". The audio buffer size can also be tweaked from the config file for possible lower latencies.
Many thanks to:
--- /dev/null
+++ b/audio.c
@@ -1,0 +1,85 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+
+#include "audio.h"
+#include "SDL_audio.h"
+#include <SDL.h>
+#include <stdint.h>
+
+static SDL_AudioDeviceID devid_in = 0;
+static SDL_AudioDeviceID devid_out = 0;
+
+void audio_cb_in(void *userdata, uint8_t *stream, int len) {+ SDL_QueueAudio(devid_out, stream, len);
+}
+
+int audio_init(int audio_buffer_size) {+
+ int i = 0;
+ int m8_found = 0;
+ int devcount_in = 0; // audio input device count
+
+ devcount_in = SDL_GetNumAudioDevices(SDL_TRUE);
+
+ if (devcount_in < 1) {+ SDL_Log("No audio capture devices, SDL Error: %s", SDL_GetError());+ return 0;
+ } else {+ for (i = 0; i < devcount_in; i++) {+ // Check if input device exists before doing anything else
+ if (strcmp(SDL_GetAudioDeviceName(i, SDL_TRUE), "M8 Analog Stereo") ==
+ 0) {+ SDL_Log("M8 Audio Input device found");+ m8_found = 1;
+ }
+ }
+ if (m8_found == 0) {+ // forget about it
+ return 0;
+ }
+
+ SDL_AudioSpec want_in, have_in, want_out, have_out;
+
+ // Open output device first to avoid possible Directsound errors
+ SDL_zero(want_out);
+ want_out.freq = 44100;
+ want_out.format = AUDIO_S16;
+ want_out.channels = 2;
+ want_out.samples = audio_buffer_size;
+ devid_out = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out,
+ SDL_AUDIO_ALLOW_ANY_CHANGE);
+ if (devid_out == 0) {+ SDL_Log("Failed to open output: %s", SDL_GetError());+ return 0;
+ }
+ SDL_Log("Using default audio output device: %s",SDL_GetAudioDeviceName(0, 0));+
+ SDL_zero(want_in);
+ want_in.freq = 44100;
+ want_in.format = AUDIO_S16;
+ want_in.channels = 2;
+ want_in.samples = audio_buffer_size;
+ want_in.callback = audio_cb_in;
+ devid_in = SDL_OpenAudioDevice("M8 Analog Stereo", SDL_TRUE, &want_in,+ &have_in, SDL_AUDIO_ALLOW_ANY_CHANGE);
+ if (devid_in == 0) {+ SDL_Log("Failed to open M8 audio device, SDL Error: %s", SDL_GetError());+ return 0;
+ }
+ }
+
+ // Start audio processing
+ SDL_Log("Opening audio devices");+ SDL_PauseAudioDevice(devid_in, 0);
+ SDL_PauseAudioDevice(devid_out, 0);
+
+ return 1;
+}
+
+void audio_destroy() {+ SDL_Log("Closing audio devices");+ SDL_PauseAudioDevice(devid_in, 1);
+ SDL_PauseAudioDevice(devid_out, 1);
+ SDL_CloseAudioDevice(devid_in);
+ SDL_CloseAudioDevice(devid_out);
+}
--- /dev/null
+++ b/audio.h
@@ -1,0 +1,13 @@
+// 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);
+void audio_destroy();
+
+#endif
--- a/config.c
+++ b/config.c
@@ -2,9 +2,11 @@
// Released under the MIT licence, https://opensource.org/licenses/MIT
#include "config.h"
+#include "SDL_stdinc.h"
#include "ini.h"
#include <SDL.h>
#include <assert.h>
+#include <stdio.h>
/* Case insensitive string compare from ini.h library */
static int strcmpci(const char *a, const char *b) {@@ -26,7 +28,10 @@
c.init_use_gpu = 1; // default to use hardware acceleration
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)
+ c.wait_packets = 1024; // default zero-byte attempts to disconnect (about 2
+ // 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.key_up = SDL_SCANCODE_UP;
c.key_left = SDL_SCANCODE_LEFT;
@@ -77,7 +82,7 @@
SDL_Log("Writing config file to %s", config_path);- const unsigned int INI_LINE_COUNT = 40;
+ const unsigned int INI_LINE_COUNT = 43;
const unsigned int LINELEN = 50;
// Entries for the config file
@@ -91,7 +96,13 @@
snprintf(ini_values[initPointer++], LINELEN, "idle_ms=%d\n", conf->idle_ms);
snprintf(ini_values[initPointer++], LINELEN, "wait_for_device=%s\n",
conf->wait_for_device ? "true" : "false");
- snprintf(ini_values[initPointer++], LINELEN, "wait_packets=%d\n", conf->wait_packets);
+ snprintf(ini_values[initPointer++], LINELEN, "wait_packets=%d\n",
+ conf->wait_packets);
+ snprintf(ini_values[initPointer++], LINELEN, "[audio]\n");
+ snprintf(ini_values[initPointer++], LINELEN, "audio_enabled=%s\n",
+ conf->audio_enabled ? "true" : "false");
+ snprintf(ini_values[initPointer++], LINELEN, "audio_buffer_size=%d\n",
+ conf->audio_buffer_size);
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);
@@ -190,6 +201,7 @@
return;
}
+ read_audio_config(ini, conf);
read_graphics_config(ini, conf);
read_key_config(ini, conf);
read_gamepad_config(ini, conf);
@@ -199,6 +211,24 @@
// Write any new default options after loading
write_config(conf);
+}
+
+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");
+
+ if (param_audio_enabled != NULL) {+ if (strcmpci(param_audio_enabled, "true") == 0) {+ conf->audio_enabled = 1;
+ } else {+ conf->audio_enabled = 0;
+ }
+ }
+
+ if (param_audio_buffer_size != NULL) {+ conf->audio_buffer_size = SDL_atoi(param_audio_buffer_size);
+ }
}
void read_graphics_config(ini_t *ini, config_params_s *conf) {--- a/config.h
+++ b/config.h
@@ -13,6 +13,8 @@
int idle_ms;
int wait_for_device;
int wait_packets;
+ int audio_enabled;
+ int audio_buffer_size;
int key_up;
int key_left;
@@ -53,9 +55,10 @@
config_params_s init_config();
-void read_config();
+void read_config(config_params_s *conf);
+void read_audio_config(ini_t *config, config_params_s *conf);
void read_graphics_config(ini_t *config, config_params_s *conf);
void read_key_config(ini_t *config, config_params_s *conf);
void read_gamepad_config(ini_t *config, config_params_s *conf);
-#endif
\ No newline at end of file
+#endif
--- a/main.c
+++ b/main.c
@@ -8,6 +8,7 @@
#include <SDL.h>
#include <signal.h>
+#include "audio.h"
#include "command.h"
#include "config.h"
#include "input.h"
@@ -79,6 +80,13 @@
// initial scan for (existing) game controllers
initialize_game_controllers();
+ if (conf.audio_enabled == 1) {+ if (audio_init(conf.audio_buffer_size) == 0){+ SDL_Log("Cannot initialize audio, exiting.");+ run = QUIT;
+ }
+ }
+
#ifdef DEBUG_MSG
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
#endif
@@ -249,6 +257,9 @@
// exit, clean up
SDL_Log("Shutting down\n");+ if (conf.audio_enabled == 1){+ audio_destroy();
+ }
close_game_controllers();
close_renderer();
close_serial_port();
--
⑨