Skip to content

Commit

Permalink
FEAT: initial audio device prototype for Windows (using XAudio2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Oldes committed Jul 5, 2023
1 parent dc1bcd0 commit fd5656d
Show file tree
Hide file tree
Showing 15 changed files with 602 additions and 68 deletions.
13 changes: 12 additions & 1 deletion make/rebol3.nest
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ core-files: [
%core/n-sets.c
%core/n-strings.c
%core/n-system.c
; %core/p-audio.c ;not implemented
; %core/p-audio.c ;optional, use: include-audio
%core/p-checksum.c
; %core/p-clipboard.c ;optional, use: include-clipboard (windows only!)
%core/p-console.c
Expand Down Expand Up @@ -490,12 +490,14 @@ include-midi: [
#if Windows? [
core-files: %core/p-midi.c
host-files: %os/win32/dev-midi.c
mezz-sys-files: %mezz/sys-ports-midi.reb
config: INCLUDE_MIDI_DEVICE
library: %winmm
]
#if macOS? [
core-files: %core/p-midi.c
host-files: %os/osx/dev-midi.c
mezz-sys-files: %mezz/sys-ports-midi.reb
config: INCLUDE_MIDI_DEVICE
frameworks: [CoreServices CoreMIDI]

Expand All @@ -519,6 +521,14 @@ include-serial: [
host-files: %os/posix/dev-serial.c
]
]
include-audio: [
#if Windows? [
core-files: %core/p-audio.c
host-files: %os/win32/dev-audio.c
mezz-sys-files: %mezz/sys-ports-audio.reb
config: INCLUDE_AUDIO_DEVICE
]
]

