audiowaveform 0.1.0

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +674 -0
  3. data/Cargo.lock +1108 -0
  4. data/Cargo.toml +28 -0
  5. data/README.md +255 -0
  6. data/bindings/ruby/CHANGELOG.md +29 -0
  7. data/bindings/ruby/Cargo.lock +621 -0
  8. data/bindings/ruby/Cargo.toml +8 -0
  9. data/bindings/ruby/README.md +193 -0
  10. data/bindings/ruby/ext/audiowaveform/Cargo.toml +20 -0
  11. data/bindings/ruby/ext/audiowaveform/build.rs +4 -0
  12. data/bindings/ruby/ext/audiowaveform/extconf.rb +6 -0
  13. data/bindings/ruby/ext/audiowaveform/src/lib.rs +239 -0
  14. data/bindings/ruby/lib/audiowaveform/version.rb +5 -0
  15. data/bindings/ruby/lib/audiowaveform.rb +131 -0
  16. data/crates/audiowaveform/Cargo.toml +73 -0
  17. data/crates/audiowaveform/examples/generate_from_pcm.rs +25 -0
  18. data/crates/audiowaveform/examples/generate_waveform.rs +18 -0
  19. data/crates/audiowaveform/examples/render_waveform.rs +18 -0
  20. data/crates/audiowaveform/examples/resample_waveform.rs +19 -0
  21. data/crates/audiowaveform/src/audio.rs +938 -0
  22. data/crates/audiowaveform/src/color.rs +201 -0
  23. data/crates/audiowaveform/src/error.rs +89 -0
  24. data/crates/audiowaveform/src/format.rs +215 -0
  25. data/crates/audiowaveform/src/lib.rs +79 -0
  26. data/crates/audiowaveform/src/render.rs +802 -0
  27. data/crates/audiowaveform/src/wav.rs +95 -0
  28. data/crates/audiowaveform/src/waveform.rs +790 -0
  29. data/crates/audiowaveform/tests/formats.rs +223 -0
  30. data/crates/audiowaveform/tests/generate.rs +233 -0
  31. data/crates/audiowaveform/tests/render.rs +263 -0
  32. data/crates/audiowaveform/tests/support/mod.rs +125 -0
  33. data/crates/audiowaveform/tests/wav.rs +54 -0
  34. data/crates/audiowaveform/tests/waveform_io.rs +255 -0
  35. data/crates/audiowaveform-cli/Cargo.toml +43 -0
  36. data/crates/audiowaveform-cli/src/main.rs +803 -0
  37. data/crates/audiowaveform-cli/tests/cli.rs +483 -0
  38. data/crates/audiowaveform-cli/tests/support/mod.rs +111 -0
  39. data/sig/audiowaveform.rbs +33 -0
  40. metadata +100 -0
