wavify 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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/.serena/.gitignore +1 -0
  3. data/.serena/memories/project_overview.md +5 -0
  4. data/.serena/memories/style_and_completion.md +5 -0
  5. data/.serena/memories/suggested_commands.md +11 -0
  6. data/.serena/project.yml +126 -0
  7. data/.simplecov +18 -0
  8. data/.yardopts +4 -0
  9. data/CHANGELOG.md +11 -0
  10. data/LICENSE +21 -0
  11. data/README.md +196 -0
  12. data/Rakefile +190 -0
  13. data/benchmarks/README.md +46 -0
  14. data/benchmarks/benchmark_helper.rb +112 -0
  15. data/benchmarks/dsp_effects_benchmark.rb +46 -0
  16. data/benchmarks/flac_benchmark.rb +74 -0
  17. data/benchmarks/streaming_memory_benchmark.rb +94 -0
  18. data/benchmarks/wav_io_benchmark.rb +110 -0
  19. data/examples/audio_processing.rb +73 -0
  20. data/examples/cinematic_transition.rb +118 -0
  21. data/examples/drum_machine.rb +74 -0
  22. data/examples/format_convert.rb +81 -0
  23. data/examples/hybrid_arrangement.rb +165 -0
  24. data/examples/streaming_master_chain.rb +129 -0
  25. data/examples/synth_pad.rb +42 -0
  26. data/lib/wavify/audio.rb +483 -0
  27. data/lib/wavify/codecs/aiff.rb +338 -0
  28. data/lib/wavify/codecs/base.rb +108 -0
  29. data/lib/wavify/codecs/flac.rb +1322 -0
  30. data/lib/wavify/codecs/ogg_vorbis.rb +1447 -0
  31. data/lib/wavify/codecs/raw.rb +193 -0
  32. data/lib/wavify/codecs/registry.rb +87 -0
  33. data/lib/wavify/codecs/wav.rb +459 -0
  34. data/lib/wavify/core/duration.rb +99 -0
  35. data/lib/wavify/core/format.rb +133 -0
  36. data/lib/wavify/core/sample_buffer.rb +216 -0
  37. data/lib/wavify/core/stream.rb +129 -0
  38. data/lib/wavify/dsl.rb +537 -0
  39. data/lib/wavify/dsp/effects/chorus.rb +98 -0
  40. data/lib/wavify/dsp/effects/compressor.rb +85 -0
  41. data/lib/wavify/dsp/effects/delay.rb +69 -0
  42. data/lib/wavify/dsp/effects/distortion.rb +64 -0
  43. data/lib/wavify/dsp/effects/effect_base.rb +68 -0
  44. data/lib/wavify/dsp/effects/reverb.rb +112 -0
  45. data/lib/wavify/dsp/effects.rb +21 -0
  46. data/lib/wavify/dsp/envelope.rb +97 -0
  47. data/lib/wavify/dsp/filter.rb +271 -0
  48. data/lib/wavify/dsp/oscillator.rb +123 -0
  49. data/lib/wavify/errors.rb +34 -0
  50. data/lib/wavify/sequencer/engine.rb +278 -0
  51. data/lib/wavify/sequencer/note_sequence.rb +132 -0
  52. data/lib/wavify/sequencer/pattern.rb +102 -0
  53. data/lib/wavify/sequencer/track.rb +298 -0
  54. data/lib/wavify/sequencer.rb +12 -0
  55. data/lib/wavify/version.rb +6 -0
  56. data/lib/wavify.rb +28 -0
  57. data/tools/fixture_writer.rb +85 -0
  58. metadata +129 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0573e324cb4eb544c7a82ed254dd7fcd473439747b5c675ab474457d5cad1fec
