Add loading and saving of measurements to file

pull/14/head
Ewald de Wit 2022-09-30 11:44:46 +02:00
rodzic 1cfdd91ab4
commit 30e6f7239d
3 zmienionych plików z 90 dodań i 26 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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:

Wyświetl plik

@ -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)