From 6d25aad16bb6014c2ca74bd0d96a0efe7b0b11b6 Mon Sep 17 00:00:00 2001 From: beeanyew Date: Tue, 18 May 2021 13:53:01 +0200 Subject: [PATCH] Adapt piaudio to work on PiStorm + A314 emulation Maybe, maybe not, I have no idea. Untested, and will remain so, by me. --- a314/files_pi/.asoundrc | 17 + a314/files_pi/piaudio.py | 252 ++++++++++++++ a314/software-amiga/piaudio | Bin 0 -> 12000 bytes a314/software-amiga/piaudio_pistorm/README.md | 13 + a314/software-amiga/piaudio_pistorm/build.bat | 1 + a314/software-amiga/piaudio_pistorm/piaudio.c | 319 ++++++++++++++++++ 6 files changed, 602 insertions(+) create mode 100644 a314/files_pi/.asoundrc create mode 100644 a314/files_pi/piaudio.py create mode 100644 a314/software-amiga/piaudio create mode 100644 a314/software-amiga/piaudio_pistorm/README.md create mode 100644 a314/software-amiga/piaudio_pistorm/build.bat create mode 100644 a314/software-amiga/piaudio_pistorm/piaudio.c diff --git a/a314/files_pi/.asoundrc b/a314/files_pi/.asoundrc new file mode 100644 index 0000000..283f999 --- /dev/null +++ b/a314/files_pi/.asoundrc @@ -0,0 +1,17 @@ +pcm.amiga { + type plug + slave { + pcm { + type file + format raw + file "/tmp/piaudio_pipe" + slave.pcm null + } + format S8 + rate 18000 + channels 2 + } + hint { + description "Play audio to Amiga using A314" + } +} diff --git a/a314/files_pi/piaudio.py b/a314/files_pi/piaudio.py new file mode 100644 index 0000000..113a4ce --- /dev/null +++ b/a314/files_pi/piaudio.py @@ -0,0 +1,252 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 Niklas Ekström + +import fcntl +import logging +import os +import select +import socket +import struct +import sys + +fcntl.F_SETPIPE_SZ = 1031 + +logging.basicConfig(format = '%(levelname)s, %(asctime)s, %(name)s, line %(lineno)d: %(message)s') +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +MSG_REGISTER_REQ = 1 +MSG_REGISTER_RES = 2 +MSG_DEREGISTER_REQ = 3 +MSG_DEREGISTER_RES = 4 +MSG_READ_MEM_REQ = 5 +MSG_READ_MEM_RES = 6 +MSG_WRITE_MEM_REQ = 7 +MSG_WRITE_MEM_RES = 8 +MSG_CONNECT = 9 +MSG_CONNECT_RESPONSE = 10 +MSG_DATA = 11 +MSG_EOS = 12 +MSG_RESET = 13 + +def wait_for_msg(): + header = b'' + while len(header) < 9: + data = drv.recv(9 - len(header)) + if not data: + logger.error('Connection to a314d was closed, terminating.') + exit(-1) + header += data + (plen, stream_id, ptype) = struct.unpack('=IIB', header) + payload = b'' + while len(payload) < plen: + data = drv.recv(plen - len(payload)) + if not data: + logger.error('Connection to a314d was closed, terminating.') + exit(-1) + payload += data + return (stream_id, ptype, payload) + +def send_register_req(name): + m = struct.pack('=IIB', len(name), 0, MSG_REGISTER_REQ) + name + drv.sendall(m) + +def send_read_mem_req(address, length): + m = struct.pack('=IIBII', 8, 0, MSG_READ_MEM_REQ, address, length) + drv.sendall(m) + +def read_mem(address, length): + send_read_mem_req(address, length) + stream_id, ptype, payload = wait_for_msg() + if ptype != MSG_READ_MEM_RES: + logger.error('Expected MSG_READ_MEM_RES but got %s. Shutting down.', ptype) + exit(-1) + return payload + +def send_write_mem_req(address, data): + m = struct.pack('=IIBI', 4 + len(data), 0, MSG_WRITE_MEM_REQ, address) + data + drv.sendall(m) + +def write_mem(address, data): + send_write_mem_req(address, data) + stream_id, ptype, payload = wait_for_msg() + if ptype != MSG_WRITE_MEM_RES: + logger.error('Expected MSG_WRITE_MEM_RES but got %s. Shutting down.', ptype) + exit(-1) + +def send_connect_response(stream_id, result): + m = struct.pack('=IIBB', 1, stream_id, MSG_CONNECT_RESPONSE, result) + drv.sendall(m) + +def send_data(stream_id, data): + m = struct.pack('=IIB', len(data), stream_id, MSG_DATA) + data + drv.sendall(m) + +def send_eos(stream_id): + m = struct.pack('=IIB', 0, stream_id, MSG_EOS) + drv.sendall(m) + +def send_reset(stream_id): + m = struct.pack('=IIB', 0, stream_id, MSG_RESET) + drv.sendall(m) + +current_stream_id = None +first_msg = True +raw_received = b'' +is_empty = [True, True] + +def process_msg_data(payload): + global ptrs, first_msg, raw_received + + if first_msg: + ptrs = struct.unpack('>II', payload) + logger.debug('Received pointers %s', ptrs) + first_msg = False + return + + buf_index = payload[0] + + if len(raw_received) < 900*2: + if not is_empty[buf_index]: + data = b'\x00' * (900*2) + send_write_mem_req(ptrs[buf_index], data) + is_empty[buf_index] = True + else: + ldata = raw_received[0:900*2:2] + rdata = raw_received[1:900*2:2] + data = ldata + rdata + raw_received = raw_received[900*2:] + send_write_mem_req(ptrs[buf_index], data) + is_empty[buf_index] = False + +def process_drv_msg(stream_id, ptype, payload): + global current_stream_id, first_msg + + if ptype == MSG_CONNECT: + if payload == b'piaudio' and current_stream_id is None: + logger.info('Amiga connected') + current_stream_id = stream_id + first_msg = True + send_connect_response(stream_id, 0) + else: + send_connect_response(stream_id, 3) + elif current_stream_id == stream_id: + if ptype == MSG_DATA: + process_msg_data(payload) + elif ptype == MSG_EOS: + pass + elif ptype == MSG_RESET: + current_stream_id = None + logger.info('Amiga disconnected') + +done = False + +try: + idx = sys.argv.index('-ondemand') +except ValueError: + idx = -1 + +if idx != -1: + fd = int(sys.argv[idx + 1]) + drv = socket.socket(fileno=fd) +else: + drv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + drv.connect(('localhost', 7110)) + drv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + + send_register_req(b'piaudio') + _, _, payload = wait_for_msg() + if payload[0] != 1: + logger.error('Unable to register piaudio with driver, shutting down') + drv.close() + done = True + +rbuf = b'' + +PIPE_NAME = '/tmp/piaudio_pipe' + +if not done: + exists = True + try: + if (os.stat(PIPE_NAME).st_mode & 0o170000) != 0o10000: + logger.error('A file that is not a named pipe exists at ' + PIPE_NAME) + done = True + except: + exists = False + +if not done and not exists: + try: + os.mkfifo(PIPE_NAME) + except: + logger.error('Unable to create named pipe at ' + PIPE_NAME) + done = True + +if not done: + try: + pipe_fd = os.open(PIPE_NAME, os.O_RDONLY | os.O_NONBLOCK) + fcntl.fcntl(pipe_fd, fcntl.F_SETPIPE_SZ, 4096) + except: + logger.error('Unable to open named pipe at ' + PIPE_NAME) + done = True + +if not done: + logger.info('piaudio service is running') + +while not done: + sel_fds = [drv] + + if idx == -1: + sel_fds.append(sys.stdin) + + if len(raw_received) < 900*2: + sel_fds.append(pipe_fd) + + rfd, wfd, xfd = select.select(sel_fds, [], [], 5.0) + + for fd in rfd: + if fd == sys.stdin: + line = sys.stdin.readline() + if not line or line.startswith('quit'): + if current_stream_id is not None: + send_reset(current_stream_id) + drv.close() + done = True + elif fd == drv: + buf = drv.recv(1024) + if not buf: + if current_stream_id is not None: + send_reset(current_stream_id) + drv.close() + done = True + else: + rbuf += buf + while True: + if len(rbuf) < 9: + break + + (plen, stream_id, ptype) = struct.unpack('=IIB', rbuf[:9]) + if len(rbuf) < 9 + plen: + break + + rbuf = rbuf[9:] + payload = rbuf[:plen] + rbuf = rbuf[plen:] + + process_drv_msg(stream_id, ptype, payload) + elif fd == pipe_fd: + data = os.read(pipe_fd, 900*2) + + if len(data) == 0: + os.close(pipe_fd) + + l = len(raw_received) + c = l // (900*2) + if c * 900*2 < l: + raw_received += b'\x00' * ((c + 1) * 900*2 - l) + + pipe_fd = os.open(PIPE_NAME, os.O_RDONLY | os.O_NONBLOCK) + fcntl.fcntl(pipe_fd, fcntl.F_SETPIPE_SZ, 4096) + else: + raw_received += data diff --git a/a314/software-amiga/piaudio b/a314/software-amiga/piaudio new file mode 100644 index 0000000000000000000000000000000000000000..5aec55ec55ed677a2ac76dcd4ddb956b08da73e9 GIT binary patch literal 12000 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: +``` + +The file *.asoundrc* should be stored in `/home/pi` on the RPi. Most programs that play audio on the RPi can then be used. One such program is mpg123, which is started as: +``` +mpg123 -a amiga song.mp3 +``` diff --git a/a314/software-amiga/piaudio_pistorm/build.bat b/a314/software-amiga/piaudio_pistorm/build.bat new file mode 100644 index 0000000..9cde638 --- /dev/null +++ b/a314/software-amiga/piaudio_pistorm/build.bat @@ -0,0 +1 @@ +vc piaudio.c -lamiga -o ../piaudio diff --git a/a314/software-amiga/piaudio_pistorm/piaudio.c b/a314/software-amiga/piaudio_pistorm/piaudio.c new file mode 100644 index 0000000..82c23fd --- /dev/null +++ b/a314/software-amiga/piaudio_pistorm/piaudio.c @@ -0,0 +1,319 @@ +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +#include "../../a314device/a314.h" +#include "../../a314device/proto_a314.h" + +#include + +#define SERVICE_NAME "piaudio" + +#define FREQ 18000 +#define BUFFER_LEN_MS 50 +#define SAMPLES (FREQ * BUFFER_LEN_MS / 1000) + +#define R0 1 +#define L0 2 +#define L1 4 +#define R1 8 + +#define LEFT_CHAN_MASK (L0 | L1) +#define RIGHT_CHAN_MASK (R0 | R1) + +#define LEFT 0 +#define RIGHT 1 + +struct MsgPort *sync_mp = NULL; +struct MsgPort *async_mp = NULL; + +struct A314_IORequest *sync_a314_req = NULL; +struct A314_IORequest *write_a314_req = NULL; + +struct Library *A314Base; + +char *audio_buffers[4] = { NULL, NULL, NULL, NULL }; + +struct IOAudio *sync_audio_req = NULL; +struct IOAudio *async_audio_req[4] = { NULL, NULL, NULL, NULL }; + +ULONG allocated_channels; + +BOOL a314_device_open = FALSE; +BOOL audio_device_open = FALSE; +BOOL stream_open = FALSE; +BOOL pending_a314_write = FALSE; + +ULONG socket; +int back_index = 0; +char awbuf[8]; + +void start_a314_cmd(struct MsgPort *reply_port, struct A314_IORequest *ior, UWORD cmd, char *buffer, int length) +{ + ior->a314_Request.io_Message.mn_ReplyPort = reply_port; + ior->a314_Request.io_Command = cmd; + ior->a314_Request.io_Error = 0; + ior->a314_Socket = socket; + ior->a314_Buffer = buffer; + ior->a314_Length = length; + SendIO((struct IORequest *)ior); +} + +BYTE a314_connect(char *name) +{ + socket = time(NULL); + start_a314_cmd(sync_mp, sync_a314_req, A314_CONNECT, name, strlen(name)); + Wait(1L << sync_mp->mp_SigBit); + GetMsg(sync_mp); + return sync_a314_req->a314_Request.io_Error; +} + +BYTE a314_write(char *buffer, int length) +{ + start_a314_cmd(sync_mp, sync_a314_req, A314_WRITE, buffer, length); + Wait(1L << sync_mp->mp_SigBit); + GetMsg(sync_mp); + return sync_a314_req->a314_Request.io_Error; +} + +BYTE a314_eos() +{ + start_a314_cmd(sync_mp, sync_a314_req, A314_EOS, NULL, 0); + Wait(1L << sync_mp->mp_SigBit); + GetMsg(sync_mp); + return sync_a314_req->a314_Request.io_Error; +} + +BYTE a314_reset() +{ + start_a314_cmd(sync_mp, sync_a314_req, A314_RESET, NULL, 0); + Wait(1L << sync_mp->mp_SigBit); + GetMsg(sync_mp); + return sync_a314_req->a314_Request.io_Error; +} + +void start_a314_write(char *buffer, int length) +{ + start_a314_cmd(async_mp, write_a314_req, A314_WRITE, buffer, length); + pending_a314_write = TRUE; +} + +void submit_async_audio_req(int index) +{ + ULONG mask = ((index & 1) == LEFT) ? LEFT_CHAN_MASK : RIGHT_CHAN_MASK; + ULONG unit = allocated_channels & mask; + + async_audio_req[index]->ioa_Request.io_Message.mn_ReplyPort = async_mp; + async_audio_req[index]->ioa_Request.io_Command = CMD_WRITE; + async_audio_req[index]->ioa_Request.io_Flags = ADIOF_PERVOL; + async_audio_req[index]->ioa_Request.io_Unit = (void*)unit; + async_audio_req[index]->ioa_Data = audio_buffers[index]; + async_audio_req[index]->ioa_Length = SAMPLES; + async_audio_req[index]->ioa_Period = 197; + async_audio_req[index]->ioa_Volume = 64; + async_audio_req[index]->ioa_Cycles = 1; + BeginIO((struct IORequest *)async_audio_req[index]); +} + +int main() +{ + SetTaskPri(FindTask(NULL), 50); + + sync_mp = CreatePort(NULL, 0); + if (!sync_mp) + { + printf("Unable to create sync reply message port\n"); + goto cleanup; + } + + async_mp = CreatePort(NULL, 0); + if (!async_mp) + { + printf("Unable to create async reply message port\n"); + goto cleanup; + } + + sync_a314_req = (struct A314_IORequest *)CreateExtIO(sync_mp, sizeof(struct A314_IORequest)); + write_a314_req = (struct A314_IORequest *)CreateExtIO(sync_mp, sizeof(struct A314_IORequest)); + if (!sync_a314_req || !write_a314_req) + { + printf("Unable to create A314_IORequest\n"); + goto cleanup; + } + + if (OpenDevice(A314_NAME, 0, (struct IORequest *)sync_a314_req, 0)) + { + printf("Unable to open a314.device\n"); + goto cleanup; + } + + a314_device_open = TRUE; + + A314Base = &(sync_a314_req->a314_Request.io_Device->dd_Library); + + memcpy(write_a314_req, sync_a314_req, sizeof(struct A314_IORequest)); + + audio_buffers[0] = AllocMem(SAMPLES * 2, MEMF_FAST | MEMF_CHIP | MEMF_CLEAR); + audio_buffers[2] = AllocMem(SAMPLES * 2, MEMF_FAST | MEMF_CHIP | MEMF_CLEAR); + if (!audio_buffers[0] || !audio_buffers[2]) + { + printf("Unable to allocate audio buffers in A314 chip memory\n"); + goto cleanup; + } + + audio_buffers[1] = audio_buffers[0] + SAMPLES; + audio_buffers[3] = audio_buffers[2] + SAMPLES; + + sync_audio_req = (struct IOAudio *)CreateExtIO(sync_mp, sizeof(struct IOAudio)); + if (!sync_audio_req) + { + printf("Unable to allocate sync audio request\n"); + goto cleanup; + } + + int i; + for (i = 0; i < 4; i++) + { + async_audio_req[i] = AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC); + if (!async_audio_req[i]) + { + printf("Unable to allocate async audio request\n"); + goto cleanup; + } + } + + UBYTE which_channels[] = { L0 | R0, L0 | R1, L1 | R0, L1 | R1 }; + + sync_audio_req->ioa_Request.io_Message.mn_ReplyPort = sync_mp; + sync_audio_req->ioa_Request.io_Message.mn_Node.ln_Pri = 127; + sync_audio_req->ioa_Request.io_Command = ADCMD_ALLOCATE; + sync_audio_req->ioa_Request.io_Flags = ADIOF_NOWAIT; + sync_audio_req->ioa_AllocKey = 0; + sync_audio_req->ioa_Data = which_channels; + sync_audio_req->ioa_Length = sizeof(which_channels); + + if (OpenDevice(AUDIONAME, 0, (struct IORequest *)sync_audio_req, 0)) + { + printf("Unable to open audio.device\n"); + goto cleanup; + } + + audio_device_open = TRUE; + + allocated_channels = (ULONG)sync_audio_req->ioa_Request.io_Unit; + + for (i = 0; i < 4; i++) + memcpy(async_audio_req[i], sync_audio_req, sizeof(struct IOAudio)); + + if (a314_connect(SERVICE_NAME) != A314_CONNECT_OK) + { + printf("Unable to connect to piaudio service\n"); + goto cleanup; + } + + stream_open = TRUE; + + ULONG *buf_ptrs = (ULONG *)awbuf; + buf_ptrs[0] = TranslateAddressA314(audio_buffers[0]); + buf_ptrs[1] = TranslateAddressA314(audio_buffers[2]); + if (a314_write(awbuf, 8) != A314_WRITE_OK) + { + printf("Unable to write buffer pointers\n"); + goto cleanup; + } + + printf("PiAudio started, allocated channels: L%d, R%d\n", + (allocated_channels & LEFT_CHAN_MASK) == L0 ? 0 : 1, + (allocated_channels & RIGHT_CHAN_MASK) == R0 ? 0 : 1); + + sync_audio_req->ioa_Request.io_Command = CMD_STOP; + DoIO((struct IORequest *)sync_audio_req); + + submit_async_audio_req(back_index + LEFT); + submit_async_audio_req(back_index + RIGHT); + + sync_audio_req->ioa_Request.io_Command = CMD_START; + DoIO((struct IORequest *)sync_audio_req); + + int pending_audio_reqs = 2; + + ULONG portsig = 1L << async_mp->mp_SigBit; + + printf("Press ctrl-c to exit...\n"); + + while (TRUE) + { + if (pending_audio_reqs <= 2) + { + back_index ^= 2; + + submit_async_audio_req(back_index + LEFT); + submit_async_audio_req(back_index + RIGHT); + + pending_audio_reqs += 2; + + if (!pending_a314_write) + { + awbuf[0] = back_index == 0 ? 0 : 1; + start_a314_write(awbuf, 1); + } + } + + ULONG signal = Wait(SIGBREAKF_CTRL_C | portsig); + + if (signal & SIGBREAKF_CTRL_C) + break; + else if (signal & portsig) + { + struct Message *msg; + while (msg = GetMsg(async_mp)) + { + if (msg == (struct Message *)write_a314_req) + { + if (write_a314_req->a314_Request.io_Error == A314_WRITE_OK) + pending_a314_write = FALSE; + else + goto cleanup; + } + else + pending_audio_reqs--; + } + } + } + +cleanup: + if (stream_open) + a314_reset(); + if (audio_device_open) + CloseDevice((struct IORequest *)sync_audio_req); + for (i = 3; i >= 0; i--) + if (async_audio_req[i]) + FreeMem(async_audio_req[i], sizeof(struct IOAudio)); + if (sync_audio_req) + DeleteExtIO((struct IORequest *)sync_audio_req); + if (audio_buffers[2]) + FreeMem(audio_buffers[2], SAMPLES * 2); + if (audio_buffers[0]) + FreeMem(audio_buffers[0], SAMPLES * 2); + if (a314_device_open) + CloseDevice((struct IORequest *)sync_a314_req); + if (write_a314_req) + DeleteExtIO((struct IORequest *)write_a314_req); + if (sync_a314_req) + DeleteExtIO((struct IORequest *)sync_a314_req); + if (async_mp) + DeletePort(async_mp); + if (sync_mp) + DeletePort(sync_mp); + + return 0; +} -- 2.39.2