Porównaj commity

...

3 Commity

Autor SHA1 Wiadomość Data
Ewald de Wit 9b6e4b22e1 v1.5.0 2023-11-23 15:02:54 +01:00
Ewald de Wit 98c8d7d575 Update requirements 2023-11-23 15:02:38 +01:00
Ewald de Wit 4e98d09ee9 Add combobox to select sample rate. 2023-11-23 15:02:19 +01:00
5 zmienionych plików z 85 dodań i 50 usunięć

Wyświetl plik

@ -150,8 +150,8 @@ class Analyzer:
"""
size = size or self.X().size
nyq = self.rate / 2
i0 = int(0.5 + size * self.fmin / nyq)
i1 = int(0.5 + size * self.fmax / nyq)
i0 = min(size - 1, int(0.5 + size * self.fmin / nyq))
i1 = min(size - 1, int(0.5 + size * self.fmax / nyq))
return slice(i0, i1 + 1)
def calibration(self) -> Optional[np.ndarray]:

Wyświetl plik

@ -8,16 +8,20 @@ import signal
import sys
from pathlib import Path
from PyQt6 import QtCore as qtcore, QtGui as qtgui, QtWidgets as qt
import numpy as np
import pyqtgraph as pg
import sounddevice as sd
from PyQt6 import QtCore as qtcore, QtGui as qtgui, QtWidgets as qt
import hifiscan as hifi
class App(qt.QWidget):
SAMPLE_RATES = {rate: i for i, rate in enumerate([
8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000,
176400, 192000, 352800, 384000])}
def __init__(self):
super().__init__()
self.paused = False
@ -39,40 +43,61 @@ class App(qt.QWidget):
vbox.addWidget(self.stack)
vbox.addWidget(self.sharedControls())
self.resetAudio()
self.setLayout(vbox)
self.setWindowTitle('HiFi Scan')
self.resize(1800, 900)
self.show()
async def analyze(self):
with hifi.Audio() as audio:
while True:
lo = self.lo.value()
hi = self.hi.value()
secs = self.secs.value()
ampl = self.ampl.value() / 100
ch = self.channelsBox.currentIndex()
if self.paused or lo >= hi or secs <= 0 or not ampl:
await asyncio.sleep(0.1)
continue
while True:
self.audioChanged = False
try:
rate = int(self.rateCombo.currentText())
audio = None
audio = hifi.Audio(rate)
while not self.audioChanged:
lo = self.lo.value()
hi = self.hi.value()
secs = self.secs.value()
ampl = self.ampl.value() / 100
ch = self.channelCombo.currentIndex()
if self.paused or lo >= hi or secs <= 0 or not ampl:
await asyncio.sleep(0.1)
continue
analyzer = hifi.Analyzer(lo, hi, secs, audio.rate, ampl,
self.calibration, self.target)
sound = analyzer.chirp
if ch:
silence = np.zeros_like(sound)
sound = [sound, silence] if ch == 1 else [silence, sound]
audio.play(sound)
async for recording in audio.record():
if self.paused:
audio.cancelPlay()
break
if analyzer.findMatch(recording):
self.analyzer = analyzer
self.plot()
break
if analyzer.timedOut():
break
analyzer = hifi.Analyzer(lo, hi, secs, audio.rate, ampl,
self.calibration, self.target)
sound = analyzer.chirp
if ch:
silence = np.zeros_like(sound)
sound = [sound, silence] if ch == 1 \
else [silence, sound]
audio.play(sound)
async for recording in audio.record():
if self.paused:
audio.cancelPlay()
break
if analyzer.findMatch(recording):
self.analyzer = analyzer
self.plot()
break
if analyzer.timedOut():
break
except Exception as exc:
qt.QMessageBox.critical(self, 'Error', str(exc))
self.resetAudio()
finally:
if audio:
audio.close()
def resetAudio(self):
defaultRate = sd.query_devices(device='default')['default_samplerate']
if defaultRate not in self.SAMPLE_RATES:
defaultRate = 48000
index = self.SAMPLE_RATES[defaultRate]
self.rateCombo.setCurrentIndex(index)
self.audioChanged = True
def plot(self, *_):
if self.stack.currentIndex() == 0:
@ -95,7 +120,7 @@ class App(qt.QWidget):
self.refSpectrumPlot.setData(*spectrum)
def plotIR(self):
if self.refAnalyzer and self.useBox.currentIndex() == 0:
if self.refAnalyzer and self.useCombo.currentIndex() == 0:
analyzer = self.refAnalyzer
else:
analyzer = self.analyzer
@ -135,7 +160,7 @@ class App(qt.QWidget):
self.saveDir = Path(path).parent
def saveIR(self):
if self.refAnalyzer and self.useBox.currentIndex() == 0:
if self.refAnalyzer and self.useCombo.currentIndex() == 0:
analyzer = self.refAnalyzer
else:
analyzer = self.analyzer
@ -205,8 +230,8 @@ class App(qt.QWidget):
value=1.0, step=0.1, bounds=[0.1, 30], suffix='s')
self.ampl = pg.SpinBox(
value=40, step=1, bounds=[0, 100], suffix='%')
self.channelsBox = qt.QComboBox()
self.channelsBox.addItems(['Stereo', 'Left', 'Right'])
self.channelCombo = qt.QComboBox()
self.channelCombo.addItems(['Stereo', 'Left', 'Right'])
self.spectrumSmoothing = pg.SpinBox(
value=15, step=1, bounds=[0, 30])
self.spectrumSmoothing.sigValueChanging.connect(self.plot)
@ -224,7 +249,7 @@ class App(qt.QWidget):
hbox.addSpacing(32)
hbox.addWidget(qt.QLabel('Amplitude: '))
hbox.addWidget(self.ampl)
hbox.addWidget(self.channelsBox)
hbox.addWidget(self.channelCombo)
hbox.addSpacing(32)
hbox.addWidget(qt.QLabel('Smoothing: '))
hbox.addWidget(self.spectrumSmoothing)
@ -292,9 +317,9 @@ class App(qt.QWidget):
value=0, step=5, bounds=[0, 100], suffix='%')
self.causality.sigValueChanging.connect(self.plot)
self.useBox = qt.QComboBox()
self.useBox.addItems(['Stored measurements', 'Last measurement'])
self.useBox.currentIndexChanged.connect(self.plot)
self.useCombo = qt.QComboBox()
self.useCombo.addItems(['Stored measurements', 'Last measurement'])
self.useCombo.currentIndexChanged.connect(self.plot)
exportButton = qt.QPushButton('Export as WAV')
exportButton.setShortcut('E')
@ -319,7 +344,7 @@ class App(qt.QWidget):
hbox.addWidget(self.causality)
hbox.addSpacing(32)
hbox.addWidget(qt.QLabel('Use: '))
hbox.addWidget(self.useBox)
hbox.addWidget(self.useCombo)
hbox.addStretch(1)
hbox.addWidget(exportButton)
vbox.addLayout(hbox)
@ -452,6 +477,13 @@ class App(qt.QWidget):
spectrumButton.setChecked(True)
buttons.idClicked.connect(self.stack.setCurrentIndex)
def setAudioChanged():
self.audioChanged = True
self.rateCombo = qt.QComboBox()
self.rateCombo.addItems(str(rate) for rate in self.SAMPLE_RATES)
self.rateCombo.currentIndexChanged.connect(setAudioChanged)
def toolsPressed():
tools.popup(toolsButton.mapToGlobal(qtcore.QPoint(0, 0)))
@ -620,10 +652,11 @@ class App(qt.QWidget):
hbox.addWidget(clearButton)
hbox.addWidget(fileButton)
hbox.addStretch(1)
hbox.addWidget(qt.QLabel('Sample rate:'))
hbox.addWidget(self.rateCombo)
hbox.addStretch(1)
hbox.addWidget(screenshotButton)
hbox.addSpacing(32)
hbox.addWidget(pauseButton)
hbox.addSpacing(32)
hbox.addWidget(exitButton)
vbox.addLayout(hbox)

Wyświetl plik

@ -1,7 +1,7 @@
import array
from collections import deque
from dataclasses import dataclass
from typing import AsyncIterator, Deque
from typing import AsyncIterator, Deque, Optional
import eventkit as ev
import numpy as np
@ -18,11 +18,12 @@ class Audio:
Emits a new piece of recorded sound as a numpy float array.
"""
def __init__(self):
def __init__(self, rate: Optional[float] = None):
self.recorded = ev.Event()
self.playQ: Deque[PlayItem] = deque()
self.stream = sd.Stream(
channels=(1, 2),
samplerate=rate,
callback=self._onStream)
self.stream.start()
self.rate = self.stream.samplerate

Wyświetl plik

@ -1,6 +1,6 @@
numpy~=1.23.3
pyqtgraph~=0.12.4
PyQt6~=6.3.1
numpy~=1.26.1
pyqtgraph~=0.13.3
PyQt6~=6.6.0
eventkit~=0.8.9
sounddevice~=0.4.5
setuptools~=59.8.0
sounddevice~=0.4.6
setuptools~=67.7.2

Wyświetl plik

@ -7,7 +7,7 @@ with open("README.rst", 'r', encoding="utf-8") as f:
setup(
name='hifiscan',
version='1.4.3',
version='1.5.0',
description='Optimize the audio quality of loudspeakers',
long_description=long_description,
packages=['hifiscan'],
@ -24,6 +24,7 @@ setup(
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3 :: Only',
],
keywords='frequency impulse response audio spectrum equalizer',