Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USB Audio support #99

Merged
merged 5 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ If you like the M8 and you gel with the tracker workflow, please support [Dirtyw

*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:

Expand Down
85 changes: 85 additions & 0 deletions audio.c
Original file line number Diff line number Diff line change
@@ -0,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);
}
13 changes: 13 additions & 0 deletions audio.h
Original file line number Diff line number Diff line change
@@ -0,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
36 changes: 33 additions & 3 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -26,7 +28,10 @@ config_params_s init_config() {
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;
Expand Down Expand Up @@ -77,7 +82,7 @@ void write_config(config_params_s *conf) {

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
Expand All @@ -91,7 +96,13 @@ void write_config(config_params_s *conf) {
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);
Expand Down Expand Up @@ -190,6 +201,7 @@ void read_config(config_params_s *conf) {
return;
}

read_audio_config(ini, conf);
read_graphics_config(ini, conf);
read_key_config(ini, conf);
read_gamepad_config(ini, conf);
Expand All @@ -201,6 +213,24 @@ void read_config(config_params_s *conf) {
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) {
const char *param_fs = ini_get(ini, "graphics", "fullscreen");
const char *param_gpu = ini_get(ini, "graphics", "use_gpu");
Expand Down
7 changes: 5 additions & 2 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ typedef struct config_params_s {
int idle_ms;
int wait_for_device;
int wait_packets;
int audio_enabled;
int audio_buffer_size;

int key_up;
int key_left;
Expand Down Expand Up @@ -53,9 +55,10 @@ typedef struct config_params_s {


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
#endif
11 changes: 11 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <SDL.h>
#include <signal.h>

#include "audio.h"
#include "command.h"
#include "config.h"
#include "input.h"
Expand Down Expand Up @@ -79,6 +80,13 @@ int main(int argc, char *argv[]) {
// 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
Expand Down Expand Up @@ -249,6 +257,9 @@ int main(int argc, char *argv[]) {

// exit, clean up
SDL_Log("Shutting down\n");
if (conf.audio_enabled == 1){
audio_destroy();
}
close_game_controllers();
close_renderer();
close_serial_port();
Expand Down