initial project setup
This commit is contained in:
commit
5315f102d6
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.idea
|
|
@ -0,0 +1,182 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6945cc5422176fc5e602e590c2878d2c2acd9a4fe20a4baa7c28022521698ec6"
|
||||
|
||||
[[package]]
|
||||
name = "binrw"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d479fa33e2335c8289267d60a2bbd30dae31f8ab04b8bd9fc3353958edccb83a"
|
||||
dependencies = [
|
||||
"array-init",
|
||||
"binrw_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binrw_derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac7f612479ad23cc83ce25201586316012a0f2d74a81244b5dbdf9baa3726b09"
|
||||
dependencies = [
|
||||
"owo-colors",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "modular-bitfield"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
|
||||
dependencies = [
|
||||
"modular-bitfield-impl",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "modular-bitfield-impl"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a61765925aec40abdb23812a3a1a01fafc6ffb9da22768b2ce665a9e84e527c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resound"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"binrw",
|
||||
"modular-bitfield",
|
||||
"sdl2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sdl2"
|
||||
version = "0.35.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f035f8e87735fa3a8437292be49fe6056450f7cbb13c230b4bcd1bdd7279421f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"sdl2-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sdl2-sys"
|
||||
version = "0.35.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94cb479353c0603785c834e2307440d83d196bf255f204f7f6741358de8d6a2f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "resound"
|
||||
description = "a userspace implementation of the EsounD protocol"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
#structopt = "0.3"
|
||||
binrw = "0.8"
|
||||
thiserror = "1"
|
||||
sdl2 = { version = "0.35", features = ["mixer"] }
|
||||
modular-bitfield = "0.11"
|
|
@ -0,0 +1,279 @@
|
|||
EsounD Protocol (Draft)
|
||||
Author: ymnk<ymnk@jcraft.com>
|
||||
Data: 2000-10-02
|
||||
===============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
This document describes the protocol in EsounD system.
|
||||
Unfortunately, any formal description about EsounD had not existed.
|
||||
So the author has tried to read the source code of EsounD and written this
|
||||
document. The author is also the author of JEsd, which is a re-implementation
|
||||
of EsounD in pure Java and this document is based on his knowledge,
|
||||
which had gotten in hacking JEsd.
|
||||
|
||||
|
||||
Connection Setup
|
||||
----------------
|
||||
The esd will wait for the TCP connection requests from EsounD compatible
|
||||
applications. In the default, esd will listen to the TCP port 16001.
|
||||
The client must send an initial byte of data to be authorized them-self
|
||||
and to identify the byte order to be employed.
|
||||
For authorization, client must send 'esd-key', which is a 16 byte data.
|
||||
For endian-ness, client must send a 4 byte data.
|
||||
If esd does not detect any error, '1' will be sent back and
|
||||
'0' will be sent back in error.
|
||||
|
||||
Requests
|
||||
--------
|
||||
lock:
|
||||
esd-key:ESDKEY
|
||||
w result:BOOLEAN
|
||||
|
||||
At first, esd will check if the client has the right to lock the device
|
||||
by esd-key. If that client has the right to do so, esd will lock the device
|
||||
and send back true. If not, false will be sent back
|
||||
|
||||
unlock:
|
||||
esd-key:ESDKEY
|
||||
w result:BOOLEAN
|
||||
|
||||
At first, esd will check if the client has the right to unlock the device
|
||||
by esd-key. If that client has the right to do so, esd will unlock the
|
||||
device and send back true. If not, false will be sent back
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
Syntactic Conventions
|
||||
--------------------
|
||||
All numbers are in decimal, unless prefixed with '0x', in which case
|
||||
they are in hexadecimal(base 16).
|
||||
|
||||
The general syntax used to describe data packets is:
|
||||
Name:
|
||||
encoded-form
|
||||
...
|
||||
encoded-form
|
||||
|
||||
For components described in the protocol descriptions as:
|
||||
name: TYPE
|
||||
w name: TYPE
|
||||
the encode-form are:
|
||||
N TYPE name
|
||||
and
|
||||
w N TYPE name
|
||||
N is the number of bytes in the data stream, and TYPE is the interpretation
|
||||
of those bytes. For example,
|
||||
result: BOOLEAN
|
||||
becomes:
|
||||
4 BOOLEAN result
|
||||
|
||||
For components with a static numeric value the encode-form is:
|
||||
N value name
|
||||
The value is always interpreted as a N-byte unsigned integer.
|
||||
|
||||
|
||||
Data Types
|
||||
----------
|
||||
CARD8: A single byte unsigned integer.
|
||||
CARD32: 32-bit unsigned integer
|
||||
ARRAY8: A collection of CARD8.
|
||||
ARRAY8(n): This is a ARRAY8, which includes 'n' elements.
|
||||
ENDIAN: This is a ARRAY8(4), which includes 'ENDN' or 'NDNE'.
|
||||
If the first element is 'E', data from clients is in big-endian.
|
||||
BOOLEAN: This is a CARD32 and includes '0' or '1'. '1' means true.
|
||||
ESDKEY: This is a ARRAY8(16), which includes 'esd-key'.
|
||||
ESDNAME: This is a ARRAY8(128), which includes 'esd-name'.
|
||||
ESDSTREAM: This is a infinite ARRAY8.
|
||||
FORMAT: This is a CARD32. Each bits in this data has following semantics,
|
||||
(format&0x000f)==0x0000 8bit data
|
||||
(format&0x000f)==0x0001 16bit data
|
||||
(format&0x00f0)==0x0010 mono
|
||||
(format&0x00f0)==0x0020 stereo
|
||||
(format&0x0f00)==0x0000 stream
|
||||
(format&0x0f00)==0x0100 sample
|
||||
(format&0x0f00)==0x0200 ADPCM
|
||||
(format&0xf000)==0x1000 play
|
||||
(format&0xf000)==0x0000 monitor for streams, stop for samples
|
||||
(format&0xf000)==0x2000 record for streams, loop for samples
|
||||
MODE: This is a CARD32, which is '0', '1', '2' or '3'.
|
||||
0 ERROR
|
||||
1 STANDBY
|
||||
2 AUTOSTANDBY
|
||||
3 RUNNING
|
||||
|
||||
|
||||
Packet Format
|
||||
-------------
|
||||
init:
|
||||
4 0 opcode
|
||||
16 ESDKEY esd-key
|
||||
4 ENDIAN 'ENDN' or 'NDNE'
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
|
||||
lock:
|
||||
4 1 opcode
|
||||
16 ESDKEY esd-key
|
||||
4 ENDIAN unused
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
unlock:
|
||||
4 2 opcode
|
||||
16 ESDKEY esd-key
|
||||
4 ENDIAN unused
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
stream-play:
|
||||
4 3 opcode
|
||||
4 FORMAT format
|
||||
4 CARD32 rate
|
||||
128 ESDNAME name
|
||||
? ESDSTREAM stream of PCM sound
|
||||
|
||||
//stream-mon: This protocol is not used for the remote esd.
|
||||
// 4 4 opcode
|
||||
// 4 FORMA format
|
||||
// 4 CARD3 rate
|
||||
// 128 ESDNAME name
|
||||
// w ? ESDSTREAM stream of PCM sound
|
||||
|
||||
stream-mon:
|
||||
4 5 opcode
|
||||
4 FORMAT format
|
||||
4 CARD32 rate
|
||||
128 ESDNAME name
|
||||
w ? ESDSTREAM stream of PCM sound
|
||||
|
||||
sample-cache:
|
||||
4 6 opcode
|
||||
4 FORMAT format
|
||||
4 CARD32 rate
|
||||
4 n size
|
||||
128 ESDNAME name
|
||||
w 4 CARD32 sample-id
|
||||
n ARRAY8(n) stream of PCM sound
|
||||
w 4 CARD32 sample-id
|
||||
|
||||
sample-free:
|
||||
4 7 opcode
|
||||
4 CARD32 sample-id
|
||||
w 4 CARD32 sample-id
|
||||
|
||||
sample-play:
|
||||
4 8 opcode
|
||||
4 CARD32 sample-id
|
||||
w 4 CARD32 sample-id
|
||||
|
||||
sample-loop:
|
||||
4 9 opcode
|
||||
4 CARD32 sample-id
|
||||
w 4 CARD32 sample-id
|
||||
|
||||
sample-stop:
|
||||
4 10 opcode
|
||||
4 CARD32 sample-id
|
||||
w 4 CARD32 sample-id
|
||||
|
||||
sample-kill:
|
||||
4 11 opcode
|
||||
4 CARD32 sample-id
|
||||
w 4 CARD32 sample-id
|
||||
|
||||
standby:
|
||||
4 12 opcode
|
||||
16 ESDKEY esd-key
|
||||
4 ENDIAN unused
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
resume:
|
||||
4 13 opcode
|
||||
16 ESDKEY esd-key
|
||||
4 ENDIAN unused
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
sample-getid:
|
||||
4 14 opcode
|
||||
128 ESDNAME name
|
||||
w 4 CARD32 sample-id
|
||||
|
||||
stream-filter:
|
||||
4 15 opcode
|
||||
4 FORMAT format
|
||||
4 CARD32 rate
|
||||
128 ESDNAME name
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
server-info:
|
||||
4 16 opcode
|
||||
w 4 CARD32 version
|
||||
w 4 CARD32 rate
|
||||
w 4 FORMAT format
|
||||
|
||||
server-all-info:
|
||||
4 17 opcode
|
||||
w 4 CARD32 version
|
||||
w 4 CARD32 rate
|
||||
w 4 FORMAT format
|
||||
w ? STREAMINFO
|
||||
w ? SAMPLEINFO
|
||||
|
||||
STREAMINFO:
|
||||
except for last in series
|
||||
w 4 CARD32 id
|
||||
w 16 ESDNAME name
|
||||
w 4 CARD32 rate
|
||||
w 4 CARD32 left-vol-scale
|
||||
w 4 CARD32 right-vol-scale
|
||||
w 4 FORMAT format
|
||||
last in series
|
||||
w 4 CARD32 0
|
||||
w 32 ARRAY8(32) unused
|
||||
|
||||
|
||||
SAMPLEINFO:
|
||||
except for last in series
|
||||
w 4 CARD32 id
|
||||
w 16 ESDNAME name
|
||||
w 4 CARD32 rate
|
||||
w 4 CARD32 left-vol-scale
|
||||
w 4 CARD32 right-vol-scale
|
||||
w 4 FORMAT format
|
||||
w 4 CARD32 sample-length
|
||||
last in series
|
||||
w 4 CARD32 0
|
||||
w 36 ARRAY8(36) unused
|
||||
|
||||
|
||||
//subscribe: undefined
|
||||
// 4 18 opcode
|
||||
|
||||
//unsubjcribe: undefined
|
||||
// 4 19 opcode
|
||||
|
||||
stream-pan:
|
||||
4 20 opcode
|
||||
4 CARD32 stream-id
|
||||
4 CARD32 left-scale
|
||||
4 CARD32 right-scale
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
sample-pan:
|
||||
4 21 opcode
|
||||
4 CARD32 sample-id
|
||||
4 CARD32 left-scale
|
||||
4 CARD32 right-scale
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
|
||||
standby-mode:
|
||||
4 22 opcode
|
||||
4 0 version
|
||||
w 4 MODE mode
|
||||
w 4 BOOLEAN 0 or 1
|
||||
|
||||
latency:
|
||||
4 23 opcode
|
||||
w 4 CARD32 latency
|
|
@ -0,0 +1,98 @@
|
|||
use std::net::{TcpListener, TcpStream};
|
||||
use binrw::NullString;
|
||||
use binrw::binread;
|
||||
use binrw::prelude::*;
|
||||
use modular_bitfield::prelude::*;
|
||||
|
||||
#[derive(BitfieldSpecifier)]
|
||||
#[bits = 4]
|
||||
enum FormatSampleDepth {
|
||||
EightBit = 0,
|
||||
SixteenBit = 1,
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier)]
|
||||
#[bits = 4]
|
||||
enum FormatDataType {
|
||||
Stream = 0,
|
||||
Sample = 1,
|
||||
Adpcm = 2,
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier)]
|
||||
#[bits = 4]
|
||||
enum FormatCommand {
|
||||
MonitorStreamOrStopSample = 0,
|
||||
Play = 1,
|
||||
RecordStreamOrLoopSample = 2,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
struct Endian([u8; 4]);
|
||||
|
||||
#[binread]
|
||||
struct EsdKey([u8; 16]);
|
||||
|
||||
#[binread]
|
||||
struct EsdName(#[br(pad_size_to(128))] NullString);
|
||||
|
||||
#[binread]
|
||||
enum Packet {
|
||||
Init { key: EsdKey, endian: Endian, }, // response: bool
|
||||
Lock { key: EsdKey, endian: Endian, }, // response: bool
|
||||
Unlock { key: EsdKey, endian: Endian, }, // response: bool
|
||||
StreamPlay { format: Format, rate: u32, name: EsdName, },
|
||||
_StreamMon,
|
||||
StreamMon { format: Format, rate: u32, name: EsdName, }, // response: stream
|
||||
SampleCache {
|
||||
format: Format,
|
||||
rate: u32,
|
||||
size: u32,
|
||||
sample_id: u32,
|
||||
#[br(count = size)]
|
||||
pcm: Vec<u8>,
|
||||
}, // response: u32
|
||||
SampleFree { sample_id: u32, }, // response: u32
|
||||
SamplePlay { sample_id: u32, }, // response: u32
|
||||
SampleLoop { sample_id: u32, }, // response: u32
|
||||
SampleStop { sample_id: u32, }, // response: u32
|
||||
SampleKill { sample_id: u32, }, // response: u32
|
||||
Standby { key: EsdKey, endian: Endian, }, // response: bool
|
||||
Resume { key: EsdKey, endian: Endian }, // response: bool
|
||||
SampleGetId { name: EsdName, }, // response: u32
|
||||
StreamFilter { format: Format, rate: u32, name: EsdName, }, // response: bool
|
||||
ServerInfo, // response: ServerInfo
|
||||
ServerAllInfo, // response: ServerInfo + [StreamInfo] + [SampleInfo]
|
||||
_Subscribe,
|
||||
_Unsubscribe,
|
||||
StreamPan { stream_id: u32, left_scale: u32, right_scale: u32, }, // response: bool
|
||||
SamplePan { sample_id: u32, left_scale: u32, right_scale: u32, }, // response: bool
|
||||
StandbyMode { version: u32, }, // response: { mode: u32, x: bool }
|
||||
Latency, // response: u32
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[binread]
|
||||
#[br(map = Self::from_bytes)]
|
||||
struct Format {
|
||||
sample_depth: FormatSampleDepth,
|
||||
channels: B4,
|
||||
data_type: FormatDataType,
|
||||
command: FormatCommand,
|
||||
#[skip]
|
||||
unused: B16,
|
||||
}
|
||||
|
||||
fn handle_client(stream: TcpStream) {
|
||||
// ...
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let listener = TcpListener::bind("127.0.0.1:80")?;
|
||||
|
||||
// accept connections and process them serially
|
||||
for stream in listener.incoming() {
|
||||
handle_client(stream?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue