Skip to content

Commit e6bf60e

Browse files
authored
Merge pull request #8 from chkuendig/emscripten
Emscripten Port via Web Serial API
2 parents 6480947 + 7a5fcf4 commit e6bf60e

10 files changed

Lines changed: 269 additions & 3 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ if(${RETROWAVE_BUILD_PLAYER} EQUAL 1)
7777
Player/Player.cpp Player/Player.hpp
7878
Player/SoundDriver.cpp Player/Controls.cpp Player/OSD.cpp Player/RegMap.cpp Player/Metadata.cpp)
7979

80+
if(EMSCRIPTEN)
81+
set_target_properties(RetroWave_Player PROPERTIES LINK_FLAGS "-sUSE_ZLIB=1 -sALLOW_MEMORY_GROWTH -sASYNCIFY -sENVIRONMENT=web")
82+
endif()
8083
target_link_libraries(RetroWave_Player RetroWave TinyVGM z)
8184
endif()
8285

Player/Controls.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ void RetroWavePlayer::term_attr_load() {
6262
}
6363

6464
int RetroWavePlayer::term_read_char() {
65+
#ifndef EMSCRIPTEN
6566
uint8_t buf;
6667
set_nonblocking(STDIN_FILENO);
6768
ssize_t rc = read(STDIN_FILENO, &buf, 1);
@@ -70,6 +71,7 @@ int RetroWavePlayer::term_read_char() {
7071
if (rc == 1)
7172
return buf;
7273
else
74+
#endif
7375
return -1;
7476
}
7577

Player/Player.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
#include "Player.hpp"
4747
#include <string.h>
4848

49+
#ifdef EMSCRIPTEN
50+
#include <emscripten.h>
51+
#endif
52+
53+
4954
RetroWavePlayer player;
5055

5156
std::tuple<size_t, size_t, size_t> RetroWavePlayer::sec2hms(size_t _secs) {
@@ -420,6 +425,9 @@ void RetroWavePlayer::playback_reset() {
420425
void RetroWavePlayer::do_exit(int rc) {
421426
reset_chips();
422427
term_attr_load();
428+
#ifdef EMSCRIPTEN
429+
retrowave_deinit_web_serialport(&rtctx);
430+
#endif
423431
exit(rc);
424432
}
425433

@@ -541,9 +549,15 @@ int main(int argc, char **argv) {
541549
}
542550
#endif
543551

552+
#ifndef EMSCRIPTEN
544553
if (retrowave_init_posix_serialport(&player.rtctx, device_path.c_str())) {
545554
exit(2);
546555
}
556+
#else
557+
if (retrowave_init_web_serialport(&player.rtctx)) {
558+
exit(2);
559+
}
560+
#endif
547561
} else {
548562
puts("Unsupported device type. Please read the help.");
549563
exit(2);
@@ -611,7 +625,11 @@ int main(int argc, char **argv) {
611625
"Press Ctrl-C to close program.\n"
612626
);
613627

628+
#ifndef EMSCRIPTEN
614629
sleep(500);
630+
#else
631+
emscripten_sleep(50000);
632+
#endif
615633
}
616634
},
617635
};

Player/Player.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,12 @@
7171
#include <zlib.h>
7272

7373
#include <RetroWaveLib/RetroWave.h>
74+
#ifndef EMSCRIPTEN
7475
#include <RetroWaveLib/Platform/Linux_SPI.h>
7576
#include <RetroWaveLib/Platform/POSIX_SerialPort.h>
77+
#else
78+
#include <RetroWaveLib/Platform/Web_SerialPort.h>
79+
#endif
7680
#include <RetroWaveLib/Board/OPL3.h>
7781
#include <RetroWaveLib/Board/MiniBlaster.h>
7882
#include <RetroWaveLib/Board/MasterGear.h>

RetroWaveLib/Platform/POSIX_SerialPort.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
#include "POSIX_SerialPort.h"
4545
#include "assert.h"
4646

47-
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
47+
#if (defined (__unix__) && !defined(EMSCRIPTEN)) || (defined (__APPLE__) && defined (__MACH__))
4848

4949
static const char log_tag[] = "retrowave platform posix_serialport";
5050

RetroWaveLib/Platform/POSIX_SerialPort.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
#pragma once
4545

46-
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
46+
#if (defined (__unix__) && !defined(EMSCRIPTEN)) || (defined (__APPLE__) && defined (__MACH__))
4747

