Skip to content

Commit 452c3a3

Browse files
committed
Expose BLE-MIDI server as a local ALSA sequencer
1 parent 52448b9 commit 452c3a3

14 files changed

+449
-9
lines changed

.github/iwyu.imp

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
{ include: [ '<bsd/libutil.h>', private, '<bsd/stdlib.h>', public ] },
66
{ include: [ '<bsd/sys/time.h>', private, '<sys/time.h>', public ] },
77
{ symbol: [ 'sig_atomic_t', private, '<signal.h>', public ] },
8+
{ symbol: [ 'S_IRWXU', private, '<sys/stat.h>', public ] },
89

910
{ include: [ '<alsa/conf.h>', private, '<alsa/asoundlib.h>', public ] },
1011
{ include: [ '<alsa/control.h>', private, '<alsa/asoundlib.h>', public ] },
@@ -15,6 +16,10 @@
1516
{ include: [ '<alsa/output.h>', private, '<alsa/asoundlib.h>', public ] },
1617
{ include: [ '<alsa/pcm.h>', private, '<alsa/asoundlib.h>', public ] },
1718
{ include: [ '<alsa/pcm_ioplug.h>', private, '<alsa/pcm_external.h>', public ] },
19+
{ include: [ '<alsa/seq.h>', private, '<alsa/asoundlib.h>', public ] },
20+
{ include: [ '<alsa/seq_event.h>', private, '<alsa/asoundlib.h>', public ] },
21+
{ include: [ '<alsa/seq_midi_event.h>', private, '<alsa/asoundlib.h>', public ] },
22+
{ include: [ '<alsa/seqmid.h>', private, '<alsa/asoundlib.h>', public ] },
1823
{ include: [ '<alsa/version.h>', private, '<alsa/asoundlib.h>', public ] },
1924

2025
{ include: [ '"dbus/dbus-memory.h"', private, '<dbus/dbus.h>', public ] },

NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
unreleased
22
==========
33

4+
- optional support for BLE MIDI 1.0 profile as a GATT server
45
- command line option to set real-time priority for IO threads
56
- allow to select individual extended controls in ALSA plug-in
67
- codec-specific delay adjustment with ALSA control and persistency

README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ ALSA, with only one application at a time using each Bluetooth audio stream.
3434
In such systems BlueALSA adds Bluetooth audio support to the existing
3535
ALSA sound card support. Note this means that the applications are constrained
3636
by the capabilities of the ALSA API, and the higher-level audio processing
37-
features of audio servers such as PulseAudio and Pipewire are not available.
37+
features of audio servers such as PulseAudio and PipeWire are not available.
3838

