Skip to content

Commit 203c747

Browse files
committed
Update the tap's formula url to v0.1.1 and recompute the source-tarball sha256 before publishing.)
1 parent 1b4afa0 commit 203c747

8 files changed

Lines changed: 518 additions & 17 deletions

File tree

Formula/liveaudioserver.rb

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,16 @@ class Liveaudioserver < Formula
2323
head "https://github.com/dsward2/LiveAudioServer.git", branch: "main"
2424

2525
depends_on macos: :ventura
26-
depends_on "lame"
2726

2827
def install
2928
# --disable-sandbox is required for SwiftPM under Homebrew, which
3029
# otherwise can't fetch its own toolchain cache.
3130
#
32-
# Pass -I/-L explicitly because Homebrew's lame keg does not ship a
33-
# mp3lame.pc; the CLame system-library target's pkgConfig: "mp3lame"
34-
# lookup therefore returns no flags and the lame_shim.h #include
35-
# cascade fails to find <lame/lame.h>.
31+
# libmp3lame is vendored as Frameworks/Mp3Lame.xcframework and consumed
32+
# by SwiftPM as a binary target — no external `lame` dependency needed.
3633
system "swift", "build",
3734
"--configuration", "release",
38-
"--disable-sandbox",
39-
"-Xcc", "-I#{Formula["lame"].opt_include}",
40-
"-Xlinker", "-L#{Formula["lame"].opt_lib}"
35+
"--disable-sandbox"
4136
bin.install ".build/release/LiveAudioServer" => "liveaudioserver"
4237
end
4338

