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.
- checksums.yaml +7 -0
- data/.serena/.gitignore +1 -0
- data/.serena/memories/project_overview.md +5 -0
- data/.serena/memories/style_and_completion.md +5 -0
- data/.serena/memories/suggested_commands.md +11 -0
- data/.serena/project.yml +126 -0
- data/.simplecov +18 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +11 -0
- data/LICENSE +21 -0
- data/README.md +196 -0
- data/Rakefile +190 -0
- data/benchmarks/README.md +46 -0
- data/benchmarks/benchmark_helper.rb +112 -0
- data/benchmarks/dsp_effects_benchmark.rb +46 -0
- data/benchmarks/flac_benchmark.rb +74 -0
- data/benchmarks/streaming_memory_benchmark.rb +94 -0
- data/benchmarks/wav_io_benchmark.rb +110 -0
- data/examples/audio_processing.rb +73 -0
- data/examples/cinematic_transition.rb +118 -0
- data/examples/drum_machine.rb +74 -0
- data/examples/format_convert.rb +81 -0
- data/examples/hybrid_arrangement.rb +165 -0
- data/examples/streaming_master_chain.rb +129 -0
- data/examples/synth_pad.rb +42 -0
- data/lib/wavify/audio.rb +483 -0
- data/lib/wavify/codecs/aiff.rb +338 -0
- data/lib/wavify/codecs/base.rb +108 -0
- data/lib/wavify/codecs/flac.rb +1322 -0
- data/lib/wavify/codecs/ogg_vorbis.rb +1447 -0
- data/lib/wavify/codecs/raw.rb +193 -0
- data/lib/wavify/codecs/registry.rb +87 -0
- data/lib/wavify/codecs/wav.rb +459 -0
- data/lib/wavify/core/duration.rb +99 -0
- data/lib/wavify/core/format.rb +133 -0
- data/lib/wavify/core/sample_buffer.rb +216 -0
- data/lib/wavify/core/stream.rb +129 -0
- data/lib/wavify/dsl.rb +537 -0
- data/lib/wavify/dsp/effects/chorus.rb +98 -0
- data/lib/wavify/dsp/effects/compressor.rb +85 -0
- data/lib/wavify/dsp/effects/delay.rb +69 -0
- data/lib/wavify/dsp/effects/distortion.rb +64 -0
- data/lib/wavify/dsp/effects/effect_base.rb +68 -0
- data/lib/wavify/dsp/effects/reverb.rb +112 -0
- data/lib/wavify/dsp/effects.rb +21 -0
- data/lib/wavify/dsp/envelope.rb +97 -0
- data/lib/wavify/dsp/filter.rb +271 -0
- data/lib/wavify/dsp/oscillator.rb +123 -0
- data/lib/wavify/errors.rb +34 -0
- data/lib/wavify/sequencer/engine.rb +278 -0
- data/lib/wavify/sequencer/note_sequence.rb +132 -0
- data/lib/wavify/sequencer/pattern.rb +102 -0
- data/lib/wavify/sequencer/track.rb +298 -0
- data/lib/wavify/sequencer.rb +12 -0
- data/lib/wavify/version.rb +6 -0
- data/lib/wavify.rb +28 -0
- data/tools/fixture_writer.rb +85 -0
- 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
|
data/.serena/.gitignore
ADDED
|
@@ -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`
|
data/.serena/project.yml
ADDED
|
@@ -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
data/CHANGELOG.md
ADDED
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`).
|