Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ docs
.github
CODE_OF_CONDUCT.md
SECURITY.md
CONTRIBUTING.md
CONTRIBUTING.md
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ $ npm install @performanc/voice
$ npm install github:PerformanC/voice
```

### Native build requirements

Installation builds the native audio sender and requires Python, a C compiler,
and the libsodium development files.

On Windows, use the official Visual Studio libsodium package and set
`SODIUM_ROOT` to its extracted directory:

```powershell
$env:SODIUM_ROOT = 'C:\path\to\libsodium'
npm install
```

The build also detects a static libsodium installation under `VCPKG_ROOT`.
Custom layouts can set `SODIUM_INCLUDE_DIR` to the directory containing
`sodium.h` and `SODIUM_LIB` to the full path of the static `.lib` file.

Linux and macOS builds use the system libsodium development package, such as
`libsodium-dev` on Debian/Ubuntu or `brew install libsodium` on macOS.

## Usage

```javascript
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ class Connection extends EventEmitter {
nonce.fill(0)
data.copy(nonce, 0, data.length - 4, data.length)

const headerSize = 12 + cc * 4
let headerSize = 12 + cc * 4
let extensionLengthInWords = 0
if (data.length < headerSize) return;

Expand Down
44 changes: 37 additions & 7 deletions native/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@
"target_name": "voice_send_native",
"sources": [
"src/binding.c",
"src/cthreads.c",
"src/voice_send.c"
],
"include_dirs": [
"."
],
"libraries": [
"-lsodium",
"-lpthread"
],
"conditions": [
["OS=='linux'", {
"cflags": [
Expand All @@ -21,9 +18,16 @@
"-Wextra",
"-Wpedantic"
],
"ldflags": []
"libraries": [
"-lsodium",
"-lpthread"
]
}],
["OS=='mac'", {
"libraries": [
"-lsodium",
"-lpthread"
],
"xcode_settings": {
"OTHER_CFLAGS": [
"-std=c99",
Expand All @@ -34,11 +38,37 @@
}
}],
["OS=='win'", {
"defines": [
"SODIUM_STATIC"
],
"msvs_settings": {
"VCCLCompilerTool": {
"AdditionalOptions!": [
"-flto=full",
"-flto=thin"
]
},
"VCLibrarianTool": {
"AdditionalOptions!": [
"-flto=full",
"-flto=thin"
]
},
"VCLinkerTool": {
"AdditionalOptions!": [
"-flto=full",
"-flto=thin",
"/opt:lldltojobs=<(lto_jobs)"
]
}
},
"libraries": [
"-lsodium.lib"
"<!(node find-libsodium.js library <(target_arch))",
"ws2_32.lib"
],
"include_dirs": [
"src"
"src",
"<!(node find-libsodium.js include <(target_arch))"
]
}]
]
Expand Down
105 changes: 105 additions & 0 deletions native/find-libsodium.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import fs from 'node:fs'
import path from 'node:path'

const [kind, targetArch] = process.argv.slice(2)
const archNames = {
arm64: 'ARM64',
ia32: 'Win32',
x64: 'x64'
}
const vcpkgArchNames = {
arm64: 'arm64',
ia32: 'x86',
x64: 'x64'
}

if (!['include', 'library'].includes(kind) || !archNames[targetArch]) {
fail(`Unsupported libsodium lookup: ${kind ?? ''} ${targetArch ?? ''}`)
}

const roots = []
const sodiumRoot = process.env.SODIUM_ROOT ?? process.env.npm_config_sodium_root

if (sodiumRoot) {
roots.push(sodiumRoot)
roots.push(vcpkgInstalledRoot(sodiumRoot))
}

if (process.env.VCPKG_ROOT) {
roots.push(vcpkgInstalledRoot(process.env.VCPKG_ROOT))
}

const candidates =
kind === 'include'
? includeCandidates(roots)
: libraryCandidates(roots, targetArch)
const result = candidates.find(
(candidate) => candidate && fs.existsSync(candidate)
)

if (!result) {
fail(
`Could not find the libsodium ${kind}. Set SODIUM_ROOT to a libsodium ` +
'SDK directory, or set SODIUM_INCLUDE_DIR and SODIUM_LIB explicitly. ' +
'A VCPKG_ROOT installation is also supported.'
)
}

process.stdout.write(
path.resolve(kind === 'include' ? path.dirname(result) : result)
)

function includeCandidates(searchRoots) {
return [
process.env.SODIUM_INCLUDE_DIR,
...searchRoots.map((root) => root && path.join(root, 'include'))
]
.filter(Boolean)
.map((includeDir) => path.join(includeDir, 'sodium.h'))
}

function libraryCandidates(searchRoots, arch) {
const candidates = [process.env.SODIUM_LIB]

for (const root of searchRoots.filter(Boolean)) {
candidates.push(
path.join(root, 'lib', 'libsodium.lib'),
path.join(root, 'lib', 'sodium.lib')
)

const releaseRoot = path.join(root, archNames[arch], 'Release')
if (!fs.existsSync(releaseRoot)) continue

const toolsets = fs
.readdirSync(releaseRoot, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && /^v\d+$/.test(entry.name))
.map((entry) => entry.name)
.sort((left, right) =>
right.localeCompare(left, undefined, {
numeric: true
})
)

for (const toolset of toolsets) {
candidates.push(
path.join(releaseRoot, toolset, 'static', 'libsodium.lib')
)
}
}

return candidates.filter(Boolean)
}

function vcpkgInstalledRoot(root) {
const triplet =
process.env.VCPKG_TARGET_TRIPLET ??
process.env.VCPKG_DEFAULT_TRIPLET ??
`${vcpkgArchNames[targetArch]}-windows-static`

return path.join(root, 'installed', triplet)
}

function fail(message) {
process.stderr.write(`\n@performanc/voice: ${message}\n`)
process.exit(1)
}
2 changes: 1 addition & 1 deletion native/src/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ static void *loop_thread_func(void *arg) {
/* INFO: Send via UDP */
if (pkt_len > 0) {
int sent = os_sendto(nc->udp_fd, packet, (size_t)pkt_len, 0, (const struct sockaddr *)&nc->udp_addr, sizeof(nc->udp_addr));
if (sent == (ssize_t)pkt_len) {
if (sent == pkt_len) {
nc->ctx.stats.packets_sent++;

/* INFO: Update the stats counter and sync to JS every 50 packets */
Expand Down
Loading