Source code for voxatlas.features.phonology.formant.slope
from voxatlas.features.base_extractor import BaseExtractor
from voxatlas.features.feature_output import TableFeatureOutput
from voxatlas.phonology.formant_utils import compute_vowel_slope
from voxatlas.registry.feature_registry import registry
[docs]
class FormantSlopeExtractor(BaseExtractor):
r"""
Extract the ``phonology.formant.slope`` feature within the VoxAtlas pipeline.
This public extractor defines the reusable API for computing ``phonology.formant.slope`` from VoxAtlas structured inputs. It consumes ``phoneme`` units and produces values aligned to ``phoneme`` units, making the extractor a stable pipeline node that can be cited independently of the surrounding execution machinery.
Algorithm
---------
The extractor derives vowel formant structure from aligned phoneme segments and then aggregates those measurements to the declared unit level.
1. Segment selection
Vowel-bearing phoneme spans are isolated from the aligned unit table, and the corresponding waveform segments are converted into short analysis frames.
2. Resonance estimation
Linear-predictive analysis or Parselmouth formant tracking is used to estimate :math:`F_1`, :math:`F_2`, and :math:`F_3` for each analysis frame.
3. Metric computation
The extractor estimates linear trajectory slope for each formant, :math:`\hat\beta_m`, by regressing formant value against normalized time.
4. Packaging
The resulting statistic is aligned to ``phoneme`` units for use in subsequent phonological or conversation-level analyses.
Notes
-----
This extractor declares the upstream dependencies ['phonology.formant.tracks'] and is executed only after those features are available in the pipeline feature store.
Examples
--------
>>> import pandas as pd
>>> from voxatlas.features.feature_input import FeatureInput
>>> from voxatlas.features.feature_output import TableFeatureOutput
>>> from voxatlas.features.phonology.formant.slope import FormantSlopeExtractor
>>> from voxatlas.pipeline.feature_store import FeatureStore
>>> tracks = pd.DataFrame(
... [
... {"frame_id": 1, "start": 0.0, "end": 0.01, "time": 0.005, "phoneme_id": 1, "label": "i", "ipa": "i", "is_vowel": 1.0, "F1": 300.0, "F2": 2200.0, "F3": 3000.0},
... {"frame_id": 2, "start": 0.01, "end": 0.02, "time": 0.015, "phoneme_id": 1, "label": "i", "ipa": "i", "is_vowel": 1.0, "F1": 320.0, "F2": 2180.0, "F3": 2980.0},
... ]
... )
>>> store = FeatureStore()
>>> store.add("phonology.formant.tracks", TableFeatureOutput(feature="phonology.formant.tracks", unit="frame", values=tracks))
>>> out = FormantSlopeExtractor().compute(FeatureInput(audio=None, units=None, context={"feature_store": store}), {})
>>> "F1_slope" in out.values.columns
True
"""
name = "phonology.formant.slope"
input_units = "phoneme"
output_units = "phoneme"
dependencies = ["phonology.formant.tracks"]
default_config = {}
[docs]
def compute(self, feature_input, params):
"""
Compute the extractor output for a single pipeline invocation.
This method is the reusable execution entry point for the extractor. It receives the standard ``FeatureInput`` bundle, applies the configured algorithm, and returns feature values aligned to the extractor output units for storage in the pipeline feature store.
Parameters
----------
feature_input : object
Structured extractor input bundling audio, hierarchical units, and execution context for this feature computation.
params : object
Resolved feature configuration for this invocation. Keys are feature-specific and merged from defaults and pipeline settings.
Returns
-------
FeatureOutput
Structured output aligned to the ``phoneme`` unit level when applicable.
Examples
--------
>>> import pandas as pd
>>> from voxatlas.features.feature_input import FeatureInput
>>> from voxatlas.features.feature_output import TableFeatureOutput
>>> from voxatlas.features.phonology.formant.slope import FormantSlopeExtractor
>>> from voxatlas.pipeline.feature_store import FeatureStore
>>> tracks = pd.DataFrame(
... [{"frame_id": 1, "start": 0.0, "end": 0.01, "time": 0.005, "phoneme_id": 1, "label": "i", "ipa": "i", "is_vowel": 1.0, "F1": 300.0, "F2": 2200.0, "F3": 3000.0}]
... )
>>> store = FeatureStore()
>>> store.add("phonology.formant.tracks", TableFeatureOutput(feature="phonology.formant.tracks", unit="frame", values=tracks))
>>> result = FormantSlopeExtractor().compute(FeatureInput(audio=None, units=None, context={"feature_store": store}), {})
>>> result.unit
'phoneme'
"""
tracks = feature_input.context["feature_store"].get("phonology.formant.tracks").values
return TableFeatureOutput(
feature=self.name,
unit="phoneme",
values=compute_vowel_slope(tracks),
)
registry.register(FormantSlopeExtractor)