Package.swift

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,16 @@ let package = Package(
1010
.package(url: "https://github.com/apple/swift-testing.git", from: "0.10.0")
1111
],
1212
targets: [
13-
.systemLibrary(
13+
// Vendored libmp3lame as a universal (arm64 + x86_64) static
14+
// XCFramework. Regenerate by running scripts/build-mp3lame-xcframework.sh.
15+
.binaryTarget(
1416
name: "CLame",
15-
pkgConfig: "mp3lame",
16-
providers: [
17-
.brew(["lame"])
18-
]
17+
path: "Frameworks/Mp3Lame.xcframework"
1918
),
2019
.executableTarget(
2120
name: "LiveAudioServer",
2221
dependencies: ["CLame"],
23-
path: "Sources/LiveAudioServer",
24-
linkerSettings: [
25-
.linkedLibrary("mp3lame")
26-
]
22+
path: "Sources/LiveAudioServer"
2723
),
2824
.testTarget(
2925
name: "LiveAudioServerTests",

THIRD-PARTY-LICENSES.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Third-party software included in LiveAudioServer
2+
3+
LiveAudioServer itself is licensed under Apache-2.0 (see [LICENSE](LICENSE)).
4+
This file documents the third-party libraries that are statically linked into
5+
the distributed `liveaudioserver` binary and the license terms that apply to
6+
them.
7+
8+
## libmp3lame (LAME) — LGPL-2.1-or-later
9+
10+
LiveAudioServer bundles **LAME 3.100** for MP3 encoding. LAME is distributed
11+
under the GNU Lesser General Public License, version 2.1 or later
12+
(LGPL-2.1+).
13+
14+
- Upstream project: https://lame.sourceforge.io
15+
- License text: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
16+
- Source distribution used for the bundled build:
17+
https://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz
18+
(SHA256 `ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e`)
19+
20+
### LGPL compliance — re-linking against a modified libmp3lame
21+
22+
Section 6 of LGPL-2.1 permits static linking of LGPL libraries into otherwise
23+
non-LGPL programs **provided** the recipient can replace the bundled
24+
libmp3lame with a modified version and re-link.
25+
26+
To satisfy this requirement, LiveAudioServer ships the exact, fully
27+
reproducible recipe used to build the bundled `libmp3lame.a`:
28+
29+
- [`scripts/build-mp3lame-xcframework.sh`](scripts/build-mp3lame-xcframework.sh)
30+
downloads LAME 3.100 from the URL above, builds it for arm64 and x86_64
31+
with the documented `CFLAGS`/`LDFLAGS`, and packages the result as the
32+
`Frameworks/Mp3Lame.xcframework` that SwiftPM consumes.
33+
34+
To produce a `liveaudioserver` binary linked against a *modified* LAME, edit
35+
`LAME_VERSION` / `LAME_URL` / `LAME_SHA256` in that script (or substitute the
36+
unpacked source tree at `.build-mp3lame/lame-${LAME_VERSION}`), then re-run:
37+
38+
```bash
39+
./scripts/build-mp3lame-xcframework.sh
40+
swift build -c release
41+
```
42+
43+
The resulting `.build/release/LiveAudioServer` is statically linked against
44+
your modified libmp3lame.
45+
46+
The Apache-2.0-licensed parts of LiveAudioServer (everything under
47+
`Sources/`) remain Apache-2.0; only the bundled LAME object code is
48+
LGPL-licensed.
49+
50+
## AudioToolbox, Network.framework, Foundation — Apple
51+
52+
LiveAudioServer dynamically links against frameworks that ship with macOS
53+
13+. These are governed by the macOS software license and are not
54+
redistributed by this project.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// LiveAudioServer — https://github.com/dsward2/LiveAudioServer
2+
//
3+
// Copyright (c)2026 by Douglas Ward - Conway, Arkansas US
4+
// Licensed under the Apache License, Version 2.0.
5+
6+
import Testing
7+
@testable import LiveAudioServer
8+
9+
@Suite("TPDF dither generator")
10+
struct TPDFDitherTests {
11+
12+
@Test("Output is bounded to ±1 LSB")
13+
func boundedRange() {
14+
var d = TPDFDither(seed: 42)
15+
for _ in 0..<100_000 {
16+
let n = d.nextNoiseSample()
17+
#expect(n >= -1 && n <= 1)
18+
}
19+
}
20+
21+
@Test("Same seed yields identical sequence")
22+
func deterministic() {
23+
var a = TPDFDither(seed: 0xCAFEBABE)
24+
var b = TPDFDither(seed: 0xCAFEBABE)
25+
for _ in 0..<1024 {
26+
#expect(a.nextNoiseSample() == b.nextNoiseSample())
27+
}
28+
}
29+
30+
@Test("Distribution is roughly triangular (mean ~0, includes all three buckets)")
31+
func roughlyTriangular() {
32+
var d = TPDFDither(seed: 0xDEADBEEF)
33+
var counts: [Int16: Int] = [-1: 0, 0: 0, 1: 0]
34+
var sum = 0
35+
let n = 200_000
36+
for _ in 0..<n {
37+
let s = d.nextNoiseSample()
38+
counts[s, default: 0] += 1
39+
sum += Int(s)
40+
}
41+
// All three buckets should be populated.
42+
#expect((counts[-1] ?? 0) > 0)
43+
#expect((counts[0] ?? 0) > 0)
44+
#expect((counts[1] ?? 0) > 0)
45+
// Triangular PDF over [-1,+1] integers (after rounding the sum of two
46+
// half-LSB uniforms) puts ~50% weight on 0 and ~25% on each of ±1.
47+
let mean = Double(sum) / Double(n)
48+
#expect(abs(mean) < 0.05) // mean ≈ 0
49+
}
50+
51+
@Test("Seed 0 falls back to a non-zero default")
52+
func zeroSeedReplaced() {
53+
var d = TPDFDither(seed: 0)
54+
// Should still produce a varied stream, not all the same value.
55+
var first = d.nextNoiseSample()
56+
var sawDifferent = false
57+
for _ in 0..<1000 {
58+
let n = d.nextNoiseSample()
59+
if n != first { sawDifferent = true; break }
60+
first = n
61+
}
62+
#expect(sawDifferent)
63+
}
64+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env bash
2+
#
3+
# LiveAudioServer — https://github.com/dsward2/LiveAudioServer
4+
#
5+
# Build a universal (arm64 + x86_64) static Mp3Lame.xcframework from the LAME
6+
# 3.100 source release and stage it at Frameworks/Mp3Lame.xcframework.
7+
#
8+
# This produces the artifact that Package.swift consumes via a binaryTarget,
9+
# so `swift build` works on a fresh clone without any external package
10+
# manager (no Homebrew, no MacPorts).
11+
#
12+
# Re-run this script only when bumping the LAME version. The generated
13+
# Frameworks/Mp3Lame.xcframework is committed to the repo.
14+
#
15+
# Requirements (already present on any macOS 13+ machine with Xcode):
16+
# - curl, tar, make, lipo
17+
# - xcodebuild (for -create-xcframework)
18+
# - Xcode command-line tools
19+
20+
set -euo pipefail
21+
22+
LAME_VERSION="3.100"
23+
LAME_SHA256="ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e"
24+
LAME_URL="https://downloads.sourceforge.net/project/lame/lame/${LAME_VERSION}/lame-${LAME_VERSION}.tar.gz"
25+
26+
# macOS deployment target — match Package.swift's .macOS(.v13).
27+
MACOSX_DEPLOYMENT_TARGET="13.0"
28+
29+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
30+
BUILD_ROOT="${REPO_ROOT}/.build-mp3lame"
31+
OUT_XCF="${REPO_ROOT}/Frameworks/Mp3Lame.xcframework"
32+
33+
echo "[1/7] Preparing build directories"
34+
rm -rf "${BUILD_ROOT}" "${OUT_XCF}"
35+
mkdir -p "${BUILD_ROOT}" "${REPO_ROOT}/Frameworks"
36+
cd "${BUILD_ROOT}"
37+
38+
echo "[2/7] Downloading lame-${LAME_VERSION}.tar.gz"
39+
curl -fL -o "lame-${LAME_VERSION}.tar.gz" "${LAME_URL}"
40+
41+
actual_sha="$(shasum -a 256 "lame-${LAME_VERSION}.tar.gz" | awk '{print $1}')"
42+
if [[ "${actual_sha}" != "${LAME_SHA256}" ]]; then
43+
echo "ERROR: SHA256 mismatch for LAME tarball" >&2
44+
echo " expected: ${LAME_SHA256}" >&2
45+
echo " actual: ${actual_sha}" >&2
46+
exit 1
47+
fi
48+
49+
tar xzf "lame-${LAME_VERSION}.tar.gz"
50+
SRC="${BUILD_ROOT}/lame-${LAME_VERSION}"
51+
52+
# LAME 3.100 has a long-standing issue where xmmintrin.h causes a build failure
53+
# when compiled against modern Apple SDKs. The fix is to remove the offending
54+
# decl in include/libmp3lame.sym (the .a doesn't need it).
55+
# Reference: https://sourceforge.net/p/lame/mailman/lame-dev/thread/...
56+
sed -i.bak '/lame_init_old/d' "${SRC}/include/libmp3lame.sym"
57+
58+
build_one_arch() {
59+
local arch="$1"
60+
local host="$2"
61+
local stage="${BUILD_ROOT}/stage-${arch}"
62+
echo "[*] Configuring + building libmp3lame for ${arch}"
63+
rm -rf "${stage}"
64+
mkdir -p "${stage}"
65+
(
66+
cd "${SRC}"
67+
make distclean >/dev/null 2>&1 || true
68+
CC="$(xcrun -find clang)"
69+
CFLAGS="-arch ${arch} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET} -O2 -fPIC"
70+
LDFLAGS="-arch ${arch} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}"
71+
./configure \
72+
--host="${host}" \
73+
--prefix="${stage}" \
74+
--disable-shared \
75+
--enable-static \
76+
--disable-frontend \
77+
--disable-decoder \
78+
--disable-analyzer-hooks \
79+
--disable-gtktest \
80+
CC="${CC}" \
81+
CFLAGS="${CFLAGS}" \
82+
LDFLAGS="${LDFLAGS}" \
83+
>/dev/null
84+
make -j"$(sysctl -n hw.ncpu)" >/dev/null
85+
make install >/dev/null
86+
)
87+
}
88+
89+
echo "[3/7] Building arm64 slice"
90+
build_one_arch arm64 aarch64-apple-darwin
91+
92+
echo "[4/7] Building x86_64 slice"
93+
build_one_arch x86_64 x86_64-apple-darwin
94+
95+
echo "[5/7] lipo-ing universal libmp3lame.a"
96+
UNI_DIR="${BUILD_ROOT}/universal"
97+
mkdir -p "${UNI_DIR}"
98+
lipo -create \
99+
"${BUILD_ROOT}/stage-arm64/lib/libmp3lame.a" \
100+
"${BUILD_ROOT}/stage-x86_64/lib/libmp3lame.a" \
101+
-output "${UNI_DIR}/libmp3lame.a"
102+
lipo -info "${UNI_DIR}/libmp3lame.a"
103+
104+
echo "[6/7] Assembling Headers and module map"
105+
HDR="${BUILD_ROOT}/Headers"
106+
rm -rf "${HDR}"
107+
mkdir -p "${HDR}"
108+
cp "${BUILD_ROOT}/stage-arm64/include/lame/lame.h" "${HDR}/lame.h"
109+
cat > "${HDR}/module.modulemap" <<'MODMAP'
110+
module CLame {
111+
header "lame.h"
112+
export *
113+
}
114+
MODMAP
115+
116+
echo "[7/7] Creating Mp3Lame.xcframework"
117+
xcodebuild -create-xcframework \
118+
-library "${UNI_DIR}/libmp3lame.a" \
119+
-headers "${HDR}" \
120+
-output "${OUT_XCF}" \
121+
>/dev/null
122+
123+
echo
124+
echo "Done."
125+
echo " Output: ${OUT_XCF}"
126+
du -sh "${OUT_XCF}"
127+
echo
128+
echo "You can now build the project with: swift build -c release"

0 commit comments

Comments
 (0)