# -*- coding: utf-8 -*-
#
# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
# Copyright (c) 2009 Olivier Guilyardi <olivier@samalyse.com>
# This file is part of TimeSide.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Author: Guillaume Pellerin <yomguy@parisson.com>
from timeside.core import implements, interfacedoc
from timeside.core.analyzer import Analyzer
from timeside.core.api import IValueAnalyzer
import numpy as np
from timeside.plugins.analyzer.utils import MACHINE_EPSILON
[docs]class Level(Analyzer):
"""Audio level analyzer
Examples
--------
>>> import timeside
>>> from timeside.core import get_processor
>>> from timeside.core.tools.test_samples import samples
>>> source = samples['sweep.mp3']
>>> decoder = get_processor('file_decoder')(uri=source)
>>> level = get_processor('level')()
>>> (decoder | level).run()
>>> ('level.max' in level.results.keys()) and ('level.rms' in level.results.keys() )
True
>>> max = level.results['level.max']
>>> print(max.data)
[0.]
>>> rms = level.results['level.rms']
>>> print(rms.data) # doctest: +ELLIPSIS
[-3.7...]
"""
implements(IValueAnalyzer)
[docs] @interfacedoc
def setup(self, channels=None, samplerate=None, blocksize=None,
totalframes=None):
super(Level, self).setup(channels, samplerate, blocksize, totalframes)
# max_level
self.max_value = 0
# rms_level
self.mean_values = np.array([])
[docs] @staticmethod
@interfacedoc
def id():
return "level"
[docs] @staticmethod
@interfacedoc
def name():
return "Level"
[docs] @staticmethod
@interfacedoc
def version():
return "1.0"
[docs] @staticmethod
@interfacedoc
def unit():
return "dBFS"
[docs] def process(self, frames, eod=False):
if frames.size:
# max_level
max_value = np.abs(frames).max()
if max_value > self.max_value:
self.max_value = max_value
# rms_level
self.mean_values = np.append(self.mean_values,
np.mean(np.square(frames)))
return frames, eod
[docs] def post_process(self):
# Max level
max_level = self.new_result(data_mode='value', time_mode='global')
max_level.id_metadata.id += '.' + "max"
max_level.id_metadata.name += ' ' + "Max"
if self.max_value == 0: # Prevent np.log10(0) = Inf
self.max_value = MACHINE_EPSILON
max_level.data_object.value = np.round(
20 * np.log10(self.max_value), 3)
self.add_result(max_level)
# RMS level
rms_level = self.new_result(data_mode='value', time_mode='global')
rms_level.id_metadata.id += '.' + "rms"
rms_level.id_metadata.name += ' ' + "RMS"
rms_val = np.sqrt(np.mean(self.mean_values))
if rms_val == 0:
rms_val = MACHINE_EPSILON
rms_level.data_object.value = np.round(20 * np.log10(rms_val), 3)
self.add_result(rms_level)
if __name__ == "__main__":
import doctest
import timeside
doctest.testmod(timeside.analyzer.level)