audiowaveform 0.1.0-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # audiowaveform
2
+
3
+ [![CI](https://github.com/Antti/audiowaveform/actions/workflows/rust.yml/badge.svg)](https://github.com/Antti/audiowaveform/actions/workflows/rust.yml)
4
+
5
+ `audiowaveform` is a Rust library and CLI for generating waveform data from audio,
6
+ serializing waveform files, rendering PNG waveform images, and transcoding audio
7
+ to PCM16 WAV.
8
+
9
+ This repository is the canonical home of the Rust rewrite:
10
+ [github.com/Antti/audiowaveform](https://github.com/Antti/audiowaveform).
11
+
12
+ It is a Rust rewrite of the original BBC `audiowaveform` project:
13
+ [github.com/bbc/audiowaveform](https://github.com/bbc/audiowaveform).
14
+ The original project was created by Chris Needham and contributors at BBC
15
+ Research & Development.
16
+
17
+ The repository's main workspace remains Rust-only:
18
+
19
+ - `crates/audiowaveform`: reusable library crate
20
+ - `crates/audiowaveform-cli`: thin `audiowaveform` command-line wrapper
21
+
22
+ Ruby applications can use the native extension in `bindings/ruby` to generate
23
+ waveforms in process through the same library crate.
24
+
25
+ ![Example Waveform](doc/example.png "Example Waveform")
26
+
27
+ ## Features
28
+
29
+ - Decode AAC-LC, ALAC, MP1/MP2/MP3, WAV, FLAC, Ogg, AIFF, CAF, and audio in MP4/Matroska/WebM containers
30
+ - Generate `.dat`, `.json`, and `.txt` waveform files
31
+ - Render PNG waveform images in pure Rust
32
+ - Transcode decoded audio or raw PCM input to PCM16 WAV
33
+ - Use path-based, stream-based, or in-memory APIs from the library crate
34
+
35
+ Opus and HE-AAC are unsupported by the current decoder. AAC-LC supports mono and
36
+ stereo. Enabling a container such as WebM does not add unsupported codecs.
37
+ AAC/MP4 decoding does not apply gapless trimming, so decoded audio and waveform
38
+ duration can include encoder delay and padding.
39
+
40
+ ## Quick Start
41
+
42
+ Build the workspace:
43
+
44
+ ```sh
45
+ cargo build --workspace
46
+ ```
47
+
48
+ Run the CLI:
49
+
50
+ ```sh
51
+ cargo run -p audiowaveform-cli -- -i fixtures/test_file_stereo.wav -o output.dat
52
+ ```
53
+
54
+ Install the CLI locally:
55
+
56
+ ```sh
57
+ cargo install --path crates/audiowaveform-cli
58
+ ```
59
+
60
+ Run the test suite:
61
+
62
+ ```sh
63
+ cargo test --workspace
64
+ ```
65
+
66
+ ## Library Usage
67
+
68
+ The Rust library has **no default features**. PCM/raw waveform generation,
69
+ waveform serialization, and resampling are always available. Enable input formats
70
+ and output capabilities explicitly:
71
+
72
+ ```toml
73
+ [dependencies]
74
+ audiowaveform = { version = "1.10.3", features = ["format-mp3", "format-m4a"] }
75
+ ```
76
+
77
+ | Cargo feature | Capability |
78
+ | --- | --- |
79
+ | `format-aac` | AAC-LC in ADTS (`.aac`, `.adts`) |
80
+ | `format-aiff` | PCM in AIFF/AIFF-C (`.aiff`, `.aif`, `.aifc`) |
81
+ | `format-caf` | PCM and ALAC in CAF |
82
+ | `format-flac` | FLAC |
83
+ | `format-m4a` / `format-mp4` | AAC-LC, ALAC, MP3, and PCM in MP4/M4A/MOV containers |
84
+ | `format-mkv` / `format-webm` | Supported audio codecs in Matroska/WebM (`.mkv`, `.mka`, `.webm`); no Opus |
85
+ | `format-mp1`, `format-mp2`, `format-mp3` | MPEG audio layers I, II, and III respectively |
86
+ | `format-ogg` | Vorbis and FLAC in Ogg (`.ogg`, `.oga`) |
87
+ | `format-wav` | PCM and ADPCM in WAV/W64 |
88
+ | `all-formats` | All input format bundles above |
89
+ | `render` | PNG rendering |
90
+ | `wav-output` | PCM16 WAV writing |
91
+
92
+ `format-mp4` aliases `format-m4a`; `format-webm` aliases `format-mkv`.
93
+ Format features enable the shared `decode` plumbing automatically. `decode`
94
+ alone does not enable any codecs or containers. `all-formats` does not enable
95
+ PNG rendering or WAV writing. Cargo features are additive: another dependency
96
+ can enable additional features in a shared build.
97
+
98
+ Generate waveform data from an audio file:
99
+
100
+ ```rust,no_run
101
+ use audiowaveform::{GenerateOptions, WaveformFormat, generate_waveform_from_path};
102
+
103
+ fn main() -> Result<(), audiowaveform::Error> {
104
+ let waveform = generate_waveform_from_path("input.mp3", &GenerateOptions::default())?;
105
+ waveform.save_to_path("output.dat", Some(WaveformFormat::Dat))?;
106
+ Ok(())
107
+ }
108
+ ```
109
+
110
+ Render a PNG from a stored waveform (requires `render`):
111
+
112
+ ```rust,no_run
113
+ use std::fs::File;
114
+
115
+ use audiowaveform::{RenderOptions, Waveform, write_waveform_png};
116
+
117
+ fn main() -> Result<(), audiowaveform::Error> {
118
+ let waveform = Waveform::load_from_path("input.dat", None)?;
119
+ write_waveform_png(&waveform, &RenderOptions::default(), File::create("output.png")?)?;
120
+ Ok(())
121
+ }
122
+ ```
123
+
124
+ Generate waveform data from in-memory PCM:
125
+
126
+ ```rust
127
+ use audiowaveform::{GenerateOptions, PcmAudio, generate_waveform_from_pcm};
128
+
129
+ fn main() -> Result<(), audiowaveform::Error> {
130
+ let pcm = PcmAudio::new(48_000, 1, vec![0_i16; 48_000])?;
131
+ let waveform = generate_waveform_from_pcm(&pcm, &GenerateOptions::default())?;
132
+ assert!(!waveform.is_empty());
133
+ Ok(())
134
+ }
135
+ ```
136
+
137
+ Additional examples live in `crates/audiowaveform/examples`.
138
+
139
+ ## Ruby Usage
140
+
141
+ Install the `audiowaveform` gem from RubyGems and generate waveform
142
+ data without invoking the command-line program:
143
+
144
+ ```ruby
145
+ gem "audiowaveform", "~> 0.1"
146
+ ```
147
+
148
+ ```ruby
149
+ require "audiowaveform"
150
+
151
+ waveform = AudioWaveform.generate("input.mp3", samples_per_pixel: 256)
152
+ waveform.save("output.dat", bits: 8)
153
+ ```
154
+
155
+ See [`bindings/ruby/README.md`](bindings/ruby/README.md) for the full Ruby API
156
+ and development instructions.
157
+
158
+ ## CLI Usage
159
+
160
+ The CLI defaults to `all-formats`, `render`, and `wav-output`. To build a smaller
161
+ CLI, disable defaults and enable only the features you need:
162
+
163
+ ```sh
164
+ cargo build -p audiowaveform-cli --no-default-features --features format-mp3,format-m4a
165
+ ```
166
+
167
+ Add `render` or `wav-output` for those outputs. With no features, the CLI can
168
+ still process raw PCM and convert/resample waveform data. Requests for omitted
169
+ formats or outputs report the required Cargo feature.
170
+
171
+ Generate `.dat` waveform data:
172
+
173
+ ```sh
174
+ audiowaveform -i input.wav -o output.dat -z 128 -b 8
175
+ ```
176
+
177
+ Render a PNG from waveform data:
178
+
179
+ ```sh
180
+ audiowaveform -i input.dat -o output.png -w 1000 -h 200
181
+ ```
182
+
183
+ Generate JSON waveform data from compressed audio:
184
+
185
+ ```sh
186
+ audiowaveform -i input.flac -o output.json --pixels-per-second 50
187
+ ```
188
+
189
+ Convert raw PCM to WAV:
190
+
191
+ ```sh
192
+ audiowaveform -i input.raw -o output.wav --input-format raw --raw-samplerate 48000 --raw-channels 2 --raw-format s16le
193
+ ```
194
+
195
+ See all options with:
196
+
197
+ ```sh
198
+ audiowaveform --help
199
+ ```
200
+
201
+ ## Supported Formats
202
+
203
+ Audio input:
204
+
205
+ - `aac` and `adts` (AAC-LC)
206
+ - `mp1`, `mp2`, and `mp3`
207
+ - `mp4`, `m4a`, `m4b`, `m4r`, `m4v`, and `mov` (supported audio tracks only)
208
+ - `mkv`, `mka`, and `webm` (supported audio tracks only; no Opus)
209
+ - `aiff`, `aif`, and `aifc`
210
+ - `caf`
211
+ - `wav` and `w64`
212
+ - `flac`
213
+ - `ogg` and `oga`
214
+ - `raw`
215
+
216
+ Waveform input:
217
+
218
+ - `dat`
219
+ - `json`
220
+
221
+ Waveform output:
222
+
223
+ - `dat`
224
+ - `json`
225
+ - `txt`
226
+
227
+ Image output:
228
+
229
+ - `png`
230
+
231
+ Audio output:
232
+
233
+ - `wav`
234
+
235
+ ## Documentation
236
+
237
+ - Waveform file formats: [doc/DataFormat.md](doc/DataFormat.md)
238
+ - CLI man page: [doc/audiowaveform.1](doc/audiowaveform.1)
239
+ - Waveform format man page: [doc/audiowaveform.5](doc/audiowaveform.5)
240
+ - Project release history: [CHANGELOG.md](CHANGELOG.md)
241
+ - Ruby gem release history: [bindings/ruby/CHANGELOG.md](bindings/ruby/CHANGELOG.md)
242
+
243
+ Generate local API docs with:
244
+
245
+ ```sh
246
+ cargo doc -p audiowaveform --all-features --no-deps
247
+ ```
248
+
249
+ ## Contributing
250
+
251
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for workflow, documentation, and testing expectations.
252
+
253
+ ## License
254
+
255
+ `audiowaveform` is released under the GPL-3.0-or-later license. See [COPYING](COPYING).
@@ -0,0 +1,29 @@
1
+ # Ruby Gem Version History
2
+
3
+ ## 0.1.0 - 2026-09-20
4
+
5
+ ### Added
6
+
7
+ - Publish source and precompiled gems through RubyGems Trusted Publishing on
8
+ `ruby-vX.Y.Z` tag pushes. Manual workflow runs build and test without publishing.
9
+ - Build native gems for CRuby 3.2–4.0 on Linux glibc/musl (x86-64 and ARM64),
10
+ macOS (Intel and Apple Silicon), and Windows UCRT (x86-64).
11
+ - Load the extension matching the running Ruby version and verify installed
12
+ platform gems before publishing. Native gems have no build-time dependencies.
13
+ - Enable all supported Rust input formats in source and precompiled builds,
14
+ including AAC-LC/M4A, ALAC, AIFF, CAF, MPEG layers I/II, Matroska/WebM audio,
15
+ Ogg FLAC, and WAV ADPCM. Opus and HE-AAC remain unsupported.
16
+
17
+ - Generate waveform data directly from WAV, MP3, FLAC, and Ogg/Vorbis files.
18
+ - Read waveform metadata and points, then serialize data as DAT, JSON, or text.
19
+ - Install a source gem from GitHub releases or directly from the repository.
20
+
21
+ ### Changed
22
+
23
+ - Release Ruby's global VM lock during generation, serialization, and file writes.
24
+ - Point installation instructions and package metadata at `Antti/audiowaveform`.
25
+
26
+ ### Fixed
27
+
28
+ - Build, install, and test the native extension across supported Linux, macOS,
29
+ and Windows environments.
@@ -0,0 +1,193 @@
1
+ # audiowaveform for Ruby
2
+
3
+ The `audiowaveform` gem provides native Ruby bindings for the Rust
4
+ `audiowaveform` library. It generates waveform data in process without invoking
5
+ the command-line program. Audio decoding runs without Ruby's global VM lock, so
6
+ other Ruby threads can continue while a waveform is generated.
7
+
8
+ ## Installation
9
+
10
+ Add the gem to your bundle:
11
+
12
+ ```ruby
13
+ gem "audiowaveform", "~> 0.1"
14
+ ```
15
+
16
+ Releases include precompiled gems for CRuby 3.2, 3.3, 3.4, and 4.0 on these platforms:
17
+
18
+ | OS | Architectures | Gem platforms |
19
+ | --- | --- | --- |
20
+ | Linux (glibc) | x86-64, ARM64 | `x86_64-linux-gnu`, `aarch64-linux-gnu` |
21
+ | Linux (musl/Alpine) | x86-64, ARM64 | `x86_64-linux-musl`, `aarch64-linux-musl` |
22
+ | macOS | Intel, Apple Silicon | `x86_64-darwin`, `arm64-darwin` |
23
+ | Windows (UCRT) | x86-64 | `x64-mingw-ucrt` |
24
+
25
+ Precompiled gems do not require Rust, FFmpeg, or compilation during installation.
26
+ They contain a separate extension for each supported Ruby minor version.
27
+ Other Ruby versions and platforms fall back to the source gem, which needs Rust
28
+ and Ruby development headers. Installing directly from GitHub also builds from
29
+ source. JRuby and TruffleRuby are not supported.
30
+
31
+ ## Usage
32
+
33
+ Generate waveform data from an audio file:
34
+
35
+ ```ruby
36
+ require "audiowaveform"
37
+
38
+ waveform = AudioWaveform.generate(
39
+ "recording.mp3",
40
+ samples_per_pixel: 256,
41
+ split_channels: false
42
+ )
43
+
44
+ waveform.sample_rate # => 44_100
45
+ waveform.samples_per_pixel # => 256
46
+ waveform.channels # => 1
47
+ waveform.length # => number of waveform points per channel
48
+ waveform.data # => interleaved [min, max, ...] samples
49
+ ```
50
+
51
+ `AudioWaveform.generate` accepts these keyword arguments:
52
+
53
+ | Keyword | Default | Description |
54
+ | --- | --- | --- |
55
+ | `samples_per_pixel` | `256` | Number of source samples represented by each waveform point. Must be at least 2. |
56
+ | `pixels_per_second` | none | Time-based scale. Cannot be combined with `samples_per_pixel`. |
57
+ | `split_channels` | `false` | Preserve separate audio channels instead of mixing them down. |
58
+ | `amplitude_scale` | none | Non-negative numeric multiplier, or `:auto` to normalize automatically. |
59
+
60
+ The returned `AudioWaveform::Waveform` exposes:
61
+
62
+ | Method | Result |
63
+ | --- | --- |
64
+ | `sample_rate` | Source sample rate in hertz. |
65
+ | `samples_per_pixel` | Source samples represented by each waveform point. |
66
+ | `channels` | Number of waveform channels. |
67
+ | `storage_bits` / `bits` | Internal sample resolution, either 8 or 16. |
68
+ | `length` / `size` | Number of waveform points per channel. |
69
+ | `empty?` | Whether the waveform contains no points. |
70
+ | `duration` / `duration_seconds` | Approximate duration in seconds. |
71
+ | `data` | Interleaved minimum and maximum sample values. |
72
+ | `point(index, channel: 0)` | `[minimum, maximum]` pair for one point and channel. |
73
+
74
+ Save the generated waveform as binary DAT, JSON, or text:
75
+
76
+ ```ruby
77
+ waveform.save("recording.dat", bits: 8)
78
+ waveform.save("recording.json", bits: 16)
79
+ waveform.save("recording.waveform", format: :txt)
80
+
81
+ json = waveform.to_json(bits: 8)
82
+ binary = waveform.to_dat(bits: 16)
83
+ text = waveform.to_txt(bits: 8)
84
+ ```
85
+
86
+ `bits` must be 8 or 16. `save` infers the format from a `.dat`, `.json`, or
87
+ `.txt` extension unless `format:` is provided explicitly. Invalid generation
88
+ or serialization options raise `ArgumentError`; an out-of-range `point` raises
89
+ `IndexError`; decoding and file I/O failures raise `AudioWaveform::Error`.
90
+
91
+ Use `pixels_per_second` instead of `samples_per_pixel` when a time-based scale
92
+ is more convenient:
93
+
94
+ ```ruby
95
+ waveform = AudioWaveform.generate("recording.flac", pixels_per_second: 100)
96
+ ```
97
+
98
+ Amplitude can be scaled with a numeric multiplier or normalized automatically:
99
+
100
+ ```ruby
101
+ AudioWaveform.generate("quiet.wav", amplitude_scale: 1.5)
102
+ AudioWaveform.generate("quiet.wav", amplitude_scale: :auto)
103
+ ```
104
+
105
+ Both source and precompiled gems enable the Rust library's `all-formats` feature:
106
+ AAC-LC/ADTS, AAC-LC and ALAC in M4A/MP4, MP1/MP2/MP3, WAV/W64 (PCM and ADPCM),
107
+ FLAC, Ogg (Vorbis and FLAC), AIFF, CAF (PCM and ALAC), and supported audio tracks
108
+ in Matroska/WebM. No FFmpeg installation is required for decoding.
109
+
110
+ AAC-LC supports mono and stereo. HE-AAC and Opus remain unsupported, including
111
+ Opus inside Ogg/WebM/MP4. Raw PCM input is not currently exposed by the gem.
112
+ The filename extension identifies the container; an enabled container can still
113
+ contain an unsupported codec. Such files raise `AudioWaveform::Error`.
114
+ AAC/MP4 waveform duration can include encoder delay and padding; gapless
115
+ trimming is not currently supported.
116
+
117
+ ## Development
118
+
119
+ From the repository root:
120
+
121
+ ```sh
122
+ bundle install
123
+ bundle exec rake
124
+ bundle exec rake build
125
+ ```
126
+
127
+ Gem packages are written to the repository root's `pkg/` directory. To build a
128
+ native gem for the current Ruby and platform, use `bundle exec rake native gem`.
129
+ For the same multi-version build used in CI, install Docker and run:
130
+
131
+ ```sh
132
+ bundle exec rb-sys-dock --platform x86_64-linux --ruby-versions 3.2,3.3,3.4,4.0 --build
133
+ ```
134
+
135
+ The Linux build targets `x86_64-linux` and `aarch64-linux` produce explicitly
136
+ tagged `-linux-gnu` gems, so RubyGems can distinguish glibc from musl.
137
+ The rb_sys version pinned in the root Gemfile selects the cross-compilation image.
138
+
139
+ ## Releasing to RubyGems
140
+
141
+ The gem has its own semantic version, independent of the Rust library. Releases
142
+ use `ruby-vX.Y.Z` tags and [the Ruby Gem Release workflow](../../.github/workflows/ruby-release.yml).
143
+ Each release runs the Ruby tests, builds a source gem and seven platform gems,
144
+ installs and exercises every native gem on all four supported Ruby versions, and
145
+ publishes the validated artifacts to RubyGems and a GitHub release.
146
+
147
+ ### One-time setup
148
+
149
+ 1. Create a GitHub environment named `release` in `Antti/audiowaveform`, allowing
150
+ deployments only from tags matching `ruby-v*`.
151
+ 2. Sign in to the RubyGems account that will own `audiowaveform`, with MFA enabled.
152
+ For the first release, create a **pending trusted publisher** from your profile.
153
+ For an existing gem you own, use its **Trusted publishers** page.
154
+ 3. Configure these exact values:
155
+
156
+ | Field | Value |
157
+ | --- | --- |
158
+ | Gem name (pending publisher) | `audiowaveform` |
159
+ | Repository owner | `Antti` |
160
+ | Repository name | `audiowaveform` |
161
+ | Workflow filename | `ruby-release.yml` |
162
+ | Environment | `release` |
163
+
164
+ Leave the optional reusable-workflow repository fields empty: publishing
165
+ happens directly in `ruby-release.yml`. No RubyGems API-key secret is needed.
166
+ Pending publishers expire after 12 hours, so create one shortly before the
167
+ first release; recreate it if it expires before the first successful push.
168
+ See the [RubyGems Trusted Publishing guide](https://guides.rubygems.org/trusted-publishing/).
169
+
170
+ ### Each release
171
+
172
+ 1. Update `bindings/ruby/lib/audiowaveform/version.rb` and the [Ruby changelog](CHANGELOG.md).
173
+ Refresh the local Bundler lockfile with `bundle install` after changing the version.
174
+ 2. Run `bundle exec rake` and `bundle exec rake build`, then commit and merge the
175
+ release preparation. The source gem is `pkg/audiowaveform-X.Y.Z.gem`.
176
+ 3. Optionally run **Ruby Gem Release** manually on that commit. Manual runs build
177
+ and test all packages without publishing, including when run against a tag.
178
+ 4. Tag the release commit and push the tag to the project repository:
179
+
180
+ ```sh
181
+ git tag -a ruby-vX.Y.Z -m "Release Ruby gem X.Y.Z"
182
+ git push antti ruby-vX.Y.Z
183
+ ```
184
+
185
+ Replace `X.Y.Z` with the gem version, and `antti` with your remote name if different.
186
+ The workflow rejects a tag that does not match the Ruby version file.
187
+ 5. Check that the release workflow succeeds and all eight artifacts are visible
188
+ on RubyGems and the GitHub release.
189
+
190
+ If publishing fails partway through, rerun the failed job using the same artifacts.
191
+ It skips previously uploaded gems only when their SHA-256 checksums match. A
192
+ different artifact under the same version/platform is rejected; release a new
193
+ version instead. Do not move an existing release tag.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AudioWaveform
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "audiowaveform/version"
4
+ require "rbconfig"
5
+
6
+ # Platform gems contain one extension per Ruby minor version. Source builds put
7
+ # the extension directly under audiowaveform/.
8
+ native_extension = File.join(
9
+ __dir__, "audiowaveform", RUBY_VERSION[/\A\d+\.\d+/],
10
+ "audiowaveform_ruby.#{RbConfig::CONFIG.fetch('DLEXT')}"
11
+ )
12
+ if File.file?(native_extension)
13
+ require native_extension
14
+ else
15
+ require "audiowaveform/audiowaveform_ruby"
16
+ end
17
+
18
+ module AudioWaveform
19
+ class << self
20
+ # Generates waveform data from an audio file.
21
+ def generate(
22
+ input,
23
+ samples_per_pixel: nil,
24
+ pixels_per_second: nil,
25
+ split_channels: false,
26
+ amplitude_scale: nil
27
+ )
28
+ scale_kind, scale_value = resolve_scale(samples_per_pixel, pixels_per_second)
29
+ amplitude_kind, amplitude_value = resolve_amplitude_scale(amplitude_scale)
30
+
31
+ Native.generate(
32
+ File.path(input),
33
+ scale_kind,
34
+ scale_value,
35
+ !!split_channels,
36
+ amplitude_kind,
37
+ amplitude_value
38
+ )
39
+ end
40
+
41
+ private
42
+
43
+ def resolve_scale(samples_per_pixel, pixels_per_second)
44
+ if samples_per_pixel && pixels_per_second
45
+ raise ArgumentError, "samples_per_pixel and pixels_per_second are mutually exclusive"
46
+ end
47
+
48
+ if pixels_per_second
49
+ ["pixels_per_second", positive_integer(pixels_per_second, :pixels_per_second)]
50
+ else
51
+ value = samples_per_pixel || 256
52
+ ["samples_per_pixel", positive_integer(value, :samples_per_pixel, minimum: 2)]
53
+ end
54
+ end
55
+
56
+ def resolve_amplitude_scale(value)
57
+ return ["none", 0.0] if value.nil?
58
+ return ["auto", 0.0] if value == :auto || value == "auto"
59
+
60
+ numeric = Float(value)
61
+ unless numeric.finite? && numeric >= 0.0
62
+ raise ArgumentError, "amplitude_scale must be a finite non-negative number or :auto"
63
+ end
64
+
65
+ ["fixed", numeric]
66
+ rescue TypeError, ArgumentError
67
+ raise ArgumentError, "amplitude_scale must be a finite non-negative number or :auto"
68
+ end
69
+
70
+ def positive_integer(value, name, minimum: 1)
71
+ unless value.is_a?(Integer) && value >= minimum
72
+ raise ArgumentError, "#{name} must be an integer greater than or equal to #{minimum}"
73
+ end
74
+
75
+ value
76
+ end
77
+ end
78
+
79
+ class Waveform
80
+ private_class_method :new
81
+
82
+ alias size length
83
+ alias bits storage_bits
84
+ alias duration_seconds duration
85
+
86
+ # Returns the [minimum, maximum] pair at +index+ for +channel+.
87
+ def point(index, channel: 0)
88
+ unless index.is_a?(Integer) && index.between?(0, length - 1) &&
89
+ channel.is_a?(Integer) && channel.between?(0, channels - 1)
90
+ raise IndexError, "waveform point is outside the available channel or index range"
91
+ end
92
+
93
+ value = __point(channel, index)
94
+ return value if value
95
+
96
+ raise IndexError, "waveform point is outside the available channel or index range"
97
+ end
98
+
99
+ # Writes waveform data to +path+ and returns self.
100
+ def save(path, format: nil, bits: storage_bits)
101
+ resolved_format = format || File.extname(File.path(path)).delete_prefix(".")
102
+ __save(File.path(path), resolved_format.to_s, validate_bits(bits))
103
+ self
104
+ end
105
+
106
+ # Returns binary DAT waveform data.
107
+ def to_dat(bits: storage_bits)
108
+ __serialize("dat", validate_bits(bits))
109
+ end
110
+
111
+ # Returns JSON waveform data.
112
+ def to_json(*, bits: storage_bits)
113
+ __serialize("json", validate_bits(bits)).force_encoding(Encoding::UTF_8)
114
+ end
115
+
116
+ # Returns plain-text waveform data.
117
+ def to_txt(bits: storage_bits)
118
+ __serialize("txt", validate_bits(bits)).force_encoding(Encoding::UTF_8)
119
+ end
120
+
121
+ private
122
+
123
+ def validate_bits(bits)
124
+ return bits if bits == 8 || bits == 16
125
+
126
+ raise ArgumentError, "bits must be either 8 or 16"
127
+ end
128
+ end
129
+
130
+ private_constant :Native
131
+ end
@@ -0,0 +1,33 @@
1
+ module AudioWaveform
2
+ VERSION: String
3
+
4
+ class Error < StandardError
5
+ end
6
+
7
+ def self.generate: (
8
+ path input,
9
+ ?samples_per_pixel: Integer?,
10
+ ?pixels_per_second: Integer?,
11
+ ?split_channels: bool,
12
+ ?amplitude_scale: (Numeric | :auto | String)?
13
+ ) -> Waveform
14
+
15
+ class Waveform
16
+ def sample_rate: () -> Integer
17
+ def samples_per_pixel: () -> Integer
18
+ def channels: () -> Integer
19
+ def storage_bits: () -> Integer
20
+ def bits: () -> Integer
21
+ def length: () -> Integer
22
+ def size: () -> Integer
23
+ def empty?: () -> bool
24
+ def duration: () -> Float
25
+ def duration_seconds: () -> Float
26
+ def data: () -> Array[Integer]
27
+ def point: (Integer index, ?channel: Integer) -> [Integer, Integer]
28
+ def save: (path path, ?format: (String | Symbol)?, ?bits: Integer) -> self
29
+ def to_dat: (?bits: Integer) -> String
30
+ def to_json: (*untyped arguments, ?bits: Integer) -> String
31
+ def to_txt: (?bits: Integer) -> String
32
+ end
33
+ end