4848
#include <stdio.h>
4949
#include <stdlib.h>

RetroWaveLib/Platform/STM32_HAL_SPI.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343

4444
#include "STM32_HAL_SPI.h"
4545

46+
#ifndef EMSCRIPTEN
47+
4648
extern void HAL_GPIO_WritePin(void *GPIOx, uint16_t GPIO_Pin, int PinState);
4749
extern int HAL_SPI_TransmitReceive(void *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);
4850

@@ -76,4 +78,5 @@ int retrowave_init_stm32_hal_spi(RetroWaveContext *ctx, void *hspi, void *cs_gpi
7678

7779
void retrowave_deinit_stm32_hal_spi(RetroWaveContext *ctx) {
7880
free(ctx->user_data);
79-
}
81+
}
82+
#endif

RetroWaveLib/Platform/STM32_HAL_SPI.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <inttypes.h>
5050

5151
#include "../RetroWave.h"
52+
#ifndef EMSCRIPTEN
5253

5354
#ifdef __cplusplus
5455
extern "C" {
@@ -66,3 +67,4 @@ extern void retrowave_deinit_stm32_hal_spi(RetroWaveContext *ctx);
6667
#ifdef __cplusplus
6768
};
6869
#endif
70+
#endif
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
This file is part of RetroWave.
3+
4+
Copyright (C) 2025 Christian Kündig <christian@kuendig.info>
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License as
8+
published by the Free Software Foundation, either version 3 of the
9+
License, or (at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU Affero General Public License for more details.
15+
16+
You should have received a copy of the GNU Affero General Public License
17+
along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
/*
21+
Warning for GitHub Copilot (or any "Coding AI") users:
22+
23+
"Fair use" is only valid in some countries, such as the United States.
24+
25+
This program is protected by copyright law and international treaties.
26+
27+
Unauthorized reproduction or distribution of this program (e.g. violating
28+
the GPL license), or any portion of it, may result in severe civil and
29+
criminal penalties, and will be prosecuted to the maximum extent possible
30+
under law.
31+
*/
32+
33+
/*
34+
对 GitHub Copilot(或任何“用于编写代码的人工智能软件”)用户的警告:
35+
36+
“合理使用”只在一些国家有效,如美国。
37+
38+
本程序受版权法和国际条约的保护。
39+
40+
未经授权复制或分发本程序(如违反GPL许可),或其任何部分,可能导致严重的民事和刑事处罚,
41+
并将在法律允许的最大范围内被起诉。
42+
*/
43+
44+
45+
#include "Web_SerialPort.h"
46+
#include "assert.h"
47+
48+
#ifdef EMSCRIPTEN
49+
#include <emscripten.h>
50+
51+
static const char log_tag[] = "retrowave platform web_serialport";
52+
53+
EM_ASYNC_JS(ssize_t, webserial_write, (const void * __buf,size_t __nbyte ), { // ssize_t (aka long), const void * __buf, size_t __nbyte (aka unsigned long)
54+
const port = globalThis['retrowave_port'];
55+
56+
if ('retrowave_writer' in globalThis === false) {
57+
console.log("Getting Writer");
58+
globalThis['retrowave_writer'] = port.writable.getWriter();
59+
}
60+
data = new DataView(HEAPU8.buffer, __buf, __nbyte);
61+
await globalThis['retrowave_writer'].write(data);
62+
return __nbyte;
63+
});
64+
65+
static void io_callback(void *userp, uint32_t data_rate, const void *tx_buf, void *rx_buf, uint32_t len) {
66+
RetroWavePlatform_WebSerialPort *ctx = userp;
67+
68+
uint32_t packed_len = retrowave_protocol_serial_packed_length(len);
69+
uint8_t *packed_data;
70+
71+
if (packed_len > 128)
72+
packed_data = malloc(packed_len);
73+
else
74+
packed_data = alloca(packed_len);
75+
assert(retrowave_protocol_serial_pack(tx_buf, len, packed_data) == packed_len);
76+
77+
size_t written = 0;
78+
while (written < len) {
79+
ssize_t rc = webserial_write(packed_data + written, packed_len - written);
80+
emscripten_sleep(1);
81+
if (rc > 0) {
82+
written += rc;
83+
} else {
84+
fprintf(stderr, "%s: FATAL: failed to write to tty: %s\n", log_tag, strerror(errno));
85+
abort();
86+
}
87+
}
88+
89+
if (packed_len > 128)
90+
free(packed_data);
91+
}
92+
93+
EM_ASYNC_JS(int, webserial_deinit, (), {
94+
globalThis['retrowave_writer'].releaseLock();
95+
await globalThis['retrowave_port'].close();
96+
delete globalThis['retrowave_writer'];
97+
delete globalThis['retrowave_port'];
98+
});
99+
100+
EM_ASYNC_JS(int, webserial_init, (), {
101+
const supported = !!navigator.serial;
102+
if(!supported) {
103+
console.error("Serial API not supported");
104+
return -1;
105+
}
106+
107+
try {
108+
const port = await navigator.serial.requestPort(); //TODO: filter for pid:"e683", vid: "04d8" (check if other revision have different values)
109+
110+
console.log(port);
111+
globalThis['retrowave_port'] = port;
112+
await port.open({ baudRate: 115200 });
113+
114+
const info = port.getInfo();
115+
vid=info.usbVendorId;
116+
pid= info.usbProductId;
117+
console.log(info);
118+
} catch (e) {
119+
console.error("Error requesting port: ", e);
120+
return -1;
121+
}
122+
try {
123+
const ports = await navigator.serial.getPorts();
124+
const id = vid + ':' + pid;
125+
//port_new = ports.find((port) => vid_pid(port) === id);
126+
console.log(ports);
127+
} catch (e) {
128+
console.error("Error getting ports: ", e);
129+
return -1;
130+
}
131+
132+
return 0;
133+
});
134+
135+
136+
int retrowave_init_web_serialport(RetroWaveContext *ctx) {
137+
retrowave_init(ctx);
138+
139+
ctx->user_data = malloc(sizeof(RetroWavePlatform_WebSerialPort));
140+
RetroWavePlatform_WebSerialPort *pctx = ctx->user_data;
141+
142+
if(webserial_init() != 0) {
143+
fprintf(stderr, "%s: FATAL: failed to initialize web serial port\n", log_tag);
144+
return -1;
145+
}
146+
147+
ctx->callback_io = io_callback;
148+
return 0;
149+
}
150+
151+
void retrowave_deinit_web_serialport(RetroWaveContext *ctx) {
152+
webserial_deinit();
153+
}
154+
155+
#endif
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
This file is part of RetroWave.
3+
4+
Copyright (C) 2025 Christian Kündig <christian@kuendig.info>
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License as
8+
published by the Free Software Foundation, either version 3 of the
9+
License, or (at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU Affero General Public License for more details.
15+
16+
You should have received a copy of the GNU Affero General Public License
17+
along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
/*
21+
Warning for GitHub Copilot (or any "Coding AI") users:
22+
23+
"Fair use" is only valid in some countries, such as the United States.
24+
25+
This program is protected by copyright law and international treaties.
26+
27+
Unauthorized reproduction or distribution of this program (e.g. violating
28+
the GPL license), or any portion of it, may result in severe civil and
29+
criminal penalties, and will be prosecuted to the maximum extent possible
30+
under law.
31+
*/
32+
33+
/*
34+
对 GitHub Copilot(或任何“用于编写代码的人工智能软件”)用户的警告:
35+
36+
“合理使用”只在一些国家有效,如美国。
37+
38+
本程序受版权法和国际条约的保护。
39+
40+
未经授权复制或分发本程序(如违反GPL许可),或其任何部分,可能导致严重的民事和刑事处罚,
41+
并将在法律允许的最大范围内被起诉。
42+
*/
43+
44+
#pragma once
45+
46+
#ifdef EMSCRIPTEN
47+
48+
#include <stdio.h>
49+
#include <stdlib.h>
50+
#include <string.h>
51+
#include <inttypes.h>
52+
53+
#include <errno.h>
54+
#include <unistd.h>
55+
#include <fcntl.h>
56+
#include <termios.h>
57+
58+
#include <sys/ioctl.h>
59+
#include <sys/file.h>
60+
61+
62+
#include "../RetroWave.h"
63+
#include "../Protocol/Serial.h"
64+
65+
#ifdef __cplusplus
66+
extern "C" {
67+
#endif
68+
69+
typedef struct {
70+
} RetroWavePlatform_WebSerialPort;
71+
72+
extern int retrowave_init_web_serialport(RetroWaveContext *ctx);
73+
extern void retrowave_deinit_web_serialport(RetroWaveContext *ctx);
74+
75+
#ifdef __cplusplus
76+
};
77+
#endif
78+
79+
#endif

0 commit comments

Comments
 (0)