@@ -0,0 +1,483 @@
1
+ mod support;
2
+
3
+ use assert_cmd::Command;
4
+ #[cfg(feature = "format-wav")]
5
+ use audiowaveform::Waveform;
6
+ use predicates::prelude::*;
7
+
8
+ #[cfg(all(feature = "format-mp3", feature = "wav-output"))]
9
+ use self::support::assert_wav_file_matches_fixture;
10
+ #[cfg(any(
11
+ feature = "format-wav",
12
+ feature = "format-m4a",
13
+ feature = "render",
14
+ all(feature = "format-mp3", feature = "wav-output")
15
+ ))]
16
+ use self::support::fixture_path;
17
+ #[cfg(any(
18
+ feature = "format-wav",
19
+ feature = "render",
20
+ all(feature = "format-mp3", feature = "wav-output")
21
+ ))]
22
+ use self::support::named_temp_file;
23
+ use self::support::read_fixture;
24
+ #[cfg(feature = "render")]
25
+ use self::support::{assert_png_bytes_match_fixture, assert_png_file_matches_fixture};
26
+
27
+ #[test]
28
+ fn prints_help_and_version() {
29
+ Command::cargo_bin("audiowaveform")
30
+ .expect("binary")
31
+ .arg("--help")
32
+ .assert()
33
+ .success()
34
+ .stdout(predicate::str::contains("Usage: audiowaveform [OPTIONS]"))
35
+ .stdout(predicate::str::contains(
36
+ "Generate waveform data and images from audio",
37
+ ));
38
+
39
+ Command::cargo_bin("audiowaveform")
40
+ .expect("binary")
41
+ .arg("--version")
42
+ .assert()
43
+ .success()
44
+ .stdout(predicate::str::contains("AudioWaveform v"));
45
+ }
46
+
47
+ #[test]
48
+ fn requires_input_and_output_configuration() {
49
+ Command::cargo_bin("audiowaveform")
50
+ .expect("binary")
51
+ .assert()
52
+ .failure()
53
+ .stderr("Error: Must specify either input filename or input format\n");
54
+
55
+ Command::cargo_bin("audiowaveform")
56
+ .expect("binary")
57
+ .args(["--input-format", "wav"])
58
+ .assert()
59
+ .failure()
60
+ .stderr("Error: Must specify either output filename or output format\n");
61
+ }
62
+
63
+ #[test]
64
+ fn rejects_invalid_enum_values_via_clap() {
65
+ Command::cargo_bin("audiowaveform")
66
+ .expect("binary")
67
+ .args(["--colors", "test"])
68
+ .assert()
69
+ .failure()
70
+ .stderr(predicate::str::contains("invalid value 'test'"))
71
+ .stderr(predicate::str::contains("possible values"));
72
+ }
73
+
74
+ #[cfg(feature = "render")]
75
+ #[test]
76
+ fn rejects_non_finite_numeric_values() {
77
+ let input = fixture_path("test_file_stereo_8bit_64spp_wav.dat");
78
+ let input = input.to_str().expect("utf8");
79
+
80
+ Command::cargo_bin("audiowaveform")
81
+ .expect("binary")
82
+ .args([
83
+ "-q",
84
+ "-i",
85
+ input,
86
+ "--output-format",
87
+ "png",
88
+ "-z",
89
+ "64",
90
+ "--start",
91
+ "inf",
92
+ ])
93
+ .assert()
94
+ .failure()
95
+ .stderr("Invalid start time: minimum 0\n");
96
+
97
+ Command::cargo_bin("audiowaveform")
98
+ .expect("binary")
99
+ .args([
100
+ "-q",
101
+ "-i",
102
+ input,
103
+ "--output-format",
104
+ "png",
105
+ "-z",
106
+ "64",
107
+ "--amplitude-scale",
108
+ "NaN",
109
+ ])
110
+ .assert()
111
+ .failure()
112
+ .stderr("Error: Invalid amplitude scale: must be a positive number\n");
113
+ }
114
+
115
+ #[test]
116
+ fn rejects_raw_channel_counts_that_do_not_fit_the_library_type() {
117
+ Command::cargo_bin("audiowaveform")
118
+ .expect("binary")
119
+ .args([
120
+ "-q",
121
+ "--input-format",
122
+ "raw",
123
+ "--output-format",
124
+ "wav",
125
+ "--raw-samplerate",
126
+ "48000",
127
+ "--raw-channels",
128
+ "65537",
129
+ "--raw-format",
130
+ "s16le",
131
+ ])
132
+ .assert()
133
+ .failure()
134
+ .stderr("Invalid number of input channels: maximum 65535\n");
135
+ }
136
+
137
+ #[cfg(feature = "format-wav")]
138
+ #[test]
139
+ fn generates_dat_output_to_file_and_stdout() {
140
+ let output = named_temp_file(".dat");
141
+ Command::cargo_bin("audiowaveform")
142
+ .expect("binary")
143
+ .args([
144
+ "-i",
145
+ fixture_path("test_file_stereo.wav").to_str().expect("utf8"),
146
+ "-o",
147
+ output.path().to_str().expect("utf8"),
148
+ "-b",
149
+ "8",
150
+ "-z",
151
+ "64",
152
+ ])
153
+ .assert()
154
+ .success()
155
+ .stderr("Done\n");
156
+ assert_eq!(
157
+ std::fs::read(output.path()).expect("read output"),
158
+ read_fixture("test_file_stereo_8bit_64spp_wav.dat")
159
+ );
160
+
161
+ Command::cargo_bin("audiowaveform")
162
+ .expect("binary")
163
+ .args([
164
+ "--input-format",
165
+ "wav",
166
+ "--output-format",
167
+ "dat",
168
+ "-b",
169
+ "8",
170
+ "-z",
171
+ "64",
172
+ ])
173
+ .write_stdin(read_fixture("test_file_stereo.wav"))
174
+ .assert()
175
+ .success()
176
+ .stdout(read_fixture("test_file_stereo_8bit_64spp_wav.dat"))
177
+ .stderr("Done\n");
178
+ }
179
+
180
+ #[cfg(feature = "format-wav")]
181
+ #[test]
182
+ fn generates_json_and_text_outputs_to_stdout() {
183
+ Command::cargo_bin("audiowaveform")
184
+ .expect("binary")
185
+ .args([
186
+ "--input-format",
187
+ "wav",
188
+ "--output-format",
189
+ "json",
190
+ "-b",
191
+ "8",
192
+ "-z",
193
+ "64",
194
+ ])
195
+ .write_stdin(read_fixture("test_file_stereo.wav"))
196
+ .assert()
197
+ .success()
198
+ .stdout(read_fixture("test_file_stereo_8bit_64spp_wav.json"))
199
+ .stderr("Done\n");
200
+
201
+ Command::cargo_bin("audiowaveform")
202
+ .expect("binary")
203
+ .args([
204
+ "-i",
205
+ fixture_path("test_file_stereo_8bit_64spp_wav.dat")
206
+ .to_str()
207
+ .expect("utf8"),
208
+ "--output-format",
209
+ "txt",
210
+ ])
211
+ .assert()
212
+ .success()
213
+ .stdout(read_fixture("test_file_stereo_8bit_64spp_wav.txt"))
214
+ .stderr("Done\n");
215
+ }
216
+
217
+ #[cfg(feature = "format-wav")]
218
+ #[test]
219
+ fn applies_fixed_amplitude_scaling_to_waveform_data_output() {
220
+ let unscaled_output = named_temp_file(".json");
221
+ let scaled_output = named_temp_file(".json");
222
+
223
+ for (output, amplitude_scale) in [(&unscaled_output, "1.0"), (&scaled_output, "2.0")] {
224
+ Command::cargo_bin("audiowaveform")
225
+ .expect("binary")
226
+ .args([
227
+ "-q",
228
+ "-i",
229
+ fixture_path("test_file_stereo.wav").to_str().expect("utf8"),
230
+ "-o",
231
+ output.path().to_str().expect("utf8"),
232
+ "-z",
233
+ "64",
234
+ "--amplitude-scale",
235
+ amplitude_scale,
236
+ ])
237
+ .assert()
238
+ .success();
239
+ }
240
+
241
+ let unscaled = Waveform::load_from_path(unscaled_output.path(), None).expect("unscaled");
242
+ let scaled = Waveform::load_from_path(scaled_output.path(), None).expect("scaled");
243
+ let expected = unscaled
244
+ .interleaved_samples()
245
+ .iter()
246
+ .map(|value| (i32::from(*value) * 2).clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16)
247
+ .collect::<Vec<_>>();
248
+
249
+ assert_eq!(scaled.interleaved_samples(), expected);
250
+ }
251
+
252
+ #[cfg(feature = "render")]
253
+ #[test]
254
+ fn generates_png_output_to_file_and_stdout() {
255
+ let output = named_temp_file(".png");
256
+ Command::cargo_bin("audiowaveform")
257
+ .expect("binary")
258
+ .args([
259
+ "-i",
260
+ fixture_path("test_file_stereo_8bit_64spp_wav.dat")
261
+ .to_str()
262
+ .expect("utf8"),
263
+ "-o",
264
+ output.path().to_str().expect("utf8"),
265
+ "-z",
266
+ "128",
267
+ ])
268
+ .assert()
269
+ .success()
270
+ .stderr("Done\n");
271
+ assert_png_file_matches_fixture(output.path(), "test_file_stereo_dat_128spp.png");
272
+
273
+ let output = Command::cargo_bin("audiowaveform")
274
+ .expect("binary")
275
+ .args([
276
+ "-i",
277
+ fixture_path("test_file_stereo_8bit_64spp_wav.dat")
278
+ .to_str()
279
+ .expect("utf8"),
280
+ "--output-format",
281
+ "png",
282
+ "-z",
283
+ "128",
284
+ ])
285
+ .assert()
286
+ .success()
287
+ .get_output()
288
+ .stdout
289
+ .clone();
290
+ assert_png_bytes_match_fixture(&output, "test_file_stereo_dat_128spp.png");
291
+ }
292
+
293
+ #[cfg(all(feature = "format-mp3", feature = "wav-output"))]
294
+ #[test]
295
+ fn transcodes_audio_to_wav_output() {
296
+ let output = named_temp_file(".wav");
297
+ Command::cargo_bin("audiowaveform")
298
+ .expect("binary")
299
+ .args([
300
+ "-i",
301
+ fixture_path("test_file_mono.mp3").to_str().expect("utf8"),
302
+ "-o",
303
+ output.path().to_str().expect("utf8"),
304
+ "--output-format",
305
+ "wav",
306
+ ])
307
+ .assert()
308
+ .success()
309
+ .stderr("Done\n");
310
+
311
+ assert_wav_file_matches_fixture(output.path(), "test_file_mono_converted.wav", 1);
312
+ }
313
+
314
+ #[cfg(feature = "format-wav")]
315
+ #[test]
316
+ fn quiet_mode_suppresses_done_output() {
317
+ let output = named_temp_file(".dat");
318
+ Command::cargo_bin("audiowaveform")
319
+ .expect("binary")
320
+ .args([
321
+ "-q",
322
+ "-i",
323
+ fixture_path("test_file_stereo.wav").to_str().expect("utf8"),
324
+ "-o",
325
+ output.path().to_str().expect("utf8"),
326
+ "-b",
327
+ "8",
328
+ "-z",
329
+ "64",
330
+ ])
331
+ .assert()
332
+ .success()
333
+ .stderr("");
334
+ }
335
+
336
+ #[cfg(feature = "format-wav")]
337
+ #[test]
338
+ fn rejects_unsupported_output_combinations() {
339
+ Command::cargo_bin("audiowaveform")
340
+ .expect("binary")
341
+ .args([
342
+ "-i",
343
+ fixture_path("test_file_stereo.wav").to_str().expect("utf8"),
344
+ "--output-format",
345
+ "mp3",
346
+ ])
347
+ .assert()
348
+ .failure()
349
+ .stderr("Can't generate mp3 format output from wav format input\n");
350
+ }
351
+
352
+ #[test]
353
+ fn converts_waveform_data_and_raw_pcm_without_optional_features() {
354
+ Command::cargo_bin("audiowaveform")
355
+ .unwrap()
356
+ .args(["-q", "--input-format", "dat", "--output-format", "txt"])
357
+ .write_stdin(read_fixture("test_file_stereo_8bit_64spp_wav.dat"))
358
+ .assert()
359
+ .success()
360
+ .stdout(read_fixture("test_file_stereo_8bit_64spp_wav.txt"));
361
+
362
+ let output = Command::cargo_bin("audiowaveform")
363
+ .unwrap()
364
+ .args([
365
+ "-q",
366
+ "--input-format",
367
+ "raw",
368
+ "--raw-samplerate",
369
+ "16000",
370
+ "--raw-channels",
371
+ "1",
372
+ "--raw-format",
373
+ "s16le",
374
+ "--output-format",
375
+ "dat",
376
+ "-b",
377
+ "8",
378
+ "-z",
379
+ "64",
380
+ ])
381
+ .write_stdin(read_fixture("test_file_mono.raw"))
382
+ .assert()
383
+ .success()
384
+ .get_output()
385
+ .stdout
386
+ .clone();
387
+ let waveform = audiowaveform::Waveform::load_from_reader(
388
+ std::io::Cursor::new(output),
389
+ audiowaveform::WaveformFormat::Dat,
390
+ )
391
+ .unwrap();
392
+ assert_eq!(waveform.sample_rate(), 16_000);
393
+ assert_eq!(waveform.channels(), 1);
394
+ assert_eq!(waveform.storage_bits(), 8);
395
+ assert_eq!(
396
+ waveform.len(),
397
+ read_fixture("test_file_mono.raw").len().div_ceil(2 * 64)
398
+ );
399
+ }
400
+
401
+ #[test]
402
+ fn reports_disabled_formats_before_creating_output_files() {
403
+ for (format, enabled, feature) in [
404
+ ("mp3", cfg!(feature = "format-mp3"), "format-mp3"),
405
+ ("m4a", cfg!(feature = "format-m4a"), "format-m4a"),
406
+ ("wav", cfg!(feature = "format-wav"), "format-wav"),
407
+ ("webm", cfg!(feature = "format-mkv"), "format-mkv"),
408
+ ] {
409
+ if enabled {
410
+ continue;
411
+ }
412
+ let directory = tempfile::tempdir().unwrap();
413
+ let output = directory.path().join("output.json");
414
+ Command::cargo_bin("audiowaveform")
415
+ .unwrap()
416
+ .args([
417
+ "-q",
418
+ "--input-format",
419
+ format,
420
+ "-o",
421
+ output.to_str().unwrap(),
422
+ ])
423
+ .assert()
424
+ .failure()
425
+ .stderr(predicate::str::contains(format!(
426
+ "enable the `{feature}` Cargo feature"
427
+ )));
428
+ assert!(!output.exists());
429
+ }
430
+ }
431
+
432
+ #[test]
433
+ fn reports_disabled_output_features() {
434
+ for (format, enabled, feature) in [
435
+ ("png", cfg!(feature = "render"), "render"),
436
+ ("wav", cfg!(feature = "wav-output"), "wav-output"),
437
+ ] {
438
+ if enabled {
439
+ continue;
440
+ }
441
+ Command::cargo_bin("audiowaveform")
442
+ .unwrap()
443
+ .args(["--input-format", "dat", "--output-format", format])
444
+ .assert()
445
+ .failure()
446
+ .stderr(predicate::str::contains(format!(
447
+ "enable the `{feature}` Cargo feature"
448
+ )));
449
+ }
450
+ }
451
+
452
+ #[cfg(feature = "format-m4a")]
453
+ #[test]
454
+ fn generates_waveforms_from_m4a_paths_and_mp4_stdin() {
455
+ for fixture in [
456
+ "formats/stereo.m4a",
457
+ "formats/alac.m4a",
458
+ "formats/video-first.mp4",
459
+ ] {
460
+ let path_output = Command::cargo_bin("audiowaveform")
461
+ .unwrap()
462
+ .args([
463
+ "-q",
464
+ "-i",
465
+ fixture_path(fixture).to_str().unwrap(),
466
+ "--output-format",
467
+ "json",
468
+ ])
469
+ .assert()
470
+ .success()
471
+ .get_output()
472
+ .stdout
473
+ .clone();
474
+ assert!(String::from_utf8_lossy(&path_output).contains("48000"));
475
+ Command::cargo_bin("audiowaveform")
476
+ .unwrap()
477
+ .args(["-q", "--input-format", "m4a", "--output-format", "json"])
478
+ .write_stdin(read_fixture(fixture))
479
+ .assert()
480
+ .success()
481
+ .stdout(path_output);
482
+ }
483
+ }
@@ -0,0 +1,111 @@
1
+ #![allow(dead_code)]
2
+
3
+ use std::fs;
4
+ use std::path::{Path, PathBuf};
5
+
6
+ use hound::WavReader;
7
+ use image::{RgbaImage, load_from_memory};
8
+ use tempfile::{Builder, NamedTempFile};
9
+
10
+ pub fn fixture_path(name: &str) -> PathBuf {
11
+ PathBuf::from(env!("CARGO_MANIFEST_DIR"))
12
+ .join("../../fixtures")
13
+ .join(name)
14
+ }
15
+
16
+ pub fn read_fixture(name: &str) -> Vec<u8> {
17
+ fs::read(fixture_path(name)).expect("fixture bytes")
18
+ }
19
+
20
+ pub fn named_temp_file(suffix: &str) -> NamedTempFile {
21
+ Builder::new()
22
+ .suffix(suffix)
23
+ .tempfile()
24
+ .expect("create temp file")
25
+ }
26
+
27
+ pub fn assert_png_file_matches_fixture(actual: impl AsRef<Path>, fixture: &str) {
28
+ let actual = image::open(actual).expect("open actual png").into_rgba8();
29
+ let expected = image::open(fixture_path(fixture))
30
+ .expect("open expected png")
31
+ .into_rgba8();
32
+ assert_png_eq(&actual, &expected, fixture);
33
+ }
34
+
35
+ pub fn assert_png_bytes_match_fixture(actual: &[u8], fixture: &str) {
36
+ let actual = load_from_memory(actual)
37
+ .expect("decode actual png")
38
+ .into_rgba8();
39
+ let expected = image::open(fixture_path(fixture))
40
+ .expect("open expected png")
41
+ .into_rgba8();
42
+ assert_png_eq(&actual, &expected, fixture);
43
+ }
44
+
45
+ fn assert_png_eq(actual: &RgbaImage, expected: &RgbaImage, fixture: &str) {
46
+ assert_eq!(
47
+ actual.dimensions(),
48
+ expected.dimensions(),
49
+ "png dimensions mismatch for {fixture}"
50
+ );
51
+ assert_eq!(
52
+ actual.as_raw(),
53
+ expected.as_raw(),
54
+ "png pixels mismatch for {fixture}"
55
+ );
56
+ }
57
+
58
+ pub fn assert_wav_file_matches_fixture(
59
+ actual: impl AsRef<Path>,
60
+ fixture: &str,
61
+ sample_tolerance: i16,
62
+ ) {
63
+ let actual = WavReader::open(actual).expect("open actual wav");
64
+ let expected = WavReader::open(fixture_path(fixture)).expect("open expected wav");
65
+ assert_wav_eq(actual, expected, fixture, sample_tolerance);
66
+ }
67
+
68
+ fn assert_wav_eq<R1: std::io::Read, R2: std::io::Read>(
69
+ mut actual: WavReader<R1>,
70
+ mut expected: WavReader<R2>,
71
+ fixture: &str,
72
+ sample_tolerance: i16,
73
+ ) {
74
+ assert_eq!(
75
+ actual.spec(),
76
+ expected.spec(),
77
+ "wav spec mismatch for {fixture}"
78
+ );
79
+ assert_eq!(
80
+ actual.duration(),
81
+ expected.duration(),
82
+ "wav duration mismatch for {fixture}"
83
+ );
84
+
85
+ let actual_samples = actual
86
+ .samples::<i16>()
87
+ .collect::<Result<Vec<_>, _>>()
88
+ .expect("read actual wav samples");
89
+ let expected_samples = expected
90
+ .samples::<i16>()
91
+ .collect::<Result<Vec<_>, _>>()
92
+ .expect("read expected wav samples");
93
+
94
+ assert_eq!(
95
+ actual_samples.len(),
96
+ expected_samples.len(),
97
+ "wav sample count mismatch for {fixture}"
98
+ );
99
+
100
+ for (index, (actual, expected)) in actual_samples
101
+ .iter()
102
+ .zip(expected_samples.iter())
103
+ .enumerate()
104
+ {
105
+ let difference = i32::from(*actual) - i32::from(*expected);
106
+ assert!(
107
+ difference.abs() <= i32::from(sample_tolerance),
108
+ "wav sample mismatch for {fixture} at index {index}: actual={actual}, expected={expected}, tolerance={sample_tolerance}"
109
+ );
110
+ }
111
+ }
@@ -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