3939
BlueALSA consists of the daemon `bluealsa`, ALSA plug-ins, and a number of
4040
utilities. The basic context is shown in this diagram:
@@ -55,8 +55,11 @@ C <--> G((bluealsa-cli))
5555
F <--> H([ALSA libasound])
5656
H <--> I((ALSA\napplications))
5757
C <--> J((other\nD-Bus clients))
58+
C <--> L((ALSA MIDI\nsequencer))
59+
L <--> M([ALSA libasound])
60+
M <--> N((ALSA MIDI\napplication))
5861
59-
class A,B,E,H,I,J,K external;
62+
class A,B,E,H,I,J,K,L,M,N external;
6063
class C,D,F,G bluealsa;
6164
```
6265

@@ -70,6 +73,10 @@ applications to use the ALSA PCM and mixer interfaces, so that existing ALSA
7073
applications can access Bluetooth audio devices in the same way as they use
7174
sound card PCMs and mixers.
7275

76+
In case of BLE MIDI, the daemon creates a simple MIDI port directly in ALSA
77+
MIDI sequencer, so that ALSA MIDI application can connect to the remote BLE
78+
MIDI device in the same way as it would connect to a local MIDI device.
79+
7380
BlueALSA also includes a number of utility applications. Of particular note
7481
are:
7582

@@ -239,7 +246,7 @@ If reporting a problem as a new issue, please use the appropriate
239246
[bluez-alsa GitHub issue reporting template][] and complete each section of
240247
the template as fully as possible.
241248

242-
[TROUBLESHOOTING]: ./TROUBLESHOOTING.md
249+
[TROUBLESHOOTING]: TROUBLESHOOTING.md
243250
[manual pages]: doc/
244251
[previous issues]: https://github.com/arkq/bluez-alsa/issues
245252
[wiki]: https://github.com/arkq/bluez-alsa/wiki

src/Makefile.am

+5-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ endif
8585

8686
if ENABLE_MIDI
8787
bluealsa_SOURCES += \
88-
bluez-midi.c
88+
ble-midi.c \
89+
bluez-midi.c \
90+
midi.c
8991
endif
9092

9193
if ENABLE_MPEG
@@ -111,6 +113,7 @@ endif
111113

112114
AM_CFLAGS = \
113115
@AAC_CFLAGS@ \
116+
@ALSA_CFLAGS@ \
114117
@APTX_CFLAGS@ \
115118
@APTX_HD_CFLAGS@ \
116119
@BLUEZ_CFLAGS@ \
@@ -127,6 +130,7 @@ AM_CFLAGS = \
127130

128131
LDADD = \
129132
@AAC_LIBS@ \
133+
@ALSA_LIBS@ \
130134
@APTX_HD_LIBS@ \
131135
@APTX_LIBS@ \
132136
@BLUEZ_LIBS@ \

src/ba-transport.c

+49-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <sys/time.h>
2626
#include <unistd.h>
2727

28+
#include <alsa/asoundlib.h>
2829
#include <bluetooth/bluetooth.h>
2930
#include <bluetooth/hci.h>
3031

@@ -42,6 +43,7 @@
4243
#include "bluez.h"
4344
#include "hci.h"
4445
#include "hfp.h"
46+
#include "midi.h"
4547
#include "sco.h"
4648
#include "storage.h"
4749
#include "shared/defs.h"
@@ -597,12 +599,13 @@ struct ba_transport *ba_transport_new_sco(
597599
#if ENABLE_MIDI
598600

599601
static int transport_acquire_bt_midi(struct ba_transport *t) {
600-
(void)t;
601-
return 0;
602+
return midi_transport_alsa_seq_create(t);
602603
}
603604

604605
static int transport_release_bt_midi(struct ba_transport *t) {
605606

607+
midi_transport_alsa_seq_delete(t);
608+
606609
if (t->midi.ble_fd_write != -1) {
607610
debug("Releasing BLE-MIDI write link: %d", t->midi.ble_fd_write);
608611
close(t->midi.ble_fd_write);
@@ -630,10 +633,28 @@ struct ba_transport *ba_transport_new_midi(
630633

631634
t->profile = profile;
632635

636+
t->midi.seq_port = -1;
637+
t->midi.ble_fd_write = -1;
638+
t->midi.ble_fd_notify = -1;
639+
640+
int err;
641+
if ((err = snd_midi_event_new(1024, &t->midi.seq_parser)) < 0) {
642+
error("Couldn't create MIDI event decoder: %s", snd_strerror(err));
643+
goto fail;
644+
}
645+
646+
/* Disable MIDI running status generated by the decoder. */
647+
snd_midi_event_no_status(t->midi.seq_parser, 1);
648+
633649
t->acquire = transport_acquire_bt_midi;
634650
t->release = transport_release_bt_midi;
635651

636652
return t;
653+
654+
fail:
655+
ba_transport_unref(t);
656+
errno = -err;
657+
return NULL;
637658
}
638659

639660
#endif
@@ -821,6 +842,12 @@ void ba_transport_unref(struct ba_transport *t) {
821842
free(t->sco.ofono_dbus_path_modem);
822843
#endif
823844
}
845+
#if ENABLE_MIDI
846+
else if (t->profile & BA_TRANSPORT_PROFILE_MIDI) {
847+
if (t->midi.seq_parser != NULL)
848+
snd_midi_event_free(t->midi.seq_parser);
849+
}
850+
#endif
824851

825852
if (t->thread_manager_pipe[0] != -1)
826853
close(t->thread_manager_pipe[0]);
@@ -989,6 +1016,11 @@ void ba_transport_set_codec(
9891016
* errno is set to indicate the error. */
9901017
int ba_transport_start(struct ba_transport *t) {
9911018

1019+
#if ENABLE_MIDI
1020+
if (t->profile & BA_TRANSPORT_PROFILE_MIDI)
1021+
return midi_transport_start(t);
1022+
#endif
1023+
9921024
/* For A2DP Source profile only, it is possible that BlueZ will
9931025
* activate the transport following a D-Bus "Acquire" request before the
9941026
* client thread has completed the acquisition procedure by initializing
@@ -1033,6 +1065,11 @@ int ba_transport_start(struct ba_transport *t) {
10331065
* to call it from IO thread itself - it will cause deadlock! */
10341066
int ba_transport_stop(struct ba_transport *t) {
10351067

1068+
#if ENABLE_MIDI
1069+
if (t->profile & BA_TRANSPORT_PROFILE_MIDI)
1070+
return midi_transport_stop(t);
1071+
#endif
1072+
10361073
if (t->profile & BA_TRANSPORT_PROFILE_MASK_A2DP) {
10371074
if (ba_transport_pcm_state_check_terminated(&t->a2dp.pcm) &&
10381075
ba_transport_pcm_state_check_terminated(&t->a2dp.pcm_bc))
@@ -1110,6 +1147,11 @@ int ba_transport_stop_if_no_clients(struct ba_transport *t) {
11101147

11111148
int ba_transport_acquire(struct ba_transport *t) {
11121149

1150+
#if ENABLE_MIDI
1151+
if (t->profile & BA_TRANSPORT_PROFILE_MIDI)
1152+
return t->acquire(t);
1153+
#endif
1154+
11131155
bool acquired = false;
11141156
int fd = -1;
11151157

@@ -1165,6 +1207,11 @@ int ba_transport_acquire(struct ba_transport *t) {
11651207

11661208
int ba_transport_release(struct ba_transport *t) {
11671209

1210+
#if ENABLE_MIDI
1211+
if (t->profile & BA_TRANSPORT_PROFILE_MIDI)
1212+
return t->release(t);
1213+
#endif
1214+
11681215
int ret = 0;
11691216

11701217
pthread_mutex_lock(&t->bt_fd_mtx);

src/ba-transport.h

+22
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
#include <stdint.h>
2323
#include <time.h>
2424

25+
#include <alsa/asoundlib.h>
26+
#include <glib.h>
27+
2528
#include "a2dp.h"
2629
#include "ba-device.h"
2730
#include "ba-transport-pcm.h"
31+
#include "ble-midi.h"
2832
#include "bluez.h"
2933
#include "shared/a2dp-codecs.h"
3034

@@ -172,11 +176,29 @@ struct ba_transport {
172176
#if ENABLE_MIDI
173177
struct {
174178

179+
/* ALSA sequencer. */
180+
snd_seq_t *seq;
181+
/* Associated sequencer port. */
182+
int seq_port;
183+
184+
/* ALSA MIDI event parser. */
185+
snd_midi_event_t *seq_parser;
186+
175187
/* BLE-MIDI input link */
176188
int ble_fd_write;
177189
/* BLE-MIDI output (notification) link */
178190
int ble_fd_notify;
179191

192+
/* BLE-MIDI parser for the incoming data. */
193+
struct ble_midi_dec ble_decoder;
194+
/* BLE-MIDI parser for the outgoing data. */
195+
struct ble_midi_enc ble_encoder;
196+
197+
/* Watch associated with the BLE-MIDI link. */
198+
GSource *watch_ble;
199+
/* Watch associated with ALSA sequencer. */
200+
GSource *watch_seq;
201+
180202
} midi;
181203
#endif
182204

src/bluez-midi.c

+7
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535
#include "ba-adapter.h"
3636
#include "ba-device.h"
3737
#include "ba-transport.h"
38+
#include "ble-midi.h"
3839
#include "bluealsa-config.h"
3940
#include "bluez-iface.h"
4041
#include "dbus.h"
42+
#include "midi.h"
4143
#include "utils.h"
4244
#include "shared/bluetooth.h"
4345
#include "shared/defs.h"
@@ -208,6 +210,8 @@ static void bluez_midi_characteristic_acquire_write(
208210

209211
/* TODO: Find a way to detect "device" disconnection condition. */
210212

213+
midi_transport_start_watch_ble_midi(t);
214+
211215
GUnixFDList *fd_list = g_unix_fd_list_new_from_array(&fds[1], 1);
212216
g_dbus_method_invocation_return_value_with_unix_fd_list(inv,
213217
g_variant_new("(hq)", 0, mtu), fd_list);
@@ -262,6 +266,7 @@ static void bluez_midi_characteristic_acquire_notify(
262266
debug("New BLE-MIDI notify link (MTU: %u): %d", mtu, fds[0]);
263267
app->notify_acquired = true;
264268
t->midi.ble_fd_notify = fds[0];
269+
ble_midi_encode_set_mtu(&t->midi.ble_encoder, mtu);
265270
t->mtu_write = mtu;
266271

267272
/* Setup IO watch for checking HUP condition on the socket. HUP means
@@ -384,6 +389,8 @@ GDBusObjectManagerServer *bluez_midi_app_new(
384389
error("Couldn't create local MIDI transport: %s", strerror(errno));
385390
else if (ba_transport_acquire(t) == -1)
386391
error("Couldn't acquire local MIDI transport: %s", strerror(errno));
392+
else if (ba_transport_start(t) == -1)
393+
error("Couldn't start local MIDI transport: %s", strerror(errno));
387394
app->t = t;
388395

389396
GDBusObjectManagerServer *manager = g_dbus_object_manager_server_new(path);

0 commit comments

Comments
 (0)