awaaz 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e2aa5ae798de6f2b722134913cc7b055f53b4b035989e94521be55d1fe76acc7
4
+ data.tar.gz: b54beb3ae4fc90ab2a4c1485ffa91eccb1b53cafca27c04e7ece14263b65abb4
5
+ SHA512:
6
+ metadata.gz: b0c79b3dbf5396de690ee17868cb8a0d2d29dfe396b8c5dd9c9a098393a40d15715d4def9990990024ad356f9463cb28feab981e96f59e4961d978e373a104ce
7
+ data.tar.gz: d28e0001af9a5b8052298f33a5dbf72ca16325d15e3762187fd7a022b6140874729f52d71d32a0e39a589b712471e248fd28dd6e73dea35dd3fa26742a22ef2b
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.4
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ EnforcedStyle: double_quotes
10
+
11
+ Metrics/MethodLength:
12
+ Max: 20
13
+
14
+
15
+
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-07-21
4
+
5
+ - Initial release
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official email address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ [INSERT CONTACT METHOD].
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series of
86
+ actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or permanent
93
+ ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within the
113
+ community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.1, available at
119
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
+
121
+ Community Impact Guidelines were inspired by
122
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
+
124
+ For answers to common questions about this code of conduct, see the FAQ at
125
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
+ [https://www.contributor-covenant.org/translations][translations].
127
+
128
+ [homepage]: https://www.contributor-covenant.org
129
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
+ [Mozilla CoC]: https://github.com/mozilla/diversity
131
+ [FAQ]: https://www.contributor-covenant.org/faq
132
+ [translations]: https://www.contributor-covenant.org/translations
data/GLOSSARY.md ADDED
@@ -0,0 +1,3 @@
1
+ # Terms and Definitions for Audio Processing
2
+
3
+ - **PCM (Pulse Code Modulation):** A method to convert analog audio signals into digital form by sampling the signal's ampllitude at regular intervals.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Saad Azam
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,72 @@
1
+ # Awaaz
2
+
3
+ Awaaz is a Ruby gem for working with audio, from decoding to analysis, making it easier to process and understand sound in your projects.
4
+
5
+ ## Requirements
6
+
7
+ Awaaz can decode audio in two ways:
8
+
9
+ #### 1. Shell-based decoding
10
+ You can install **any one** of the following:
11
+
12
+ - [`ffmpeg`](https://github.com/FFmpeg/FFmpeg) – supports most formats, including MP3.
13
+ - [`sox`](https://github.com/chirlu/sox) – also supports most formats, including MP3.
14
+ - [`mpg123`](https://github.com/madebr/mpg123) – **MP3 only**.
15
+
16
+ #### 2. Library-based decoding and resampling
17
+ - [`libsndfile`](https://github.com/libsndfile/libsndfile) – reads audio files (but **cannot** read MP3 files).
18
+ - [`libsamplerate`](https://github.com/libsndfile/libsamplerate) – resamples audio samples when using `libsndfile`.
19
+
20
+ ⚠ **Important**:
21
+ - If you need MP3 support with the library-based method, you **must also** install one of:
22
+ - `ffmpeg`
23
+ - `sox`
24
+ - `mpg123`
25
+
26
+ #### Additional Requirement
27
+ - The Ruby gem [`numo-narray`](https://github.com/ruby-numo/numo-narray) is required for numerical array operations.
28
+
29
+ ### Installation Examples
30
+
31
+ - **Just ffmpeg** → works for all formats (no `libsndfile` or `libsamplerate` needed).
32
+ - **Just sox** → works for all formats (no `libsndfile` or `libsamplerate` needed).
33
+ - **libsndfile + libsamplerate** → works for non-MP3 formats. For MP3, add `ffmpeg`, `sox`, or `mpg123`.
34
+ - **Everything installed** → maximum flexibility.
35
+
36
+ ## Installation
37
+
38
+ Install the gem and add to the application's Gemfile by executing:
39
+
40
+ ```bash
41
+ bundle add awaaz
42
+ ```
43
+
44
+ If bundler is not being used to manage dependencies, install the gem by executing:
45
+
46
+ ```bash
47
+ gem install awaaz
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ```ruby
53
+ # To decode the audio file
54
+ samples, sample_rate = Awaaz.load("path/to/audio_file")
55
+
56
+ # To decode the audio file using specified decoder
57
+ samples, sample_rate = Awaaz.load("path/to/audio_file", decoder: :sox)
58
+ ```
59
+
60
+ ## Development
61
+
62
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
63
+
64
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
65
+
66
+ ## Contributing
67
+
68
+ Bug reports and pull requests are welcome on GitHub at [Awaaz](https://github.com/SadMadLad/awaaz). This project is intended to be a safe, welcoming space for collaboration, and contributors.
69
+
70
+ ## License
71
+
72
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: :rubocop
data/TODOS.md ADDED
@@ -0,0 +1,3 @@
1
+ - Lazy decoding of an audio
2
+ - `libsndfile` support
3
+ - Streaming output of larger files
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # The Awaaz module serves as the top-level namespace for all components
5
+ # of the Awaaz gem, which provides audio decoding, resampling, and analysis tools.
6
+ #
7
+ module Awaaz
8
+ ##
9
+ # The Config class handles detection and configuration of available audio decoders
10
+ # for the Awaaz gem. It checks the system for supported decoder binaries
11
+ # (mpg123, ffmpeg, sox) and provides query helpers to check their availability.
12
+ #
13
+ # @example Check if `ffmpeg` is available
14
+ # Awaaz.config.ffmpeg? # => true or false
15
+ #
16
+ class Config
17
+ ##
18
+ # Creates a new configuration instance and detects available decoders.
19
+ #
20
+ def initialize
21
+ @available_decoders = detect_decoders
22
+ end
23
+
24
+ ##
25
+ # Checks if a given decoder is available on the system.
26
+ #
27
+ # @param name [Symbol, String] The name of the decoder (e.g., `:mpg123`, `"ffmpeg"`).
28
+ # @return [Boolean] `true` if the decoder is available, otherwise `false`.
29
+ #
30
+ def decoder?(name)
31
+ @available_decoders.include?(name.to_sym)
32
+ end
33
+
34
+ ##
35
+ # Checks if mpg123 is available.
36
+ #
37
+ # @return [Boolean] `true` if mpg123 is installed, otherwise `false`.
38
+ #
39
+ def mpg123?
40
+ decoder?(:mpg123)
41
+ end
42
+
43
+ ##
44
+ # Checks if ffmpeg is available.
45
+ #
46
+ # @return [Boolean] `true` if ffmpeg is installed, otherwise `false`.
47
+ #
48
+ def ffmpeg?
49
+ decoder?(:ffmpeg)
50
+ end
51
+
52
+ ##
53
+ # Checks if sox is available.
54
+ #
55
+ # @return [Boolean] `true` if sox is installed, otherwise `false`.
56
+ #
57
+ def sox?
58
+ decoder?(:sox)
59
+ end
60
+
61
+ ##
62
+ # Lists all potential decoders that Awaaz can work with.
63
+ #
64
+ # @return [Array<Symbol>] An array of decoder names (`:mpg123`, `:ffmpeg`, `:sox`).
65
+ #
66
+ def potential_decoders
67
+ %i[mpg123 ffmpeg sox]
68
+ end
69
+
70
+ ##
71
+ # Checks if no decoders are available on the system.
72
+ #
73
+ # @return [Boolean] `true` if no decoders were detected, otherwise `false`.
74
+ #
75
+ def no_decoders?
76
+ @available_decoders.nil? || @available_decoders.empty?
77
+ end
78
+
79
+ private
80
+
81
+ ##
82
+ # Detects which decoders are available by checking system binaries.
83
+ #
84
+ # @return [Array<Symbol>] An array of detected decoder names.
85
+ #
86
+ def detect_decoders
87
+ decoders = []
88
+ decoders << :mpg123 if system("which mpg123 > /dev/null 2>&1")
89
+ decoders << :ffmpeg if system("which ffmpeg > /dev/null 2>&1")
90
+ decoders << :sox if system("which sox > /dev/null 2>&1")
91
+ decoders
92
+ end
93
+ end
94
+
95
+ class << self
96
+ ##
97
+ # Returns the current configuration object.
98
+ #
99
+ # @return [Awaaz::Config] The configuration instance.
100
+ #
101
+ def config
102
+ @config ||= Config.new
103
+ end
104
+
105
+ ##
106
+ # Yields the current configuration object for modifications.
107
+ #
108
+ # @yieldparam config [Awaaz::Config] The configuration instance.
109
+ #
110
+ def configure
111
+ yield(config)
112
+ end
113
+
114
+ ##
115
+ # Lists all potential decoders that Awaaz can work with.
116
+ #
117
+ # @return [Array<Symbol>] An array of decoder names.
118
+ #
119
+ def potential_decoders
120
+ config.potential_decoders
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awaaz
4
+ module Decoders
5
+ # Abstract base class for audio decoders in the Awaaz gem.
6
+ #
7
+ # Provides common configuration handling, option management, and helper
8
+ # methods for working with audio data. Subclasses are expected to implement
9
+ # the {#load} method to perform the actual decoding process.
10
+ #
11
+ # @abstract
12
+ class BaseDecoder
13
+ class << self
14
+ # @return [Array<Symbol>] The default set of options available to all decoders.
15
+ def default_available_options
16
+ %i[amplification_factor decoder sample_rate mono]
17
+ end
18
+
19
+ # Sets the list of available options for this decoder class.
20
+ #
21
+ # @param provided_available_options [Array<Symbol>] the list of available option keys.
22
+ # @return [void]
23
+ def set_available_options(provided_available_options = default_available_options)
24
+ @available_options = provided_available_options
25
+ end
26
+
27
+ # @return [Array<Symbol>] The currently available option keys for this decoder class.
28
+ attr_reader :available_options
29
+
30
+ # Loads audio from a given file using this decoder.
31
+ #
32
+ # @param filename [String] The path to the audio file to load.
33
+ # @return [Object] The decoded audio data.
34
+ #
35
+ # @see #initialize
36
+ def load(filename, ...)
37
+ new(filename, ...).load
38
+ end
39
+ end
40
+
41
+ set_available_options
42
+
43
+ # @param filename [String] Path to the audio file to decode.
44
+ def initialize(filename, **)
45
+ @filename = filename
46
+ @options = Utils::SoundConfig.new(available_options, **)
47
+ end
48
+
49
+ # Loads audio data.
50
+ #
51
+ # This method must be implemented by subclasses to perform
52
+ # the actual decoding of the file.
53
+ #
54
+ # @abstract
55
+ # @raise [NotImplementedError] if called on the base class.
56
+ def load
57
+ raise NotImplementedError
58
+ end
59
+
60
+ # @return [Array<Symbol>] The available options for this instance.
61
+ def available_options
62
+ self.class.available_options
63
+ end
64
+
65
+ protected
66
+
67
+ # Reads audio data from the file using {Utils::Soundread}.
68
+ #
69
+ # @return [Array<(Numo::SFloat, Integer, Integer)>]
70
+ # A tuple containing:
71
+ # - audio samples as a Numo::SFloat array
72
+ # - number of channels
73
+ # - sample rate
74
+ def soundread
75
+ Utils::Soundread.new(@filename).read
76
+ end
77
+
78
+ # Processes the decoded audio samples by reshaping and optionally converting to mono.
79
+ #
80
+ # @param input_samples [Numo::DFloat] The raw decoded samples.
81
+ # @param channels [Integer] Number of channels in the input.
82
+ # @param sample_rate [Integer] The sample rate of the input.
83
+ # @return [Array<(Numo::DFloat, Integer)>] Processed samples and the sample rate.
84
+ def process(input_samples, channels, sample_rate)
85
+ input_samples = input_samples.reshape(channels, input_samples.size / channels)
86
+ input_samples = input_samples.mean(0) if mono?
87
+
88
+ [input_samples, sample_rate]
89
+ end
90
+
91
+ # Validates that the file extension matches the expected extension.
92
+ #
93
+ # @param file_extension [String] Expected file extension (e.g., ".mp3").
94
+ # @raise [ArgumentError] if the file extension does not match.
95
+ def validate_file_extension(file_extension)
96
+ raise ArgumentError, "Not a #{file_extension} file" unless File.extname(@filename) == file_extension
97
+ end
98
+
99
+ # @return [Awaaz::Config] The global Awaaz configuration.
100
+ def config = Awaaz.config
101
+
102
+ # Delegates config methods to the {Awaaz::Config} instance.
103
+ %i[no_decoders? potential_decoders].each do |config_method|
104
+ define_method(config_method) { config.public_send(config_method) }
105
+ end
106
+
107
+ # Delegates option accessors to the {Utils::SoundConfig} instance.
108
+ %i[
109
+ sample_rate num_channels decoder_option mono mono?
110
+ stereo? amplification_factor soundread?
111
+ ].each do |option_key|
112
+ define_method(option_key) { @options.public_send(option_key) }
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "filemagic"
4
+
5
+ # The Awaaz gem provides audio decoding utilities and related tools for working
6
+ # with various audio formats. It uses FFI bindings and Numo::NArray for numerical
7
+ # processing and includes multiple decoders, utilities, and configuration options.
8
+ module Awaaz
9
+ # Mapping of MIME types to their respective decoder classes.
10
+ DECODER_MAP = {
11
+ "audio/wav" => Decoders::WavefileDecoder,
12
+ "audio/x-wav" => Decoders::WavefileDecoder,
13
+ "audio/wave" => Decoders::WavefileDecoder,
14
+ "audio/vnd.wave" => Decoders::WavefileDecoder,
15
+ "audio/mpeg" => Decoders::Mp3Decoder,
16
+ "audio/mp3" => Decoders::Mp3Decoder,
17
+ "audio/x-mpeg" => Decoders::Mp3Decoder,
18
+ "audio/x-mp3" => Decoders::Mp3Decoder
19
+ }.freeze
20
+
21
+ class << self
22
+ # Loads an audio file and processes it using the appropriate decoder
23
+ # based on the file's MIME type.
24
+ #
25
+ # @param filename [String] the path to the audio file
26
+ # @raise [ArgumentError] if the MIME type is not supported
27
+ # @return [Object] the result of decoding, as returned by the decoder class
28
+ def load(filename)
29
+ fm = FileMagic.new(FileMagic::MAGIC_MIME_TYPE)
30
+ mime_type = fm.file(filename)
31
+
32
+ unless DECODER_MAP.key?(mime_type)
33
+ raise ArgumentError,
34
+ "Cannot load the file. Available mime types: #{DECODER_MAP.keys.join(", ")}"
35
+ end
36
+
37
+ decoding_class = DECODER_MAP[mime_type]
38
+ decoding_class.load(filename)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file loads all available decoders for the Awaaz gem.
4
+ # It ensures that the base decoder and all specific decoder
5
+ # implementations (e.g., MP3, WAV) are required and ready
6
+ # for use under the {Awaaz::Decoders} namespace.
7
+ #
8
+ # @example Accessing a decoder
9
+ # Awaaz::Decoders::Mp3Decoder.new.load
10
+ #
11
+ # @see Awaaz::Decoders::BaseDecoder
12
+ # @see Awaaz::Decoders::Mp3Decoder
13
+ # @see Awaaz::Decoders::WavefileDecoder
14
+ #
15
+
16
+ require_relative "base_decoder"
17
+ require_relative "mp3_decoder"
18
+ require_relative "wavefile_decoder"
19
+ require_relative "decode"
20
+
21
+ module Awaaz
22
+ # Namespace for all audio decoder implementations.
23
+ #
24
+ # Each decoder is responsible for loading and processing
25
+ # a specific audio format into a format usable by the Awaaz system.
26
+ #
27
+ # @since 0.1.0
28
+ module Decoders
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awaaz
4
+ module Decoders
5
+ ##
6
+ # The Mp3Decoder class provides decoding functionality for `.mp3` files
7
+ # within the Awaaz gem.
8
+ #
9
+ # It inherits from {BaseDecoder} and uses the {Utils::ViaShell} mixin
10
+ # to perform decoding via shell commands (e.g., Sox).
11
+ #
12
+ # @example Basic usage
13
+ # decoder = Awaaz::Decoders::Mp3Decoder.new(file_path: "song.mp3")
14
+ # samples = decoder.load
15
+ #
16
+ # @see BaseDecoder
17
+ # @see Utils::ViaShell
18
+ #
19
+ class Mp3Decoder < BaseDecoder
20
+ include Utils::ViaShell
21
+
22
+ # Sets available options for this decoder (defined in BaseDecoder).
23
+ set_available_options
24
+
25
+ ##
26
+ # Loads and processes an MP3 file.
27
+ #
28
+ # This method:
29
+ # 1. Validates that the file has a `.mp3` extension.
30
+ # 2. Uses {Utils::ViaShell#shell_load} to load raw audio data.
31
+ # 3. Passes the loaded data to {BaseDecoder#process} for further handling.
32
+ #
33
+ # @raise [ArgumentError]
34
+ # If the file does not have a `.mp3` extension.
35
+ #
36
+ # @return [Object]
37
+ # The processed audio data (return type depends on BaseDecoder#process).
38
+ #
39
+ def load
40
+ process(*shell_load(sox_options: { raw: true }))
41
+ end
42
+ end
43
+ end
44
+ end