Skip to content

Commit 99a0f5d

Browse files
authored
Merge pull request #6 from tino1b2be/dtmf-io
dtmf-io: file-I/O layer with pull-based AudioSource SPI
2 parents 27c1baf + e40db90 commit 99a0f5d

70 files changed

Lines changed: 22996 additions & 2 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTRIBUTING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,13 @@ Once JDK 17 is installed and verified, from the repository root:
8383
```
8484

8585
This compiles every module and runs every module's tests. No other local setup is required — dependencies are fetched via the Gradle wrapper from Maven Central.
86+
87+
## Shared test fixtures (MP3)
88+
89+
The `dtmf-io-mp3` module's unit tests reuse the MP3 fixtures committed in `dtmf-core/src/integrationTest/resources/samples/` rather than duplicating the binary blobs under `dtmf-io-mp3`. The mechanism is a `processTestResources` alias in `dtmf-io-mp3/build.gradle.kts` (wired in Task 1.5 of the `dtmf-io` spec, Requirement 15.4) that copies the following three files onto the `dtmf-io-mp3` test classpath under `shared-samples/`:
90+
91+
- `shared-samples/12345678.mp3` — a generated "12345678" DTMF sequence
92+
- `shared-samples/jazz.mp3` — non-DTMF audio, used as a negative anchor
93+
- `shared-samples/stereo.mp3` — stereo MP3 to exercise the 2-channel path
94+
95+
Unit tests load them with, e.g., `getClass().getResourceAsStream("/shared-samples/12345678.mp3")`. Renaming or removing any of the three files in `dtmf-core` will fail the `:dtmf-io-mp3:processTestResources` task with a missing-input error — that is the intended behaviour; the task's inputs are named explicitly so a rename surfaces at the build level rather than silently dropping coverage.

dtmf-bom/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ dependencies {
2929
constraints {
3030
api("com.tino1b2be:goertzel:2.0.0")
3131
api("com.tino1b2be:dtmf-core:2.0.0")
32+
api("com.tino1b2be:dtmf-io:2.0.0")
33+
api("com.tino1b2be:dtmf-io-wav:2.0.0")
34+
api("com.tino1b2be:dtmf-io-mp3:2.0.0")
3235
}
3336
}
3437

dtmf-io-mp3/build.gradle.kts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// `dtmf-io-mp3` — the MP3 `AudioSourceProvider` implementation for
2+
// `dtmf-io` (Requirement 1.4, Task 1.5). Its only project runtime
3+
// dependency is `:dtmf-io`, declared here as `api` so that consumers
4+
// pulling in `dtmf-io-mp3` transitively see `AudioSource`,
5+
// `AudioSourceProvider`, `AudioSources`, `DtmfFileDecoder`, and the
6+
// `dtmf-core` surface that `dtmf-io` re-exports.
7+
//
8+
// Exactly two external runtime dependencies live here by design
9+
// (Requirement 1.4): `javazoom:jlayer:1.0.1` provides the MPEG Layer III
10+
// decoder and `com.googlecode.soundlibs:mp3spi:1.9.5.4` bridges JLayer
11+
// into the `javax.sound.sampled` SPI, which the MP3 provider consumes via
12+
// `AudioSystem.getAudioInputStream(...)`. Both are pinned through the
13+
// version catalog (`gradle/libs.versions.toml`) so `libs.jlayer` and
14+
// `libs.mp3spi` are the single source of truth for their coordinates.
15+
// Adding a third external runtime dependency to this module is a
16+
// requirement regression.
17+
//
18+
// The provider is registered with `dtmf-io`'s SPI via
19+
// `META-INF/services/com.tino1b2be.dtmf.io.AudioSourceProvider` and is
20+
// discovered at runtime by `java.util.ServiceLoader`.
21+
//
22+
// JUnit 5 and jqwik test wiring, the Java 17 toolchain (Requirement 1.5),
23+
// `-Xlint:all -Werror`, UTF-8 encoding, sources+javadoc jars, and the bare
24+
// `maven-publish` publication all come from
25+
// `dtmf.published-library-conventions` (layered on top of
26+
// `dtmf.java-library-conventions`). Maven coordinates
27+
// (`com.tino1b2be:dtmf-io-mp3:2.0.0`) are inherited from the root
28+
// `build.gradle.kts` via `allprojects`.
29+
30+
plugins {
31+
id("dtmf.published-library-conventions")
32+
}
33+
34+
dependencies {
35+
api(project(":dtmf-io"))
36+
37+
// Exactly two external runtime dependencies (Requirement 1.4).
38+
implementation(libs.jlayer)
39+
implementation(libs.mp3spi)
40+
41+
// `DtmfGenerator` access for unit-test-time round-trip fixtures
42+
// (Requirement 1.6): tests generate a known DTMF tone via
43+
// `dtmf-core`, encode it as MP3 bytes with a test fixture, decode
44+
// through `Mp3AudioSource`, and assert the detected keys match the
45+
// generator's input within the detection-rate tolerance.
46+
testImplementation(project(":dtmf-core"))
47+
}
48+
49+
// -------------------------------------------------------------------------
50+
// Shared MP3 sample aliasing (Task 1.5, Requirement 15.4)
51+
// -------------------------------------------------------------------------
52+
//
53+
// The three MP3 fixtures live in `dtmf-core/src/integrationTest/resources/
54+
// samples/` so `dtmf-core`'s own integration tests can exercise them. To
55+
// avoid duplicating the binary blobs (and the associated review-and-merge
56+
// friction) they are aliased into this module's test classpath under the
57+
// sub-directory `shared-samples/` via `processTestResources`.
58+
//
59+
// Unit tests in `dtmf-io-mp3/src/test/java` therefore load the fixtures
60+
// with e.g. `getResource("/shared-samples/12345678.mp3")`, and no binary
61+
// file ships under this module's own source tree. The three filenames
62+
// (`12345678.mp3`, `jazz.mp3`, `stereo.mp3`) are named explicitly so the
63+
// task's inputs are tracked precisely — a rename or removal of any
64+
// listed file in `dtmf-core` surfaces as a missing-input build failure
65+
// rather than silently dropping coverage.
66+
67+
tasks.named<Copy>("processTestResources") {
68+
val sharedSamplesDir = rootProject.file(
69+
"dtmf-core/src/integrationTest/resources/samples"
70+
)
71+
from(sharedSamplesDir) {
72+
include("12345678.mp3", "jazz.mp3", "stereo.mp3")
73+
into("shared-samples")
74+
}
75+
}

0 commit comments

Comments
 (0)