Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Play raw 16-bit signed little-endian audio to a Motif XF
To play audio through the Motif, connect to it on TCP port 5556 and send a
continuous stream of audio chunks, each consisting of the eight bytes
‘W’, ‘A’, ‘V’, ‘E’, 0xLL, 0xHH, 0x00, 0x00
followed by 0xHHLL bytes of stereo, 16-bit, signed, little-endian audio data.
0x8000 works well as a chunk size, and matches that used by the instrument
when recording.
The Motif has a playback buffer of size 0x180000 bytes, and will only start
playback once this buffer is full. If we close() the socket (or shutdown() the
sending side), it immediately dumps this buffer and stops playback.
On normal exit, the sample code pads the end of the playback data with
0x180000 zero bytes and closes the socket immediately afterwards. The result
of this is that we play right up to the end of the unpadded audio data and
then stop cleanly: the instrument dumps only the silent padding.
On SIGINT, however, the socket is closed without padding, so playback stops
promptly and buffered audio data is discarded.
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#define BUFFER 0x180000
#define CHUNK 0x8000
#define SERVICE "5556"
void die(const char *s) {
perror(s);
exit(EXIT_FAILURE);
}
int openstream(char *host, char *port) {
struct addrinfo *a, *ai, hints;
int sock, zero = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
if (getaddrinfo(host, port, &hints, &ai) < 0 || !ai) {
fprintf(stderr, "unknown host %s\n", host);
exit(EXIT_FAILURE);
}
for (a = ai; a != NULL; a = a->ai_next) {
sock = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
if (sock < 0)
continue;
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &zero, sizeof(zero)) < 0)
die("setsockopt SO_SNDBUF");
if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
break;
close(sock);
}
if (!a)
die("connect");
freeaddrinfo(ai);
return sock;
}
size_t readall(int fd, void *buf, size_t n) {
ssize_t m, p;
for (p = 0; p < n; p += m)
if ((m = read(fd, buf + p, n - p)) <= 0) {
if (errno != EAGAIN && errno != EINTR)
break;
m = 0;
}
return (size_t) p;
}
size_t writeall(int fd, void *buf, size_t n) {
ssize_t m, p;
for (p = 0; p < n; p += m)
if ((m = write(fd, buf + p, n - p)) <= 0) {
if (errno != EAGAIN && errno != EINTR)
break;
m = 0;
}
return (size_t) p;
}
void sigint_handler(int sig) {
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv) {
unsigned char buf[CHUNK + 8];
size_t n;
struct sigaction sa;
int sock;
if (argc != 2) {
fprintf(stderr, "\
Usage: %s MOTIF-HOSTNAME < AUDIO-FILE\n\
Connect to the specified Yamaha Motif synthesizer and play 44.1kHz stereo\n\
audio data in raw 16-bit signed little-endian format from stdin, finishing\n\
cleanly on EOF.\n\
", argv[0]);
return EXIT_FAILURE;
}
sock = openstream(argv[1], SERVICE);
sa.sa_flags = SA_RESETHAND | SA_RESTART;
sa.sa_handler = sigint_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
memcpy(buf, "WAVE\x00\x00\x00\x00", 8);
while ((n = readall(0, buf + 8, sizeof(buf) - 8)) > 0) {
buf[4] = n & 0xff;
buf[5] = (n >> 8) & 0xff;
if (writeall(sock, buf, n + 8) != n + 8)
die("socket write");
}
memset(buf + 8, 0, sizeof(buf) - 8);
buf[4] = (sizeof(buf) - 8) & 0xff;
buf[5] = ((sizeof(buf) - 8) >> 8) & 0xff;
for (n = 0; n < BUFFER; n += CHUNK)
if (writeall(sock, buf, sizeof(buf)) != sizeof(buf))
die("socket write");
close(sock);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment