- 
                Notifications
    
You must be signed in to change notification settings  - Fork 177
 
SPL spectrum and microphone calibration files #305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -75,6 +75,28 @@ Rectangle { | |
| Layout.alignment: Qt.AlignVCenter | Qt.AlignRight | ||
| color: systemPalette.windowText | ||
| } | ||
| 
     | 
||
| Text { | ||
| id: splValues | ||
| textFormat: Text.MarkdownText | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please rebase your branch, and then move to StyledText here ? That's a change that has been merged to improve the display of the dB values. Also, please set a monospace font here.  | 
||
| text: level_view_model.two_channels ? "1: " + level_to_text(level_view_model.level_data_slow.level_spl) + "<br />2: " + level_to_text(level_view_model.level_data_slow_2.level_spl) : level_to_text(level_view_model.level_data_slow.level_spl) | ||
| font.pointSize: 14 | ||
| font.bold: true | ||
| verticalAlignment: Text.AlignBottom | ||
| horizontalAlignment: Text.AlignRight | ||
| Layout.alignment: Qt.AlignVCenter | Qt.AlignRight | ||
| color: systemPalette.windowText | ||
| } | ||
| 
     | 
||
| Text { | ||
| id: splLegend | ||
| textFormat: Text.PlainText | ||
| text: "dB SPL" | ||
| verticalAlignment: Text.AlignTop | ||
| horizontalAlignment: Text.AlignRight | ||
| Layout.alignment: Qt.AlignVCenter | Qt.AlignRight | ||
| color: systemPalette.windowText | ||
| } | ||
| 
     | 