;- native utilities:
include-bincode: [core-files: %core/u-bincode.c]
Expand Down Expand Up @@ -927,6 +937,7 @@ include-rebol-bulk: [
:include-base36-encoding
:include-base85-encoding
:include-view
:include-audio
:include-midi
:include-serial

Expand Down
4 changes: 4 additions & 0 deletions make/tools/make-boot.reb
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,10 @@ emit {^/enum reb_console_modes ^{^/}
foreach word select data '*console-modes* [ emit-enum join "MODE_CONSOLE_" up-word word ]
emit-end

emit {^/enum reb_audio_modes ^{^/}
foreach word select data '*audio-modes* [ emit-enum join "MODE_AUDIO_" up-word word ]
emit-end

write-generated inc/gen-portmodes.h out

;----------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions src/boot/modes.reb
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ REBOL [
rts ;request-to-send
]

*audio-modes* [
play ; used to pass play/pause commands into audio device
]

12 changes: 11 additions & 1 deletion src/boot/sysobj.reb
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ dialects: construct [
rebcode:
]

schemes: object []
schemes: make block! 20 ; Block only before init-scheme! Than it is an object.

ports: object [
wait-list: [] ; List of ports to add to 'wait
Expand Down Expand Up @@ -340,6 +340,16 @@ standard: object [
stop-bits: 1
flow-control: none ;not supported on all systems
]

port-spec-audio: make port-spec-head [
scheme: 'audio
source: none
channels: 2
rate: 44100
bits: 16
sample-type: 1
loop-count: 0
]

file-info: construct [
name:
Expand Down
5 changes: 4 additions & 1 deletion src/boot/words.reb
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ owner
*port-modes* ;@@ modes are defined in modes.r file and these
*console-modes* ;@@ placeholders are replaced here by make-boot.r script
*serial-modes*
*audio-modes*

local-ip
local-port
Expand Down Expand Up @@ -362,4 +363,6 @@ chacha20-poly1305
tag-length
aad-length

file-checksum
file-checksum

xaudio-voice
5 changes: 4 additions & 1 deletion src/core/c-port.c
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ xx*/ REBINT Wait_Device(REBREQ *req, REBCNT timeout)
**
***********************************************************************/

#define MAX_SCHEMES 14 // max native schemes
#define MAX_SCHEMES 15 // max native schemes

typedef struct rebol_scheme_actions {
REBCNT sym;
Expand Down Expand Up @@ -633,6 +633,9 @@ SCHEME_ACTIONS *Scheme_Actions; // Initial Global (not threaded)
#ifdef INCLUDE_CLIPBOARD
Init_Clipboard_Scheme();
#endif
#ifdef INCLUDE_AUDIO_DEVICE
Init_Audio_Scheme();
#endif
#ifdef INCLUDE_MIDI_DEVICE
Init_MIDI_Scheme();
#endif
Expand Down
98 changes: 79 additions & 19 deletions src/core/p-audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2023 Rebol Open Source Developers
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -23,7 +24,7 @@
** Summary: Audio port interface
** Section: ports
** Author: Oldes
** Notes:
** Notes: prototype
**
***********************************************************************/

Expand All @@ -42,8 +43,9 @@
REBREQ *req;
REBINT result;
REBVAL *arg;
REBCNT refs = 0; // refinement argument flags
REBCNT len;
REBOOL sync = FALSE; // act synchronously
//REBOOL sync = FALSE; // act synchronously
REBVAL tmp;

port = Validate_Port_Value(port_value);
Expand All @@ -52,46 +54,104 @@

req = Use_Port_State(port, RDI_AUDIO, sizeof(REBREQ));

printf("Audio device action: %i - ", action);


switch (action) {
case A_WRITE:
puts("write");
break;
case A_READ:
puts("read");
case A_INSERT:
if (!(IS_BINARY(arg) || IS_VECTOR(arg) || IS_LOGIC(arg)))
Trap1(RE_INVALID_PORT_ARG, arg);

if (!IS_OPEN(req)) {
if (OS_DO_DEVICE(req, RDC_OPEN)) Trap_Port(RE_CANNOT_OPEN, port, req->error);
sync = TRUE;
//sync = TRUE;
}

REBVAL *data = BLK_SKIP(port, STD_PORT_DATA);
if (IS_LOGIC(arg)) {
if (VAL_LOGIC(arg) == FALSE) {
req->modify.mode = MODE_AUDIO_PLAY;
req->modify.value = FALSE;
result = OS_DO_DEVICE(req, RDC_MODIFY);
break;
}
if (IS_VECTOR(data) || IS_BINARY(data)) {
req->data = VAL_BIN_DATA(data);
req->length = VAL_TAIL(data) - VAL_INDEX(data);
if (IS_VECTOR(data)) {
req->length *= VAL_VEC_WIDTH(data); // length in raw bytes
}
}
else break;
}
// Issue the read request:
result = OS_DO_DEVICE(req, RDC_READ);
if (result < 0) Trap_Port(RE_READ_ERROR, port, req->error);
if (sync) OS_DO_DEVICE(req, RDC_CLOSE);
else {
VAL_SERIES(data) = VAL_SERIES(arg);
VAL_TYPE(data) = VAL_TYPE(arg);
PROTECT_SERIES(VAL_SERIES(data));

len = Partial(arg, 0, D_ARG(ARG_WRITE_LENGTH), 0);

req->data = VAL_BIN_DATA(arg);
req->length = len;

if (IS_VECTOR(arg)) {
// length in raw bytes
req->length *= VAL_VEC_WIDTH(arg);
}
}

result = OS_DO_DEVICE(req, RDC_WRITE);
if (result < 0) Trap_Port(RE_WRITE_ERROR, port, req->error);
//if (sync) OS_DO_DEVICE(req, RDC_CLOSE);
break;
case A_READ:
break;

case A_PICK: // FIRST - return result
puts("pick");
break;

case A_OPEN:
puts("open");
spec = OFV(port, STD_PORT_SPEC);
if (IS_OBJECT(spec)) {
arg = Obj_Value(spec, STD_PORT_SPEC_AUDIO_CHANNELS);
if (IS_INTEGER(arg)) {
req->audio.channels = VAL_INT32(arg);
}
arg = Obj_Value(spec, STD_PORT_SPEC_AUDIO_RATE);
if (IS_INTEGER(arg)) {
req->audio.rate = VAL_INT32(arg);
}
arg = Obj_Value(spec, STD_PORT_SPEC_AUDIO_BITS);
if (IS_INTEGER(arg)) {
req->audio.bits = VAL_INT32(arg);
}
arg = Obj_Value(spec, STD_PORT_SPEC_AUDIO_SAMPLE_TYPE);
if (IS_INTEGER(arg)) {
req->audio.type = VAL_INT32(arg);
}
arg = Obj_Value(spec, STD_PORT_SPEC_AUDIO_LOOP_COUNT);
if (IS_INTEGER(arg)) {
req->audio.loop_count = MIN(MAX_U32, VAL_UNT64(arg));
}
}
if (OS_DO_DEVICE(req, RDC_OPEN)) Trap_Port(RE_CANNOT_OPEN, port, req->error);
break;

case A_CLOSE:
puts("close");
OS_DO_DEVICE(req, RDC_CLOSE);
break;

case A_OPENQ:
puts("open?");
if (IS_OPEN(req)) return R_TRUE;
return R_FALSE;

case A_UPDATE:
// Update the port object after a READ or WRITE operation.
// This is normally called by the WAKE-UP function.
//arg = OFV(port, STD_PORT_DATA);
//if (req->command == RDC_WRITE) {
// SET_NONE(arg); // Write is done.
//}
return R_TRUE;
default:
puts("not supported command");
Trap_Action(REB_PORT, action);
}
return R_ARG1; //= port
Expand Down
8 changes: 8 additions & 0 deletions src/include/reb-device.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ enum {
RDI_MIDI,
RDI_CRYPT,
RDI_SERIAL,
RDI_AUDIO,
RDI_MAX,
RDI_LIMIT = 32
};
Expand Down Expand Up @@ -218,6 +219,13 @@ struct rebol_devreq {
u32 device_in; // requested device ID (1-based; 0 = none)
u32 device_out;
} midi;
struct {
u8 type;
u8 channels;
u16 bits;
u32 rate;
u32 loop_count;
} audio;
struct {
u32 mode;
u32 value;
Expand Down
48 changes: 48 additions & 0 deletions src/mezz/sys-ports-audio.reb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
REBOL [
System: "REBOL [R3] Language Interpreter and Run-time Environment"
Title: "REBOL 3 Boot Sys: Port and Scheme Functions"
Rights: {
Copyright 2012 REBOL Technologies
REBOL is a trademark of REBOL Technologies
}
License: {
Licensed under the Apache License, Version 2.0
See: http://www.apache.org/licenses/LICENSE-2.0
}
Context: sys
Note: {
The boot binding of this module is SYS then LIB deep.
Any non-local words not found in those contexts WILL BE
UNBOUND and will error out at runtime!
}
]

append/only system/schemes [
title: "Audio"
name: 'audio
spec: system/standard/port-spec-audio
init: func [port /local spec source wav] [
spec: port/spec
if all [
url? spec/ref
parse spec/ref ["audio:" copy source to end]
][
spec/source: as file! source
]
; make spec to be only with audio related keys
spec: make copy system/standard/port-spec-audio spec
if file? spec/source [
try/except [
wav: decode 'wav read spec/source
spec/channels: wav/channels
spec/rate: wav/rate
spec/bits: wav/bits
port/data: wav/data
][ return false ]
]
port/spec: :spec
;probe spec
;protect/words port/spec ; protect spec object keys of modification
true
]
]
Loading

0 comments on commit fd5656d

Please sign in to comment.