audio-spectrogram 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audio_spectrogram/__init__.py +12 -0
- audio_spectrogram/__main__.py +36 -0
- audio_spectrogram/app/__init__.py +2 -0
- audio_spectrogram/app/application.py +81 -0
- audio_spectrogram/app/control_panel_handler.py +166 -0
- audio_spectrogram/app/plot_handler.py +107 -0
- audio_spectrogram/app/qapplication.py +26 -0
- audio_spectrogram/app/spectrogram.py +62 -0
- audio_spectrogram/app/widgets/__init__.py +2 -0
- audio_spectrogram/app/widgets/combobox.py +39 -0
- audio_spectrogram/app/widgets/control_panel_widget.py +131 -0
- audio_spectrogram/app/widgets/main_window.py +44 -0
- audio_spectrogram/app/widgets/plot_widget.py +58 -0
- audio_spectrogram/app/widgets/power_of_two_spinbox.py +42 -0
- audio_spectrogram/util.py +63 -0
- audio_spectrogram-0.1.0.dist-info/METADATA +48 -0
- audio_spectrogram-0.1.0.dist-info/RECORD +21 -0
- audio_spectrogram-0.1.0.dist-info/WHEEL +5 -0
- audio_spectrogram-0.1.0.dist-info/entry_points.txt +5 -0
- audio_spectrogram-0.1.0.dist-info/licenses/LICENSE +674 -0
- audio_spectrogram-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Copyright 2026 - 2026, Artur Drogunow and the Audio-Spectrogram contributors
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget
|
|
7
|
+
|
|
8
|
+
from audio_spectrogram import APP_TITLE, __version__
|
|
9
|
+
|
|
10
|
+
from .control_panel_widget import ControlPanelWidget
|
|
11
|
+
from .plot_widget import PlotWidget
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MainWindow(QMainWindow):
|
|
15
|
+
def __init__(self) -> None:
|
|
16
|
+
super().__init__()
|
|
17
|
+
|
|
18
|
+
# variables
|
|
19
|
+
|
|
20
|
+
# widgets
|
|
21
|
+
self.control_panel_widget = ControlPanelWidget()
|
|
22
|
+
self.plot_widget = PlotWidget()
|
|
23
|
+
|
|
24
|
+
# setup
|
|
25
|
+
self._setup_widgets()
|
|
26
|
+
self._setup_layout()
|
|
27
|
+
self._connect_signals()
|
|
28
|
+
|
|
29
|
+
def _setup_widgets(self) -> None:
|
|
30
|
+
title = (
|
|
31
|
+
f"{APP_TITLE} {__version__} "
|
|
32
|
+
f"(Python {sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]})"
|
|
33
|
+
)
|
|
34
|
+
self.setWindowTitle(title)
|
|
35
|
+
|
|
36
|
+
def _setup_layout(self) -> None:
|
|
37
|
+
central_widget = QWidget()
|
|
38
|
+
self.setCentralWidget(central_widget)
|
|
39
|
+
vbox = QVBoxLayout(central_widget)
|
|
40
|
+
vbox.addWidget(self.control_panel_widget, 0)
|
|
41
|
+
vbox.addWidget(self.plot_widget, 1)
|
|
42
|
+
|
|
43
|
+
def _connect_signals(self) -> None:
|
|
44
|
+
pass
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copyright 2026 - 2026, Artur Drogunow and the Audio-Spectrogram contributors
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import pyqtgraph as pg
|
|
5
|
+
from PySide6.QtWidgets import QGroupBox, QVBoxLayout
|
|
6
|
+
|
|
7
|
+
pg.setConfigOption(opt="useNumba", value=True)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PlotWidget(QGroupBox):
|
|
11
|
+
def __init__(self) -> None:
|
|
12
|
+
super().__init__("Signal Viewer")
|
|
13
|
+
|
|
14
|
+
# variables
|
|
15
|
+
|
|
16
|
+
# widgets
|
|
17
|
+
self.glw = pg.GraphicsLayoutWidget()
|
|
18
|
+
self.time_plot = self.glw.addPlot(row=0, col=0)
|
|
19
|
+
self.time_plot_item = pg.PlotCurveItem()
|
|
20
|
+
self.spec_plot = self.glw.addPlot(row=1, col=0)
|
|
21
|
+
self.spec_plot_item = pg.ImageItem(axisOrder="row-major", levels=[-90, 0])
|
|
22
|
+
|
|
23
|
+
# setup
|
|
24
|
+
self._setup_widgets()
|
|
25
|
+
self._setup_layout()
|
|
26
|
+
self._connect_signals()
|
|
27
|
+
|
|
28
|
+
def _setup_widgets(self) -> None:
|
|
29
|
+
# set line properties
|
|
30
|
+
self.time_plot_item.setClickable(False)
|
|
31
|
+
self.time_plot_item.setPen(pg.mkPen("coral", width=0.5))
|
|
32
|
+
self.time_plot_item.setSkipFiniteCheck(True)
|
|
33
|
+
self.time_plot_item.setSegmentedLineMode("on")
|
|
34
|
+
|
|
35
|
+
# horizontally align left axis
|
|
36
|
+
self.time_plot.getAxis("left").setWidth(40)
|
|
37
|
+
self.spec_plot.getAxis("left").setWidth(40)
|
|
38
|
+
|
|
39
|
+
# setup time plot
|
|
40
|
+
self.time_plot.setLabel("left", "Amplitude")
|
|
41
|
+
self.time_plot.setLabel("bottom", "Time", units="s")
|
|
42
|
+
self.time_plot.showGrid(x=True, y=True)
|
|
43
|
+
self.time_plot.addItem(self.time_plot_item)
|
|
44
|
+
|
|
45
|
+
# setup spectrum plot
|
|
46
|
+
self.spec_plot.setYRange(-100, 7100)
|
|
47
|
+
self.spec_plot.setLabel("left", "Frequency", units="Hz")
|
|
48
|
+
self.spec_plot.setLabel("bottom", "Time", units="s")
|
|
49
|
+
self.spec_plot.showGrid(x=False, y=False)
|
|
50
|
+
self.spec_plot_item.setColorMap(pg.colormap.get("magma"))
|
|
51
|
+
self.spec_plot.addItem(self.spec_plot_item)
|
|
52
|
+
|
|
53
|
+
def _setup_layout(self) -> None:
|
|
54
|
+
layout = QVBoxLayout(self)
|
|
55
|
+
layout.addWidget(self.glw)
|
|
56
|
+
|
|
57
|
+
def _connect_signals(self) -> None:
|
|
58
|
+
pass
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright 2026 - 2026, Artur Drogunow and the Audio-Spectrogram contributors
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from PySide6.QtWidgets import (
|
|
7
|
+
QSpinBox,
|
|
8
|
+
QWidget,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
DEFAULT_VALUE: typing.Final = 1024
|
|
12
|
+
MIN_VALUE: typing.Final = 64
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PowerOfTwoSpinBox(QSpinBox):
|
|
16
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
|
17
|
+
super().__init__(parent)
|
|
18
|
+
|
|
19
|
+
self.setReadOnly(True)
|
|
20
|
+
self.setRange(MIN_VALUE, 2**30)
|
|
21
|
+
self.setValue(1024) # default value
|
|
22
|
+
|
|
23
|
+
@typing.override
|
|
24
|
+
def stepBy(self, steps: int, /) -> None:
|
|
25
|
+
value: int = self.value()
|
|
26
|
+
|
|
27
|
+
for _ in range(abs(steps)):
|
|
28
|
+
if steps > 0:
|
|
29
|
+
value *= 2
|
|
30
|
+
else:
|
|
31
|
+
value = max(MIN_VALUE, value // 2)
|
|
32
|
+
|
|
33
|
+
self.setValue(value)
|
|
34
|
+
|
|
35
|
+
@typing.override
|
|
36
|
+
def stepEnabled(self, /) -> QSpinBox.StepEnabledFlag:
|
|
37
|
+
flags = QSpinBox.StepEnabledFlag.StepUpEnabled
|
|
38
|
+
|
|
39
|
+
if self.value() > MIN_VALUE:
|
|
40
|
+
flags |= QSpinBox.StepEnabledFlag.StepDownEnabled
|
|
41
|
+
|
|
42
|
+
return flags
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Copyright 2026 - 2026, Artur Drogunow and the Audio-Spectrogram contributors
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import datetime
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Final
|
|
7
|
+
|
|
8
|
+
from platformdirs import user_log_path
|
|
9
|
+
|
|
10
|
+
from audio_spectrogram import APP_TITLE, LOGGER, PACKAGE_NAME
|
|
11
|
+
|
|
12
|
+
VERBOSITY_LEVEL_APP_DEBUG: Final = 1
|
|
13
|
+
VERBOSITY_LEVEL_ROOT_WARNING: Final = 1
|
|
14
|
+
VERBOSITY_LEVEL_ROOT_INFO: Final = 2
|
|
15
|
+
VERBOSITY_LEVEL_ROOT_DEBUG: Final = 3
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def setup_logging(verbosity: int) -> None:
|
|
19
|
+
iso_date = datetime.datetime.now(tz=datetime.UTC).strftime("%Y-%m-%d")
|
|
20
|
+
logfile_path = user_log_path(PACKAGE_NAME, ensure_exists=True) / f"{iso_date}.log"
|
|
21
|
+
|
|
22
|
+
with logfile_path.open(mode="at") as file:
|
|
23
|
+
if file.tell() > 0:
|
|
24
|
+
file.write("\n\n")
|
|
25
|
+
file.writelines(
|
|
26
|
+
[
|
|
27
|
+
"*" * 80 + "\n",
|
|
28
|
+
f"Start {APP_TITLE}".center(80) + "\n",
|
|
29
|
+
"*" * 80 + "\n",
|
|
30
|
+
]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# instantiate logging handlers
|
|
34
|
+
file_handler = logging.FileHandler(logfile_path)
|
|
35
|
+
stream_handler = logging.StreamHandler()
|
|
36
|
+
|
|
37
|
+
# configure formatting
|
|
38
|
+
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
|
39
|
+
file_handler.setFormatter(formatter)
|
|
40
|
+
stream_handler.setFormatter(formatter)
|
|
41
|
+
|
|
42
|
+
# determine log levels for app (package_level) and dependencies (root_level)
|
|
43
|
+
package_level = logging.DEBUG if verbosity >= VERBOSITY_LEVEL_APP_DEBUG else logging.INFO
|
|
44
|
+
if verbosity >= VERBOSITY_LEVEL_ROOT_DEBUG:
|
|
45
|
+
root_level = logging.DEBUG
|
|
46
|
+
elif verbosity >= VERBOSITY_LEVEL_ROOT_INFO:
|
|
47
|
+
root_level = logging.INFO
|
|
48
|
+
elif verbosity >= VERBOSITY_LEVEL_ROOT_WARNING:
|
|
49
|
+
root_level = logging.WARNING
|
|
50
|
+
else:
|
|
51
|
+
root_level = logging.ERROR
|
|
52
|
+
|
|
53
|
+
# configure root logger
|
|
54
|
+
root_logger = logging.getLogger()
|
|
55
|
+
root_logger.handlers.clear()
|
|
56
|
+
root_logger.setLevel(root_level)
|
|
57
|
+
root_logger.addHandler(file_handler)
|
|
58
|
+
root_logger.addHandler(stream_handler)
|
|
59
|
+
|
|
60
|
+
# configure package logger
|
|
61
|
+
LOGGER.setLevel(package_level)
|
|
62
|
+
LOGGER.info("Root log level: %s", logging.getLevelName(root_level))
|
|
63
|
+
LOGGER.info("Package log level: %s", logging.getLevelName(package_level))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: audio-spectrogram
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Audio spectrogram app based on PySide6
|
|
5
|
+
Author-email: Artur Drogunow <Artur.Drogunow@zf.com>
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Issues, https://github.com/zariiii9003/audio_spectrogram/issues
|
|
8
|
+
Project-URL: Source, https://github.com/zariiii9003/audio_spectrogram
|
|
9
|
+
Keywords: Audio,Microphone,FFT,STFT,Spectrogram,Sonogram
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numba==0.65.*
|
|
21
|
+
Requires-Dist: numpy==2.4.*
|
|
22
|
+
Requires-Dist: platformdirs==4.9.*
|
|
23
|
+
Requires-Dist: pyqtgraph==0.14.*
|
|
24
|
+
Requires-Dist: pyside6==6.11.*
|
|
25
|
+
Requires-Dist: rocket-fft==0.3.*
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# Audio Spectrogram
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/audio-spectrogram)
|
|
31
|
+
[](https://pypi.org/project/audio-spectrogram)
|
|
32
|
+
|
|
33
|
+
## Introduction
|
|
34
|
+
|
|
35
|
+
*Audio Spectrogram* is a small python GUI application to analyze the frequency content of your microphone input.
|
|
36
|
+
|
|
37
|
+
<img src="https://raw.githubusercontent.com/zariiii9003/audio_spectrogram/refs/heads/master/doc/GUI.png" width="683" alt="GUI Screenshot" />
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
You can run it directly from your command line:
|
|
42
|
+
```bash
|
|
43
|
+
uvx audio-spectrogram
|
|
44
|
+
```
|
|
45
|
+
or
|
|
46
|
+
```bash
|
|
47
|
+
pipx audio-spectrogram
|
|
48
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
audio_spectrogram/__init__.py,sha256=5EDkWGwK2wElA1YUBkPjBbDX6MHrcKcLYSAUIxT6PhE,370
|
|
2
|
+
audio_spectrogram/__main__.py,sha256=Ss1Tm75zZmuX_pQqJuLa76nPwhUj7gBe-Mz5ObDssvs,910
|
|
3
|
+
audio_spectrogram/util.py,sha256=VuKBtd6rgnKkaDZx6t0wh1Ff-ZQywBLgkMqNMOzP4NE,2185
|
|
4
|
+
audio_spectrogram/app/__init__.py,sha256=-Og5GFBNZ-JabE4MKs8oKmad2KLKLljI_L8a_m_8CPY,123
|
|
5
|
+
audio_spectrogram/app/application.py,sha256=GuZSet7Im-bn_EWOQUXd9qzgrIwZbkkvhTNQYqtzYkQ,2818
|
|
6
|
+
audio_spectrogram/app/control_panel_handler.py,sha256=R1RpaZcrf_4VwOaXB4ouYnEGqbqYuiGeNaJAvH0bF00,6271
|
|
7
|
+
audio_spectrogram/app/plot_handler.py,sha256=HBYyzF_eF2PFSvQQOKuadFaOQtMROYwd1XOZRhkBDR0,3824
|
|
8
|
+
audio_spectrogram/app/qapplication.py,sha256=QO9oYPxgxZbdLS13qXS9VCuFr7gPbp0cAo4UyvAlLnc,606
|
|
9
|
+
audio_spectrogram/app/spectrogram.py,sha256=YBJMjsmU72UugLNRvXbdliqviJR4b7ivWyVJJwCQ6xM,2387
|
|
10
|
+
audio_spectrogram/app/widgets/__init__.py,sha256=-Og5GFBNZ-JabE4MKs8oKmad2KLKLljI_L8a_m_8CPY,123
|
|
11
|
+
audio_spectrogram/app/widgets/combobox.py,sha256=bOdtX3y8_oLMdlBMHKK19rKhwMmGe_4zQreDyY4E-b0,1246
|
|
12
|
+
audio_spectrogram/app/widgets/control_panel_widget.py,sha256=zCGpE-Qb57v5fvq9gDQftyRKJgHUAGNg35JCXSq-79I,4410
|
|
13
|
+
audio_spectrogram/app/widgets/main_window.py,sha256=AxlhEqKGiXXgUClBv77vVICb-o9_jJw0JRmyYY_j4lY,1228
|
|
14
|
+
audio_spectrogram/app/widgets/plot_widget.py,sha256=aQkKOEXKdfrW1wLKBN8qh3GTL7h11IpeP8ljxnvtVXI,1997
|
|
15
|
+
audio_spectrogram/app/widgets/power_of_two_spinbox.py,sha256=h97FCkDcCk_jXOk07o2138QQpQQNLAer_3RgL92qP30,1062
|
|
16
|
+
audio_spectrogram-0.1.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
17
|
+
audio_spectrogram-0.1.0.dist-info/METADATA,sha256=Zduygfxv-TWQkrndJcKa_kJxK1CGQU4FfrihWtAAYJE,1783
|
|
18
|
+
audio_spectrogram-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
19
|
+
audio_spectrogram-0.1.0.dist-info/entry_points.txt,sha256=dAZ0_Eq--lfWJ1yhxiybwPQwdUckQg4Bbkgyr2hkuZ0,145
|
|
20
|
+
audio_spectrogram-0.1.0.dist-info/top_level.txt,sha256=HcjNccVeBlauPVpuVzz1qMRDiYT3YKpyHMJH2s6_f1A,18
|
|
21
|
+
audio_spectrogram-0.1.0.dist-info/RECORD,,
|