mksinewave 1.0.0__tar.gz
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.
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mksinewave
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Simple command-line tool for creating audio files containing pure sine wave tones
|
|
5
|
+
Author: DJ Stomp
|
|
6
|
+
Author-email: 85457381+DJStompZone@users.noreply.github.com
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: numpy (>=2.5.3,<3.0.0)
|
|
14
|
+
Requires-Dist: pytest (>=9.1.1,<10.0.0) ; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-cov (>=7.1.0,<8.0.0) ; extra == "dev"
|
|
16
|
+
Requires-Dist: soundfile (>=0.14.0,<0.15.0)
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# mksinewave
|
|
20
|
+
|
|
21
|
+
`mksinewave` is a Python command-line utility for generating pure sine wave audio tones with customizable configurations, smooth attenuation fades, and automatic audio container inference.
|
|
22
|
+
|
|
23
|
+
[](https://pypi.org/project/mksinewave)
|
|
24
|
+
[](https://opensource.org/licenses/MIT)
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Flexible Frequency Formats:** Accepts standard numbers (`440`), human-friendly shortcuts (`1.4k'), or explicit units (`22kHz`).
|
|
29
|
+
- **Smart Fades:** Prevent harsh audio pops and clicks with customizable linear head and tail attenuation (fade-in/fade-out).
|
|
30
|
+
- **FFmpeg-Style Extension Inference:** Saves directly to `.wav`, `.flac`, `.ogg`, or `.aiff` by automatically detecting the format from your output filename.
|
|
31
|
+
- **Safety Proofed:** Includes built-in human hearing range guards (20 Hz - 22 kHz) and Nyquist frequency anti-aliasing checks.
|
|
32
|
+
- **Safe Overwriting:** Protects existing files unless exlplicitly overridden with the `-f / --force` flag.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Clone the repository and install it locally using `pip`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install mksinewave
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For active local development, install using the `[dev]` optional dependency group in editable mode:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install -e "mksinewave[dev]"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
Once installed, the global `mksinewave` command is available anywhere on your system.
|
|
51
|
+
|
|
52
|
+
### Basic Syntax
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
mksinewave <frequency> <duration> [options]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Examples
|
|
59
|
+
|
|
60
|
+
**1. Generate a standard A4 tone (440Hz) for 3 seconds:**
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
mksinewave 440 3
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**2. Generate a 1.5 khz tone for 5 seconds as a compressed FLAC file with a smooth fade-in and fade-out:**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
mosinewave 1.5k 5 --fade-sin 0.5 --fade-out 1.0 -o output.flac -v
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**3. Force overwrite an existing file using a specific sample rate:**
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
mksinewave "2.2 kHz" 2.5 -s 48000 -o alert.ogg -f
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Available Command-Line Flags
|
|
79
|
+
|
|
80
|
+
- --fade-in: Length of head attenuation fade-in in seconds (Default: 0.0).
|
|
81
|
+
- --fade-out: Length of tail attenuation fade-out in" seconds (Default: 0.0).
|
|
82
|
+
- -s, --sample-rate: Audio sampling rate in Hz (Default: 44100).
|
|
83
|
+
- -o, --output: Output filepath inferred via extension (Default: sine_wave.wav).
|
|
84
|
+
- -f, --force: Overwrite the target file if it already exists.
|
|
85
|
+
- -v, --verbose: Enable logging output detailing the generation process.
|
|
86
|
+
- frequency (Positional): Target frequency. Supports formats like 440, 1.4k, 22kHz.
|
|
87
|
+
- duration (Positional): Total length of the audio track in seconds.
|
|
88
|
+
|
|
89
|
+
## Development & Testing
|
|
90
|
+
|
|
91
|
+
This project maintains rigorus test coverage using `pytest` and `pytest-cov`.
|
|
92
|
+
|
|
93
|
+
If you installed the development dependencies, you can execute the test suite globally from the project root directory:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
test-mksinewave
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
This project is licensed under the MIT License.
|
|
102
|
+
See the [LICENSE](LICENSE) file for details.
|
|
103
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# mksinewave
|
|
2
|
+
|
|
3
|
+
`mksinewave` is a Python command-line utility for generating pure sine wave audio tones with customizable configurations, smooth attenuation fades, and automatic audio container inference.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/mksinewave)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Flexible Frequency Formats:** Accepts standard numbers (`440`), human-friendly shortcuts (`1.4k'), or explicit units (`22kHz`).
|
|
11
|
+
- **Smart Fades:** Prevent harsh audio pops and clicks with customizable linear head and tail attenuation (fade-in/fade-out).
|
|
12
|
+
- **FFmpeg-Style Extension Inference:** Saves directly to `.wav`, `.flac`, `.ogg`, or `.aiff` by automatically detecting the format from your output filename.
|
|
13
|
+
- **Safety Proofed:** Includes built-in human hearing range guards (20 Hz - 22 kHz) and Nyquist frequency anti-aliasing checks.
|
|
14
|
+
- **Safe Overwriting:** Protects existing files unless exlplicitly overridden with the `-f / --force` flag.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Clone the repository and install it locally using `pip`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install mksinewave
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For active local development, install using the `[dev]` optional dependency group in editable mode:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install -e "mksinewave[dev]"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
Once installed, the global `mksinewave` command is available anywhere on your system.
|
|
33
|
+
|
|
34
|
+
### Basic Syntax
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
mksinewave <frequency> <duration> [options]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Examples
|
|
41
|
+
|
|
42
|
+
**1. Generate a standard A4 tone (440Hz) for 3 seconds:**
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
mksinewave 440 3
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**2. Generate a 1.5 khz tone for 5 seconds as a compressed FLAC file with a smooth fade-in and fade-out:**
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
mosinewave 1.5k 5 --fade-sin 0.5 --fade-out 1.0 -o output.flac -v
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**3. Force overwrite an existing file using a specific sample rate:**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
mksinewave "2.2 kHz" 2.5 -s 48000 -o alert.ogg -f
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Available Command-Line Flags
|
|
61
|
+
|
|
62
|
+
- --fade-in: Length of head attenuation fade-in in seconds (Default: 0.0).
|
|
63
|
+
- --fade-out: Length of tail attenuation fade-out in" seconds (Default: 0.0).
|
|
64
|
+
- -s, --sample-rate: Audio sampling rate in Hz (Default: 44100).
|
|
65
|
+
- -o, --output: Output filepath inferred via extension (Default: sine_wave.wav).
|
|
66
|
+
- -f, --force: Overwrite the target file if it already exists.
|
|
67
|
+
- -v, --verbose: Enable logging output detailing the generation process.
|
|
68
|
+
- frequency (Positional): Target frequency. Supports formats like 440, 1.4k, 22kHz.
|
|
69
|
+
- duration (Positional): Total length of the audio track in seconds.
|
|
70
|
+
|
|
71
|
+
## Development & Testing
|
|
72
|
+
|
|
73
|
+
This project maintains rigorus test coverage using `pytest` and `pytest-cov`.
|
|
74
|
+
|
|
75
|
+
If you installed the development dependencies, you can execute the test suite globally from the project root directory:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
test-mksinewave
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
This project is licensed under the MIT License.
|
|
84
|
+
See the [LICENSE](LICENSE) file for details.
|
|
File without changes
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import soundfile as sf
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def parse_frequency(value_str):
|
|
10
|
+
"""Custom argparse type parser to handle strings like 440, 1.4k, 22kHz, etc."""
|
|
11
|
+
s = value_str.strip().lower()
|
|
12
|
+
|
|
13
|
+
match = re.match(r'^(\d*\.?\d+)\s*(k)?(?:hz)?$', s)
|
|
14
|
+
if not match:
|
|
15
|
+
raise argparse.ArgumentTypeError(
|
|
16
|
+
f"Invalid frequency format: '{value_str}'. Use numbers like '440', '1.4k', or '22kHz'."
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
number_part, k_modifier = match.groups()
|
|
20
|
+
freq = float(number_part)
|
|
21
|
+
|
|
22
|
+
if k_modifier:
|
|
23
|
+
freq *= 1000
|
|
24
|
+
|
|
25
|
+
return freq
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def generate_sine_wave(frequency, duration, sample_rate, fade_in, fade_out, output_file, force, verbose):
|
|
29
|
+
if os.path.exists(output_file) and not force:
|
|
30
|
+
print(f"Error: File '{output_file}' already exists. Use -f or --force to overwrite it.")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
if not (20.0 <= frequency <= 22000.0):
|
|
34
|
+
print(f"Error: Frequency ({frequency:g} Hz) out of bounds. Must be between 20 Hz and 22,000 Hz.")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
if frequency >= (sample_rate / 2):
|
|
38
|
+
print(f"Error: Frequency ({frequency:g} Hz) must be less than half the sample rate ({sample_rate / 2} Hz) to avoid aliasing.")
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
if verbose:
|
|
42
|
+
print(f"Configuring audio: {frequency:g}Hz for {duration}s at {sample_rate}Hz sample rate.")
|
|
43
|
+
if fade_in > 0 or fade_out > 0:
|
|
44
|
+
print(f"Applying fades: Fade-in = {fade_in}s, Fade-out = {fade_out}s")
|
|
45
|
+
|
|
46
|
+
total_samples = int(sample_rate * duration)
|
|
47
|
+
t = np.linspace(0, duration, total_samples, endpoint=False)
|
|
48
|
+
|
|
49
|
+
audio = np.sin(2 * np.pi * frequency * t)
|
|
50
|
+
|
|
51
|
+
envelope = np.ones(total_samples)
|
|
52
|
+
|
|
53
|
+
if fade_in > 0:
|
|
54
|
+
fade_in_samples = min(int(sample_rate * fade_in), total_samples)
|
|
55
|
+
envelope[:fade_in_samples] = np.linspace(0.0, 1.0, fade_in_samples)
|
|
56
|
+
|
|
57
|
+
if fade_out > 0:
|
|
58
|
+
fade_out_samples = min(int(sample_rate * fade_out), total_samples)
|
|
59
|
+
envelope[-fade_out_samples:] = np.linspace(1.0, 0.0, fade_out_samples)
|
|
60
|
+
|
|
61
|
+
audio_signals = audio * envelope
|
|
62
|
+
if verbose:
|
|
63
|
+
print(f"Writing data to '{output_file}' (inferring container format from extension)...")
|
|
64
|
+
try:
|
|
65
|
+
sf.write(output_file, audio_signals, sample_rate)
|
|
66
|
+
print(f"Success: Saved '{output_file}'")
|
|
67
|
+
except ValueError as e:
|
|
68
|
+
print(f"Error: Could not infer format or write file. {e}")
|
|
69
|
+
print("Supported extensions include: .wav, .flac, .ogg, .aiff")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_args() -> argparse.Namespace:
|
|
73
|
+
parser = argparse.ArgumentParser(description="Generate a sine wave audio file with flexible frequency inputs.")
|
|
74
|
+
parser.add_argument("frequency", type=parse_frequency, help="Frequency (e.g., 440, 1.4k, 22kHz)")
|
|
75
|
+
parser.add_argument("duration", type=float, help="Total duration of the audio in seconds")
|
|
76
|
+
parser.add_argument("--fade-in", type=float, default=0.0, help="Fade-in duration in seconds (default: 0.0)")
|
|
77
|
+
parser.add_argument("--fade-out", type=float, default=0.0, help="Fade-out duration in seconds (default: 0.0)")
|
|
78
|
+
parser.add_argument("-s", "--sample-rate", type=int, default=44100, help="Sample rate in Hz (default: 44100)")
|
|
79
|
+
parser.add_argument("-o", "--output", type=str, default="sine_wave.wav", help="Output filename with extension (default: sine_wave.wav)")
|
|
80
|
+
|
|
81
|
+
parser.add_argument("-f", "--force", action="store_true", help="Overwrite the output file if it already exists")
|
|
82
|
+
parser.add_argument("-v", "--verbose", action="store_true", help="Increase output verbosity")
|
|
83
|
+
|
|
84
|
+
args = parser.parse_args()
|
|
85
|
+
|
|
86
|
+
if args.fade_in + args.fade_out > args.duration:
|
|
87
|
+
parser.error("Combined fade-in and fade-out durations cannot exceed the total file duration.")
|
|
88
|
+
|
|
89
|
+
return args
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def main():
|
|
93
|
+
args = parse_args()
|
|
94
|
+
|
|
95
|
+
generate_sine_wave(
|
|
96
|
+
frequency=args.frequency,
|
|
97
|
+
duration=args.duration,
|
|
98
|
+
sample_rate=args.sample_rate,
|
|
99
|
+
fade_in=args.fade_in,
|
|
100
|
+
fade_out=args.fade_out,
|
|
101
|
+
output_file=args.output,
|
|
102
|
+
force=args.force,
|
|
103
|
+
verbose=args.verbose
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
main()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mksinewave"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Simple command-line tool for creating audio files containing pure sine wave tones"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "DJ Stomp",email = "85457381+DJStompZone@users.noreply.github.com"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.12,<4.0"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"numpy (>=2.5.3,<3.0.0)",
|
|
12
|
+
"soundfile (>=0.14.0,<0.15.0)"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[dependency-groups]
|
|
16
|
+
dev = [
|
|
17
|
+
"pytest (>=9.1.1,<10.0.0)",
|
|
18
|
+
"pytest-cov (>=7.1.0,<8.0.0)"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
dev = [
|
|
23
|
+
"pytest (>=9.1.1,<10.0.0)",
|
|
24
|
+
"pytest-cov (>=7.1.0,<8.0.0)"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[tool.poetry]
|
|
28
|
+
packages = [{include = "mksinewave"}]
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
32
|
+
build-backend = "poetry.core.masonry.api"
|
|
33
|
+
|
|
34
|
+
[tool.poetry.scripts]
|
|
35
|
+
mksinewave = "mksinewave.mksinewave:main"
|
|
36
|
+
test-mksinewave = "pytest:main"
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
addopts = "--cov=mksinewave --cov-report=term-missing --cov-report=html"
|
|
40
|
+
testpaths = ["tests"]
|
|
41
|
+
|
|
42
|
+
[tool.coverage.run]
|
|
43
|
+
source = ["mksinewave"]
|
|
44
|
+
|
|
45
|
+
[tool.coverage.report]
|
|
46
|
+
fail_under = 95
|
|
47
|
+
exclude_lines = [
|
|
48
|
+
"pragma: no cover",
|
|
49
|
+
"if __name__ == .__main__.:",
|
|
50
|
+
"pass"
|
|
51
|
+
]
|