||
| LevelsMeter { | ||
| Layout.fillHeight: true | ||
| 
          
            
          
           | 
    ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import QtQuick 2.15 | ||
| import QtQuick.Window 2.2 | ||
| import QtQuick.Layouts 1.15 | ||
| import QtQuick.Shapes 1.15 | ||
| import Friture 1.0 | ||
| 
     | 
||
| Item { | ||
| id: container | ||
| property var stateId | ||
| 
     | 
||
| // delay the load of the Plot until stateId has been set | ||
| Loader { | ||
| id: loader | ||
| anchors.fill: parent | ||
| } | ||
| 
     | 
||
| onStateIdChanged: { | ||
| console.log("stateId changed: " + stateId) | ||
| loader.sourceComponent = plotComponent | ||
| } | ||
| 
     | 
||
| Component { | ||
| id: plotComponent | ||
| Plot { | ||
| scopedata: Store.dock_states[container.stateId] | ||
| 
     | 
||
| Repeater { | ||
| model: scopedata.plot_items | ||
| 
     | 
||
| PlotFilledCurve { | ||
| anchors.fill: parent | ||
| curve: modelData | ||
| } | ||
| } | ||
| 
     | 
||
| Column { | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I'm surprised to see a FrequencyTracker here... I would have expected this file to look very much like https://github.com/tlecomte/friture/blob/master/friture/Scope.qml  | 
||
| anchors.fill: parent | ||
| spacing: 0 | ||
| FrequencyTracker { | ||
| visible: scopedata.showFrequencyTracker | ||
| anchors.left: parent.left | ||
| anchors.right: parent.right | ||
| fmaxValue: scopedata.fmaxValue | ||
| fmaxLogicalValue: scopedata.fmaxLogicalValue | ||
| } | ||
| 
     | 
||
| FrequencyTracker { | ||
| visible: scopedata.showPitchTracker | ||
| anchors.left: parent.left | ||
| anchors.right: parent.right | ||
| fmaxValue: scopedata.fpitchDisplayText | ||
| fmaxLogicalValue: scopedata.fpitchValue | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -19,14 +19,15 @@ | |
| 
     | 
||
| import logging | ||
| 
     | 
||
| from numpy import linspace, log10, cos, arange, pi | ||
| from numpy import linspace, log10, cos, arange, pi, interp | ||
| from numpy.fft import rfft | ||
| from friture.audiobackend import SAMPLING_RATE | ||
| 
     | 
||
| 
     | 
||
| class audioproc(): | ||
| 
     | 
||
| def __init__(self): | ||
| def __init__(self, parent): | ||
| self.parent_widget = parent | ||
| self.logger = logging.getLogger(__name__) | ||
| 
     | 
||
| self.freq = linspace(0, SAMPLING_RATE / 2, 10) | ||
| 
        
          
        
         | 
    @@ -39,6 +40,10 @@ def __init__(self): | |
| 
     | 
||
| self.fft_size = 10 | ||
| 
     | 
||
| self.calibration = [0 for i in self.freq] | ||
| 
     | 
||
| self.recalculate_calibration() | ||
| 
     | 
||
| def analyzelive(self, samples): | ||
| # FFT for a linear transformation in frequency scale | ||
| fft = rfft(samples * self.window) | ||
| 
          
            
          
           | 
    @@ -95,6 +100,17 @@ def update_freq_cache(self): | |
| self.B = 0.17 + 20. * log10(Rb + eps) | ||
| self.A = 2.0 + 20. * log10(Ra + eps) | ||
| 
     | 
||
| self.recalculate_calibration() | ||
| 
     | 
||
| def recalculate_calibration(self): | ||
| try: | ||
| if hasattr(self.parent_widget.parent(), "dockmanager"): | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This creates a strong coupling with how the audioproc is used in the object chain. It would be great to find a way to pass the calibration without having to look at the parents  | 
||
| self.calibration = interp(self.freq, self.parent_widget.parent().dockmanager.parent().original_calibration_freqs, self.parent_widget.parent().dockmanager.parent().original_calibration) | ||
| else: | ||
| self.calibration = interp(self.freq, self.parent_widget.parent().original_calibration_freqs, self.parent_widget.parent().original_calibration) | ||
| except: | ||
| self.calibration = [0 for i in self.freq] | ||
| 
     | 
||
| # above is done a FFT of the signal. This is ok for linear frequency scale, but | ||
| # not satisfying for logarithmic scale, which is much more adapted to voice or music | ||
| # analysis | ||
| 
          
            
          
           | 
    ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -33,6 +33,8 @@ | |
| from friture.audiobackend import SAMPLING_RATE | ||
| from friture.qml_tools import qml_url, raise_if_error | ||
| 
     | 
||
| from friture.spl_settings import SPL_Settings_Dialog | ||
| 
     | 
||
| SMOOTH_DISPLAY_TIMER_PERIOD_MS = 25 | ||
| LEVEL_TEXT_LABEL_PERIOD_MS = 250 | ||
| 
     | 
||
| 
          
            
          
           | 
    @@ -73,8 +75,10 @@ def __init__(self, parent, engine): | |
| # initialize the settings dialog | ||
| self.settings_dialog = Levels_Settings_Dialog(self) | ||
| 
     | 
||
| self.calibration = 0 | ||
| 
     | 
||
| # initialize the class instance that will do the fft | ||
| self.proc = audioproc() | ||
| self.proc = audioproc(self) | ||
| 
     | 
||
| # time = SMOOTH_DISPLAY_TIMER_PERIOD_MS/1000. #DISPLAY | ||
| # time = 0.025 #IMPULSE setting for a sound level meter | ||
| 
          
            
          
           | 
    @@ -136,6 +140,7 @@ def handle_new_data(self, floatdata): | |
| self.old_rms = value_rms | ||
| 
     | 
||
| self.level_view_model.level_data.level_rms = 10. * np.log10(value_rms + 0. * 1e-80) | ||
| self.level_view_model.level_data.level_spl = self.level_view_model.level_data.level_rms + 94.0 + self.calibration | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why 94 ? If this has a specific meaning, could you please create define a variable and name it ? Thanks!  | 
||
| self.level_view_model.level_data.level_max = 20. * np.log10(self.old_max + 0. * 1e-80) | ||
| self.level_view_model.level_data_ballistic.peak_iec = dB_to_IEC(max(self.level_view_model.level_data.level_max, self.level_view_model.level_data.level_rms)) | ||
| 
     | 
||
| 
        
          
        
         | 
    @@ -157,6 +162,7 @@ def handle_new_data(self, floatdata): | |
| self.old_rms_2 = value_rms | ||
| 
     | 
||
| self.level_view_model.level_data_2.level_rms = 10. * np.log10(value_rms + 0. * 1e-80) | ||
| self.level_view_model.level_data_2.level_spl = self.level_view_model.level_data_2.level_rms + 94.0 + self.calibration | ||
| self.level_view_model.level_data_2.level_max = 20. * np.log10(self.old_max_2 + 0. * 1e-80) | ||
| self.level_view_model.level_data_ballistic_2.peak_iec = dB_to_IEC(max(self.level_view_model.level_data_2.level_max, self.level_view_model.level_data_2.level_rms)) | ||
| 
     | 
||
| 
        
          
        
         | 
    @@ -169,10 +175,12 @@ def canvasUpdate(self): | |
| 
     | 
||
| if self.i == LEVEL_TEXT_LABEL_STEPS: | ||
| self.level_view_model.level_data_slow.level_rms = self.level_view_model.level_data.level_rms | ||
| self.level_view_model.level_data_slow.level_spl = self.level_view_model.level_data.level_spl | ||
| self.level_view_model.level_data_slow.level_max = self.level_view_model.level_data.level_max | ||
| 
     | 
||
| if self.two_channels: | ||
| self.level_view_model.level_data_slow_2.level_rms = self.level_view_model.level_data_2.level_rms | ||
| self.level_view_model.level_data_slow_2.level_spl = self.level_view_model.level_data_2.level_spl | ||
| self.level_view_model.level_data_slow_2.level_max = self.level_view_model.level_data_2.level_max | ||
| 
     | 
||
| self.i = self.i % LEVEL_TEXT_LABEL_STEPS | ||
| 
          
            
          
           | 
    ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -17,7 +17,7 @@ | |
| # You should have received a copy of the GNU General Public License | ||
| # along with Friture. If not, see <http://www.gnu.org/licenses/>. | ||
| 
     | 
||
| from numpy import log10, array, where | ||
| from numpy import log10, array, where, interp | ||
| 
     | 
||
| from friture.filter import (octave_filter_bank_decimation, octave_frequencies, | ||
| octave_filter_bank_decimation_filtic, NOCTAVE) | ||
| 
        
          
        
         | 
    @@ -26,12 +26,16 @@ | |
| 
     | 
||
| class Octave_Filters(): | ||
| 
     | 
||
| def __init__(self, bandsperoctave): | ||
| def __init__(self, parent, bandsperoctave): | ||
| [self.bdec, self.adec] = generated_filters.PARAMS['dec'] | ||
| 
     | 
||
| self.bdec = array(self.bdec) | ||
| self.adec = array(self.adec) | ||
| 
     | 
||
| self.calibration = [] | ||
| 
     | 
||
| self.parent_widget = parent | ||
| 
     | 
||
| self.setbandsperoctave(bandsperoctave) | ||
| 
     | 
||
| def filter(self, floatdata): | ||
| 
        
          
        
         | 
    @@ -48,12 +52,21 @@ def get_decs(self): | |
| 
     | 
||
| return decs | ||
| 
     | 
||
| def recalculate_calibration(self): | ||
| try: | ||
| 
     | 
||
| self.calibration = interp(self.fi, self.parent_widget.parent().dockmanager.parent().original_calibration_freqs, self.parent_widget.parent().dockmanager.parent().original_calibration) | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same comment as in audioproc.py: we would need to find a way to achieve this without having to walk through the parents  | 
||
| except: | ||
| self.calibration = [0 for i in self.fi] | ||
| 
     | 
||
| def setbandsperoctave(self, bandsperoctave): | ||
| self.bandsperoctave = bandsperoctave | ||
| self.nbands = NOCTAVE * self.bandsperoctave | ||
| self.fi, self.flow, self.fhigh = octave_frequencies(self.nbands, self.bandsperoctave) | ||
| [self.boct, self.aoct, fi, flow, fhigh] = generated_filters.PARAMS['%d' % bandsperoctave] | ||
| 
     | 
||
| self.recalculate_calibration() | ||
| 
     | 
||
| self.boct = [array(f) for f in self.boct] | ||
| self.aoct = [array(f) for f in self.aoct] | ||
| 
     | 
||
| 
          
            
          
           | 
    ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I read this correctly, the SPL value is shown even if there is no calibration file set. Could this be adjusted to only show the SPL values and the "dB SPL" label below when a calibration file is provided ?