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
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.1.0-blue)](https://pypi.org/project/mksinewave)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.1.0-blue)](https://pypi.org/project/mksinewave)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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,4 @@
1
+ from mksinewave.mksinewave import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -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
+ ]