4
+ data.tar.gz: a8659118673abb114e917ce23c5c8e332338962f87fe42eec9bab562628b4af9
5
+ SHA512:
6
+ metadata.gz: 197cda12919d9acb3a80b17cb6526a15e2fc5b2c8c442144f299d938d0306f0b0bc332ce57da8ec98ebe87a04b11b18b3b157d2ea2bcfbf570d3266b7dde855d
7
+ data.tar.gz: 96432c4aadea84b31a83a231678dde7faed327246e45fa7d34ca4a5cc16d0ab1da200faaef4ab7f0980ec6de33402bf1f5a82e628b7921c5447447c3793f5f41
@@ -0,0 +1 @@
1
+ /cache
@@ -0,0 +1,5 @@
1
+ # Wavify project overview
2
+ - Purpose: Pure Ruby audio processing gem with immutable, chainable audio transforms and codec support.
3
+ - Tech stack: Ruby (>=3.1), Bundler, RSpec, Rake, RuboCop, YARD/RBS (`sig/`).
4
+ - Current codec status of interest: OGG Vorbis supports container/header parsing and metadata; audio decode is under implementation with preflight scaffolding.
5
+ - Structure: `lib/` implementation, `spec/` tests, `examples/`, `benchmarks/`, `bin/`, `tools/`, `sig/`.
@@ -0,0 +1,5 @@
1
+ # Style and completion guidance
2
+ - Ruby codebase uses frozen string literal comments and idiomatic snake_case naming.
3
+ - Prefer small incremental changes with tests (RSpec) and keep public behavior stable unless task says otherwise.
4
+ - For codec work, add focused fixtures/spec regressions for parsing and decode preflight behavior.
5
+ - Task completion checks: run relevant specs first (target file), then broader `bundle exec rspec` if changes touch shared paths; ensure git worktree is clean except intended changes.
@@ -0,0 +1,11 @@
1
+ # Suggested commands (Darwin shell)
2
+ - `git status --short`
3
+ - `git log --oneline -10`
4
+ - `bundle exec rspec`
5
+ - `bundle exec rspec spec/codecs/ogg_vorbis_spec.rb`
6
+ - `bundle exec rubocop`
7
+ - `bundle exec rake spec:coverage COVERAGE_MINIMUM=90`
8
+ - `bundle exec rake docs:examples`
9
+ - `bundle exec rake docs:yard`
10
+ - `bundle exec rake docs:check YARD_MINIMUM=85`
11
+ - `rake bench:flac` / `rake bench:all`
@@ -0,0 +1,126 @@
1
+ # the name by which the project can be referenced within Serena
2
+ project_name: "wavify"
3
+
4
+
5
+ # list of languages for which language servers are started; choose from:
6
+ # al bash clojure cpp csharp
7
+ # csharp_omnisharp dart elixir elm erlang
8
+ # fortran fsharp go groovy haskell
9
+ # java julia kotlin lua markdown
10
+ # matlab nix pascal perl php
11
+ # php_phpactor powershell python python_jedi r
12
+ # rego ruby ruby_solargraph rust scala
13
+ # swift terraform toml typescript typescript_vts
14
+ # vue yaml zig
15
+ # (This list may be outdated. For the current list, see values of Language enum here:
16
+ # https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
17
+ # For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
18
+ # Note:
19
+ # - For C, use cpp
20
+ # - For JavaScript, use typescript
21
+ # - For Free Pascal/Lazarus, use pascal
22
+ # Special requirements:
23
+ # Some languages require additional setup/installations.
24
+ # See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
25
+ # When using multiple languages, the first language server that supports a given file will be used for that file.
26
+ # The first language is the default language and the respective language server will be used as a fallback.
27
+ # Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
28
+ languages:
29
+ - ruby
30
+
31
+ # the encoding used by text files in the project
32
+ # For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
33
+ encoding: "utf-8"
34
+
35
+ # whether to use project's .gitignore files to ignore files
36
+ ignore_all_files_in_gitignore: true
37
+
38
+ # list of additional paths to ignore in this project.
39
+ # Same syntax as gitignore, so you can use * and **.
40
+ # Note: global ignored_paths from serena_config.yml are also applied additively.
41
+ ignored_paths: []
42
+
43
+ # whether the project is in read-only mode
44
+ # If set to true, all editing tools will be disabled and attempts to use them will result in an error
45
+ # Added on 2025-04-18
46
+ read_only: false
47
+
48
+ # list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
49
+ # Below is the complete list of tools for convenience.
50
+ # To make sure you have the latest list of tools, and to view their descriptions,
51
+ # execute `uv run scripts/print_tool_overview.py`.
52
+ #
53
+ # * `activate_project`: Activates a project by name.
54
+ # * `check_onboarding_performed`: Checks whether project onboarding was already performed.
55
+ # * `create_text_file`: Creates/overwrites a file in the project directory.
56
+ # * `delete_lines`: Deletes a range of lines within a file.
57
+ # * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
58
+ # * `execute_shell_command`: Executes a shell command.
59
+ # * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
60
+ # * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
61
+ # * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
62
+ # * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
63
+ # * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
64
+ # * `initial_instructions`: Gets the initial instructions for the current project.
65
+ # Should only be used in settings where the system prompt cannot be set,
66
+ # e.g. in clients you have no control over, like Claude Desktop.
67
+ # * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
68
+ # * `insert_at_line`: Inserts content at a given line in a file.
69
+ # * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
70
+ # * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
71
+ # * `list_memories`: Lists memories in Serena's project-specific memory store.
72
+ # * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
73
+ # * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
74
+ # * `read_file`: Reads a file within the project directory.
75
+ # * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
76
+ # * `remove_project`: Removes a project from the Serena configuration.
77
+ # * `replace_lines`: Replaces a range of lines within a file with new content.
78
+ # * `replace_symbol_body`: Replaces the full definition of a symbol.
79
+ # * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
80
+ # * `search_for_pattern`: Performs a search for a pattern in the project.
81
+ # * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
82
+ # * `switch_modes`: Activates modes by providing a list of their names
83
+ # * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
84
+ # * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
85
+ # * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
86
+ # * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
87
+ excluded_tools: []
88
+
89
+ # list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
90
+ included_optional_tools: []
91
+
92
+ # fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
93
+ # This cannot be combined with non-empty excluded_tools or included_optional_tools.
94
+ fixed_tools: []
95
+
96
+ # list of mode names to that are always to be included in the set of active modes
97
+ # The full set of modes to be activated is base_modes + default_modes.
98
+ # If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
99
+ # Otherwise, this setting overrides the global configuration.
100
+ # Set this to [] to disable base modes for this project.
101
+ # Set this to a list of mode names to always include the respective modes for this project.
102
+ base_modes:
103
+
104
+ # list of mode names that are to be activated by default.
105
+ # The full set of modes to be activated is base_modes + default_modes.
106
+ # If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
107
+ # Otherwise, this overrides the setting from the global configuration (serena_config.yml).
108
+ # This setting can, in turn, be overridden by CLI parameters (--mode).
109
+ default_modes:
110
+
111
+ # initial prompt for the project. It will always be given to the LLM upon activating the project
112
+ # (contrary to the memories, which are loaded on demand).
113
+ initial_prompt: ""
114
+
115
+ # time budget (seconds) per tool call for the retrieval of additional symbol information
116
+ # such as docstrings or parameter information.
117
+ # This overrides the corresponding setting in the global configuration; see the documentation there.
118
+ # If null or missing, use the setting from the global configuration.
119
+ symbol_info_budget:
120
+
121
+ # The language backend to use for this project.
122
+ # If not set, the global setting from serena_config.yml is used.
123
+ # Valid values: LSP, JetBrains
124
+ # Note: the backend is fixed at startup. If a project with a different backend
125
+ # is activated post-init, an error will be returned.
126
+ language_backend:
data/.simplecov ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.configure do
4
+ enable_coverage :branch if ENV["SIMPLECOV_BRANCH"] == "1"
5
+
6
+ add_filter "/spec/"
7
+ add_filter "/examples/"
8
+ add_filter "/benchmarks/"
9
+ add_filter "/tmp/"
10
+
11
+ add_group "Core", "lib/wavify/core"
12
+ add_group "Codecs", "lib/wavify/codecs"
13
+ add_group "DSP", "lib/wavify/dsp"
14
+ add_group "Sequencer", "lib/wavify/sequencer"
15
+
16
+ minimum = ENV.fetch("COVERAGE_MINIMUM", nil)
17
+ minimum_coverage(minimum.to_i) if minimum && !minimum.empty?
18
+ end
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --readme README.md
2
+ --markup markdown
3
+ --output-dir doc
4
+ lib/**/*.rb
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-03-04
10
+
11
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # Wavify
2
+
3
+ Wavify is a Ruby audio processing toolkit with immutable transforms, codec I/O, streaming pipelines, DSP effects, and a sequencing DSL.
4
+
5
+ ## Requirements
6
+
7
+ - Ruby `>= 3.1`
8
+ - Bundler
9
+ - Native build environment for gems with C extensions (`ogg-ruby`, `vorbis`)
10
+
11
+ ## Installation
12
+
13
+ Add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem "wavify"
17
+ ```
18
+
19
+ Then install:
20
+
21
+ ```bash
22
+ bundle install
23
+ ```
24
+
25
+ Or install directly:
26
+
27
+ ```bash
28
+ gem install wavify
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ```ruby
34
+ require "wavify"
35
+
36
+ format = Wavify::Core::Format::CD_QUALITY
37
+
38
+ audio = Wavify::Audio.tone(
39
+ frequency: 440.0,
40
+ duration: 1.0,
41
+ waveform: :sine,
42
+ format: format
43
+ )
44
+
45
+ audio.fade_in(0.02).fade_out(0.05).write("tone.wav")
46
+ ```
47
+
48
+ ## Core API
49
+
50
+ ### `Wavify::Audio`
51
+
52
+ Main constructors:
53
+
54
+ - `Audio.read(path, format: nil, codec_options: {})`
55
+ - `Audio.stream(path_or_io, chunk_size: 4096, format: nil, codec_options: {})`
56
+ - `Audio.tone(frequency:, duration:, waveform:, format:)`
57
+ - `Audio.silence(duration_seconds, format:)`
58
+ - `Audio.mix(*audios)`
59
+
60
+ Immutable transforms (each also has `!` in-place variants):
61
+
62
+ - `gain`, `normalize`, `trim`, `fade_in`, `fade_out`, `pan`, `reverse`, `loop`, `apply`
63
+
64
+ Utility methods:
65
+
66
+ - `convert`, `split(at:)`, `peak_amplitude`, `rms_amplitude`, `duration`, `sample_frame_count`
67
+
68
+ ### Streaming pipeline
69
+
70
+ ```ruby
71
+ Wavify::Audio.stream("input.wav", chunk_size: 4096)
72
+ .pipe(Wavify::Effects::Compressor.new(threshold: -18, ratio: 3.0))
73
+ .pipe(Wavify::Effects::Chorus.new(rate: 0.5, depth: 0.2, mix: 0.15))
74
+ .write_to("output.aiff", format: Wavify::Core::Format::CD_QUALITY)
75
+ ```
76
+
77
+ `pipe` accepts processors that respond to `call`, `process`, or `apply`.
78
+
79
+ ## Format Support
80
+
81
+ | Format | Read | Write | Stream Read | Stream Write | Notes |
82
+ |--------|------|-------|-------------|--------------|-------|
83
+ | WAV | ✅ | ✅ | ✅ | ✅ | PCM + float WAV, including extensible WAV |
84
+ | AIFF | ✅ | ✅ | ✅ | ✅ | PCM only (AIFC unsupported) |
85
+ | FLAC | ✅ | ✅ | ✅ | ✅ | Pure Ruby implementation |
86
+ | OGG Vorbis | ✅ | ✅ | ✅ | ✅ | Backed by `ogg-ruby` + `vorbis` |
87
+ | Raw PCM/Float | ✅* | ✅ | ✅* | ✅ | `format:` is required for read/stream-read/metadata |
88
+
89
+ Raw example:
90
+
91
+ ```ruby
92
+ raw_format = Wavify::Core::Format.new(channels: 2, sample_rate: 44_100, bit_depth: 16, sample_format: :pcm)
93
+ audio = Wavify::Audio.read("input.pcm", format: raw_format)
94
+ audio.write("output.wav")
95
+ ```
96
+
97
+ ### OGG Vorbis notes
98
+
99
+ - `read` / `stream_read` support sequential chained streams and interleaved multi-stream OGG.
100
+ - Interleaved multi-stream decode is mixed into one output stream.
101
+ - If interleaved streams have different sample rates, they are resampled to the first logical stream's sample rate before mix.
102
+ - `decode_mode: :strict` and `decode_mode: :placeholder` are accepted for API compatibility.
103
+
104
+ ## Sequencer DSL
105
+
106
+ Use `Wavify.build` for one-shot rendering/writing, or `Wavify::DSL.build_definition` when you want timeline access.
107
+
108
+ ```ruby
109
+ song = Wavify::DSL.build_definition(format: Wavify::Core::Format::CD_QUALITY, tempo: 116, default_bars: 2) do
110
+ track :kick do
111
+ synth :sine
112
+ notes "C2 . . . C2 . . .", resolution: 16
113
+ envelope attack: 0.001, decay: 0.05, sustain: 0.0, release: 0.06
114
+ gain(-4)
115
+ end
116
+
117
+ arrange do
118
+ section :intro, bars: 1, tracks: %i[kick]
119
+ end
120
+ end
121
+
122
+ timeline = song.timeline
123
+ mix = song.render
124
+ mix.write("song.wav")
125
+ ```
126
+
127
+ ## DSP
128
+
129
+ Built-in modules:
130
+
131
+ - Oscillator waveforms: `:sine`, `:square`, `:sawtooth`, `:triangle`, `:white_noise`, `:pink_noise`
132
+ - Envelope (ADSR)
133
+ - Biquad filters (lowpass/highpass/bandpass/notch/peaking/shelves)
134
+ - Effects: `Delay`, `Reverb`, `Chorus`, `Distortion`, `Compressor`
135
+
136
+ ## Examples
137
+
138
+ Scripts in `examples/`:
139
+
140
+ - `examples/format_convert.rb`
141
+ - `examples/audio_processing.rb`
142
+ - `examples/synth_pad.rb`
143
+ - `examples/drum_machine.rb`
144
+ - `examples/hybrid_arrangement.rb`
145
+ - `examples/streaming_master_chain.rb`
146
+ - `examples/cinematic_transition.rb`
147
+
148
+ Run:
149
+
150
+ ```bash
151
+ ruby examples/synth_pad.rb
152
+ ```
153
+
154
+ ## Development
155
+
156
+ Install dependencies:
157
+
158
+ ```bash
159
+ bundle install
160
+ ```
161
+
162
+ Run tests:
163
+
164
+ ```bash
165
+ bundle exec rspec
166
+ bundle exec rake spec:coverage COVERAGE_MINIMUM=90
167
+ ```
168
+
169
+ Generate/check docs:
170
+
171
+ ```bash
172
+ bundle exec rake docs:examples
173
+ bundle exec rake docs:yard
174
+ YARD_MINIMUM=85 bundle exec rake docs:check
175
+ bundle exec rake docs:all
176
+ ```
177
+
178
+ Benchmarks:
179
+
180
+ ```bash
181
+ bundle exec rake bench:wav_io
182
+ bundle exec rake bench:dsp
183
+ bundle exec rake bench:flac
184
+ bundle exec rake bench:stream
185
+ bundle exec rake bench:all
186
+ ```
187
+
188
+ Release checks:
189
+
190
+ ```bash
191
+ bundle exec rake release:check
192
+ ```
193
+
194
+ ## License
195
+
196
+ Wavify is released under the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "rbconfig"
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ namespace :spec do
10
+ desc "Generate audio fixtures from YAML definitions"
11
+ task :create_fixtures do
12
+ ruby = RbConfig.ruby
13
+ success = system(ruby, File.expand_path("tools/fixture_writer.rb", __dir__))
14
+ abort("fixture generation failed") unless success
15
+ end
16
+
17
+ desc "Run specs with SimpleCov coverage (set COVERAGE_MINIMUM=90 to enforce target)"
18
+ task :coverage do
19
+ ruby = RbConfig.ruby
20
+ env = { "COVERAGE" => "1" }
21
+ env["COVERAGE_MINIMUM"] = ENV["COVERAGE_MINIMUM"] if ENV["COVERAGE_MINIMUM"]
22
+ env["SIMPLECOV_BRANCH"] = ENV["SIMPLECOV_BRANCH"] if ENV["SIMPLECOV_BRANCH"]
23
+
24
+ success = system(env, ruby, "-S", "bundle", "exec", "rspec")
25
+ abort("coverage run failed") unless success
26
+ end
27
+ end
28
+
29
+ task default: :spec
30
+
31
+ namespace :bench do
32
+ def run_benchmark_script(path)
33
+ ruby = RbConfig.ruby
34
+ success = system(ruby, File.expand_path(path, __dir__))
35
+ abort("benchmark failed: #{path}") unless success
36
+ end
37
+
38
+ desc "Run WAV I/O benchmark"
39
+ task :wav_io do
40
+ run_benchmark_script("benchmarks/wav_io_benchmark.rb")
41
+ end
42
+
43
+ desc "Run DSP effects benchmark"
44
+ task :dsp do
45
+ run_benchmark_script("benchmarks/dsp_effects_benchmark.rb")
46
+ end
47
+
48
+ desc "Run FLAC encode/decode benchmark"
49
+ task :flac do
50
+ run_benchmark_script("benchmarks/flac_benchmark.rb")
51
+ end
52
+
53
+ desc "Run streaming memory benchmark"
54
+ task :stream do
55
+ run_benchmark_script("benchmarks/streaming_memory_benchmark.rb")
56
+ end
57
+
58
+ desc "Run all benchmarks"
59
+ task all: %i[wav_io dsp flac stream]
60
+ end
61
+
62
+ namespace :docs do
63
+ def example_scripts
64
+ %w[
65
+ examples/format_convert.rb
66
+ examples/drum_machine.rb
67
+ examples/synth_pad.rb
68
+ examples/audio_processing.rb
69
+ examples/hybrid_arrangement.rb
70
+ examples/streaming_master_chain.rb
71
+ examples/cinematic_transition.rb
72
+ ].freeze
73
+ end
74
+
75
+ def run_ruby_script(path)
76
+ ruby = RbConfig.ruby
77
+ success = system(ruby, File.expand_path(path, __dir__))
78
+ abort("script failed: #{path}") unless success
79
+ end
80
+
81
+ desc "Generate YARD docs into doc/"
82
+ task :yard do
83
+ ruby = RbConfig.ruby
84
+ success = system(ruby, "-S", "bundle", "exec", "yard", "doc")
85
+ abort("yard doc failed") unless success
86
+ end
87
+
88
+ desc "Print YARD documentation coverage stats"
89
+ task :stats do
90
+ ruby = RbConfig.ruby
91
+ success = system(ruby, "-S", "bundle", "exec", "yard", "stats")
92
+ abort("yard stats failed") unless success
93
+ end
94
+
95
+ desc "Enforce minimum YARD documentation percentage (YARD_MINIMUM, default: 85)"
96
+ task :check do
97
+ ruby = RbConfig.ruby
98
+ minimum = ENV.fetch("YARD_MINIMUM", "85").to_f
99
+ output = IO.popen([ruby, "-S", "bundle", "exec", "yard", "stats"], chdir: __dir__, err: %i[child out], &:read)
100
+ puts output
101
+
102
+ match = output.match(/([0-9]+\.[0-9]+)% documented/)
103
+ abort("yard stats output did not include documentation percentage") unless match
104
+
105
+ percent = match[1].to_f
106
+ abort("documentation coverage #{percent}% is below minimum #{minimum}%") if percent < minimum
107
+
108
+ puts "docs check ok: #{percent}% >= #{minimum}%"
109
+ end
110
+
111
+ desc "Smoke-run all example scripts (self-contained demo mode)"
112
+ task :examples do
113
+ example_scripts.each do |script|
114
+ puts "== running #{script}"
115
+ run_ruby_script(script)
116
+ end
117
+ end
118
+
119
+ desc "Run docs-related checks and generators"
120
+ task all: %i[examples yard check]
121
+ end
122
+
123
+ namespace :release do
124
+ def load_release_spec!
125
+ spec = Gem::Specification.load(File.expand_path("wavify.gemspec", __dir__))
126
+ abort("failed to load wavify.gemspec") unless spec
127
+
128
+ spec
129
+ end
130
+
131
+ def assert_release_check!(condition, message)
132
+ return if condition
133
+
134
+ abort("release check failed: #{message}")
135
+ end
136
+
137
+ desc "Validate gemspec metadata and packaged file list"
138
+ task :check_gemspec do
139
+ spec = load_release_spec!
140
+
141
+ assert_release_check!(!spec.name.to_s.strip.empty?, "gemspec.name is empty")
142
+ assert_release_check!(!spec.version.to_s.strip.empty?, "gemspec.version is empty")
143
+ assert_release_check!(!spec.summary.to_s.strip.empty?, "gemspec.summary is empty")
144
+ assert_release_check!(!spec.description.to_s.strip.empty?, "gemspec.description is empty")
145
+ assert_release_check!(!spec.homepage.to_s.strip.empty?, "gemspec.homepage is empty")
146
+ assert_release_check!(!spec.license.to_s.strip.empty?, "gemspec.license is empty")
147
+ assert_release_check!(!spec.required_ruby_version.to_s.strip.empty?, "gemspec.required_ruby_version is empty")
148
+
149
+ required_metadata_keys = %w[allowed_push_host homepage_uri source_code_uri changelog_uri]
150
+ missing_metadata = required_metadata_keys.select { |key| spec.metadata[key].to_s.strip == "" }
151
+ assert_release_check!(missing_metadata.empty?, "missing gemspec metadata keys: #{missing_metadata.join(', ')}")
152
+
153
+ files = spec.files || []
154
+ assert_release_check!(files.include?("lib/wavify.rb"), "gemspec.files does not include lib/wavify.rb")
155
+ assert_release_check!(files.include?("README.md"), "gemspec.files does not include README.md")
156
+ assert_release_check!(files.any? { |f| ["LICENSE", "LICENSE.txt"].include?(f) }, "gemspec.files does not include license file")
157
+ assert_release_check!(files.none? { |f| f.start_with?(".idea/") }, ".idea files should not be packaged")
158
+
159
+ puts "release check ok: gemspec metadata and package file list"
160
+ puts " name: #{spec.name}"
161
+ puts " version: #{spec.version}"
162
+ puts " packaged files: #{files.length}"
163
+ end
164
+
165
+ desc "Validate CHANGELOG structure and unreleased section"
166
+ task :check_changelog do
167
+ changelog_path = File.expand_path("CHANGELOG.md", __dir__)
168
+ assert_release_check!(File.file?(changelog_path), "CHANGELOG.md is missing")
169
+
170
+ changelog = File.read(changelog_path)
171
+ assert_release_check!(changelog.include?("## [Unreleased]"), "CHANGELOG.md must include an [Unreleased] section")
172
+ assert_release_check!(changelog.include?("Keep a Changelog"), "CHANGELOG.md should mention Keep a Changelog format")
173
+ has_standard_subsection = changelog.match?(/^### (Added|Changed|Fixed|Removed|Security)$/)
174
+ assert_release_check!(
175
+ has_standard_subsection,
176
+ "CHANGELOG.md should include at least one subsection heading"
177
+ )
178
+
179
+ puts "release check ok: CHANGELOG.md structure"
180
+ end
181
+
182
+ desc "Build gem package locally (same artifact as release flow)"
183
+ task :build_package do
184
+ Rake::Task["build"].reenable
185
+ Rake::Task["build"].invoke
186
+ end
187
+
188
+ desc "Run release readiness checks (changelog, gemspec, gem build)"
189
+ task check: %i[check_changelog check_gemspec build_package]
190
+ end
@@ -0,0 +1,46 @@
1
+ # Benchmarks
2
+
3
+ This directory contains lightweight benchmark scripts for Wavify's core workflows.
4
+
5
+ Implemented targets (incremental Phase 6 work):
6
+
7
+ - `wav_io_benchmark.rb` - WAV read/write and streaming pipeline throughput
8
+ - `dsp_effects_benchmark.rb` - DSP effects processing cost
9
+ - `flac_benchmark.rb` - FLAC encode/decode and streaming throughput
10
+ - `streaming_memory_benchmark.rb` - streaming pipeline memory behavior (sampled RSS)
11
+
12
+ ## Usage
13
+
14
+ Run directly:
15
+
16
+ ```bash
17
+ ruby benchmarks/wav_io_benchmark.rb
18
+ ruby benchmarks/dsp_effects_benchmark.rb
19
+ ruby benchmarks/flac_benchmark.rb
20
+ ruby benchmarks/streaming_memory_benchmark.rb
21
+ ```
22
+
23
+ Or via rake tasks:
24
+
25
+ ```bash
26
+ rake bench:wav_io
27
+ rake bench:dsp
28
+ rake bench:flac
29
+ rake bench:stream
30
+ rake bench:all
31
+ ```
32
+
33
+ ## Environment Variables
34
+
35
+ - `KEEP_BENCH_FILES=1` keeps generated files in `tmp/benchmarks/`
36
+ - `WAV_IO_ITERATIONS`, `WAV_IO_DURATION`, `WAV_IO_CHUNK`
37
+ - `DSP_BENCH_ITERATIONS`, `DSP_BENCH_DURATION`
38
+ - `FLAC_BENCH_ITERATIONS`, `FLAC_BENCH_DURATION`, `FLAC_BENCH_CHUNK`
39
+ - `STREAM_BENCH_DURATION`, `STREAM_BENCH_CHUNK`
40
+
41
+ ## Notes
42
+
43
+ - OGG Vorbis audio decode is still incomplete, so it is not benchmarked yet.
44
+ - `flac_benchmark.rb` measures the current pure Ruby FLAC implementation (verbatim/fixed subframe selection).
45
+ - `streaming_memory_benchmark.rb` reports sampled RSS (`ps`) as an approximation, not a strict peak profiler.
46
+ - `wav_io_benchmark.rb` optionally compares WAV read/write throughput with the `wavefile` gem when it is installed (`gem install wavefile`).