kopia lustrzana https://github.com/erdewit/HiFiScan
Add loading and saving of measurements to file
rodzic
1cfdd91ab4
commit
30e6f7239d
|
@ -1,7 +1,7 @@
|
|||
"""'Optimize the frequency response spectrum of an audio system"""
|
||||
|
||||
from hifiscan.analyzer import (
|
||||
Analyzer, XY, geom_chirp, linear_chirp, transform_causality, resample,
|
||||
smooth, taper, tone, window)
|
||||
Analyzer, XY, geom_chirp, linear_chirp, resample, smooth, taper, tone,
|
||||
transform_causality, window)
|
||||
from hifiscan.audio import Audio
|
||||
from hifiscan.io_ import Sound, read_correction, read_wav, write_wav
|
||||
|
|
|
@ -4,9 +4,14 @@ from functools import lru_cache
|
|||
from typing import NamedTuple, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
from numpy.fft import fft, ifft, irfft, rfft
|
||||
|
||||
try:
|
||||
from numba import njit
|
||||
except ImportError:
|
||||
def njit(f):
|
||||
return f
|
||||
|
||||
from hifiscan.io_ import Correction
|
||||
|
||||
|
||||
|
@ -39,15 +44,19 @@ class Analyzer:
|
|||
|
||||
MAX_DELAY_SECS = 0.1
|
||||
TIMEOUT_SECS = 1.0
|
||||
CACHED_METHODS = [
|
||||
'X', 'Y', 'calcH', 'H', 'H2', 'h', 'h_inv', 'spectrum',
|
||||
'frequency', 'calibration', 'target']
|
||||
|
||||
chirp: np.ndarray
|
||||
x: np.ndarray
|
||||
y: np.ndarray
|
||||
sumH: np.ndarray
|
||||
numMeasurements: int
|
||||
rate: int
|
||||
fmin: float
|
||||
fmax: float
|
||||
time: float
|
||||
numMeasurements: int
|
||||
|
||||
def __init__(
|
||||
self, f0: int, f1: int, secs: float, rate: int, ampl: float,
|
||||
|
@ -63,18 +72,21 @@ class Analyzer:
|
|||
self.fmin = min(f0, f1)
|
||||
self.fmax = max(f0, f1)
|
||||
self.time = 0
|
||||
self.sumH = np.zeros(self.X().size)
|
||||
self.numMeasurements = 0
|
||||
self._calibration = calibration
|
||||
self._target = target
|
||||
self._sumH = np.zeros(self.X().size)
|
||||
|
||||
def __getstate__(self):
|
||||
return {k: v for k, v in self.__dict__.items()
|
||||
if k not in self.CACHED_METHODS}
|
||||
|
||||
def setCaching(self):
|
||||
"""
|
||||
Cache the main methods in a way that allows garbage collection of self.
|
||||
Calling this method again will in effect clear the previous caching.
|
||||
"""
|
||||
for name in ['X', 'Y', 'calcH', 'H', 'H2', 'h', 'h_inv', 'spectrum',
|
||||
'frequency', 'calibration', 'target']:
|
||||
for name in self.CACHED_METHODS:
|
||||
unbound = getattr(Analyzer, name)
|
||||
bound = types.MethodType(unbound, self)
|
||||
setattr(self, name, lru_cache(bound))
|
||||
|
@ -83,7 +95,7 @@ class Analyzer:
|
|||
"""Add measurements from other analyzer to this one."""
|
||||
if not self.isCompatible(analyzer):
|
||||
raise ValueError('Incompatible analyzers')
|
||||
self._sumH = self._sumH + analyzer._sumH
|
||||
self.sumH = self.sumH + analyzer.sumH
|
||||
self.numMeasurements += analyzer.numMeasurements
|
||||
self.setCaching()
|
||||
|
||||
|
@ -109,7 +121,7 @@ class Analyzer:
|
|||
if idx >= 0:
|
||||
self.y = np.array(recording[idx:idx + self.x.size])
|
||||
self.numMeasurements += 1
|
||||
self._sumH += self.calcH()
|
||||
self.sumH += self.calcH()
|
||||
self.setCaching()
|
||||
return True
|
||||
return False
|
||||
|
@ -181,7 +193,7 @@ class Analyzer:
|
|||
Transfer function H averaged over all measurements.
|
||||
"""
|
||||
freq = self.frequency()
|
||||
H = self._sumH / (self.numMeasurements or 1)
|
||||
H = self.sumH / (self.numMeasurements or 1)
|
||||
return XY(freq, H)
|
||||
|
||||
def H2(self, smoothing: float) -> XY:
|
||||
|
|
|
@ -3,6 +3,7 @@ import copy
|
|||
import datetime as dt
|
||||
import logging
|
||||
import os
|
||||
import pickle
|
||||
import signal
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
@ -19,6 +20,16 @@ class App(qt.QMainWindow):
|
|||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.paused = False
|
||||
self.analyzer = None
|
||||
self.refAnalyzer = None
|
||||
self.calibration = None
|
||||
self.target = None
|
||||
self.saveDir = Path.home()
|
||||
self.loop = asyncio.get_event_loop_policy().get_event_loop()
|
||||
self.task = self.loop.create_task(wrap_coro(self.analyze()))
|
||||
|
||||
self.setWindowTitle('HiFi Scan')
|
||||
topWidget = qt.QWidget()
|
||||
self.setCentralWidget(topWidget)
|
||||
|
@ -31,16 +42,6 @@ class App(qt.QMainWindow):
|
|||
self.stack.currentChanged.connect(self.plot)
|
||||
vbox.addWidget(self.stack)
|
||||
vbox.addWidget(self.createSharedControls())
|
||||
|
||||
self.paused = False
|
||||
self.analyzer = None
|
||||
self.refAnalyzer = None
|
||||
self.calibration = None
|
||||
self.target = None
|
||||
self.saveDir = Path.home()
|
||||
self.loop = asyncio.get_event_loop_policy().get_event_loop()
|
||||
self.task = self.loop.create_task(wrap_coro(self.analyze()))
|
||||
|
||||
self.resize(1800, 900)
|
||||
self.show()
|
||||
|
||||
|
@ -126,8 +127,8 @@ class App(qt.QMainWindow):
|
|||
self.targetSimPlot.clear()
|
||||
|
||||
def screenshot(self):
|
||||
timestamp = dt.datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
name = f'hifiscan_{timestamp}.png'
|
||||
timestamp = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
name = f'HiFiScan {timestamp}.png'
|
||||
filename, _ = qt.QFileDialog.getSaveFileName(
|
||||
self, 'Save screenshot', str(self.saveDir / name), 'PNG (*.png)')
|
||||
if filename:
|
||||
|
@ -393,17 +394,21 @@ class App(qt.QMainWindow):
|
|||
self.refAnalyzer.addMeasurements(self.analyzer)
|
||||
else:
|
||||
self.refAnalyzer = copy.copy(self.analyzer)
|
||||
measurementsLabel.setText(
|
||||
f'Measurements: {self.refAnalyzer.numMeasurements}')
|
||||
setMeasurementsText()
|
||||
self.plot()
|
||||
|
||||
def clearButtonClicked():
|
||||
self.refAnalyzer = None
|
||||
self.refSpectrumPlot.clear()
|
||||
measurementsLabel.setText('Measurements: ')
|
||||
setMeasurementsText()
|
||||
self.plot()
|
||||
|
||||
measurementsLabel = qt.QLabel('Measurements: ')
|
||||
def setMeasurementsText():
|
||||
num = self.refAnalyzer.numMeasurements if self.refAnalyzer else 0
|
||||
measurementsLabel.setText(f'Measurements: {num if num else ""}')
|
||||
|
||||
measurementsLabel = qt.QLabel('')
|
||||
setMeasurementsText()
|
||||
|
||||
storeButton = qt.QPushButton('Store')
|
||||
storeButton.clicked.connect(storeButtonClicked)
|
||||
|
@ -415,6 +420,52 @@ class App(qt.QMainWindow):
|
|||
clearButton.setShortcut('C')
|
||||
clearButton.setToolTip('<Key C>')
|
||||
|
||||
def load():
|
||||
path, _ = qt.QFileDialog.getOpenFileName(
|
||||
self, 'Load measurements', str(self.saveDir))
|
||||
if path:
|
||||
with open(path, 'rb') as f:
|
||||
self.refAnalyzer = pickle.load(f)
|
||||
setMeasurementsText()
|
||||
self.plot()
|
||||
|
||||
def loadAdd():
|
||||
path, _ = qt.QFileDialog.getOpenFileName(
|
||||
self, 'Load and Add measurements', str(self.saveDir))
|
||||
if path:
|
||||
with open(path, 'rb') as f:
|
||||
analyzer: hifi.Analyzer = pickle.load(f)
|
||||
if analyzer and analyzer.isCompatible(self.refAnalyzer):
|
||||
self.refAnalyzer.addMeasurements(analyzer)
|
||||
else:
|
||||
self.refAnalyzer = analyzer
|
||||
setMeasurementsText()
|
||||
self.plot()
|
||||
|
||||
def save():
|
||||
analyzer = self.refAnalyzer or self.analyzer
|
||||
timestamp = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
name = f'Measurements: {analyzer.numMeasurements}, {timestamp}'
|
||||
path, _ = qt.QFileDialog.getSaveFileName(
|
||||
self, 'Save measurements',
|
||||
str(self.saveDir / name))
|
||||
if path:
|
||||
self.saveDir = Path(path).parent
|
||||
with open(path, 'wb') as f:
|
||||
pickle.dump(analyzer, f)
|
||||
self.plot()
|
||||
|
||||
def filePressed():
|
||||
fileMenu.popup(fileButton.mapToGlobal(qtcore.QPoint(0, 0)))
|
||||
|
||||
fileMenu = qt.QMenu()
|
||||
fileMenu.addAction('Load', load)
|
||||
fileMenu.addAction('Load and Add', loadAdd)
|
||||
fileMenu.addAction('Save', save)
|
||||
|
||||
fileButton = qt.QPushButton('File...')
|
||||
fileButton.clicked.connect(filePressed)
|
||||
|
||||
screenshotButton = qt.QPushButton('Screenshot')
|
||||
screenshotButton.clicked.connect(self.screenshot)
|
||||
|
||||
|
@ -442,6 +493,7 @@ class App(qt.QMainWindow):
|
|||
hbox.addWidget(measurementsLabel)
|
||||
hbox.addWidget(storeButton)
|
||||
hbox.addWidget(clearButton)
|
||||
hbox.addWidget(fileButton)
|
||||
hbox.addStretch(1)
|
||||
hbox.addWidget(screenshotButton)
|
||||
hbox.addSpacing(32)
|
||||
|
|
Ładowanie…
Reference in New Issue