diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index b06508ac..8c2f815b 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -14,10 +14,9 @@ jobs: - ubuntu-latest - windows-latest py: + - "3.12" - "3.11" - "3.10" - - "3.9" - - "3.8" include: - os: macos-latest-xlarge py: "3.10.11" diff --git a/basic_pitch/inference.py b/basic_pitch/inference.py index 06fa54f0..eb36d8ab 100644 --- a/basic_pitch/inference.py +++ b/basic_pitch/inference.py @@ -52,13 +52,7 @@ import librosa import pretty_midi -from basic_pitch.constants import ( - AUDIO_SAMPLE_RATE, - AUDIO_N_SAMPLES, - ANNOTATIONS_FPS, - FFT_HOP, - AUDIO_WINDOW_LENGTH, -) +from basic_pitch.constants import AUDIO_SAMPLE_RATE, AUDIO_N_SAMPLES, ANNOTATIONS_FPS, FFT_HOP, AUDIO_WINDOW_LENGTH from basic_pitch.commandline_printing import ( generating_file_message, no_tf_warnings, @@ -245,10 +239,7 @@ def get_audio_input( def unwrap_output( - output: npt.NDArray[np.float32], - audio_original_length: int, - n_overlapping_frames: int, - hop_size: int, + output: npt.NDArray[np.float32], audio_original_length: int, n_overlapping_frames: int, hop_size: int ) -> np.array: """Unwrap batched model predictions to a single matrix. diff --git a/basic_pitch/layers/signal.py b/basic_pitch/layers/signal.py index f5f629f0..f05f671f 100644 --- a/basic_pitch/layers/signal.py +++ b/basic_pitch/layers/signal.py @@ -15,7 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Callable, Optional +from typing import Any, Callable, Optional, Tuple import tensorflow as tf from basic_pitch.layers.math import log_base_b @@ -62,7 +62,7 @@ def __init__( self.center = center self.pad_mode = pad_mode - def build(self, input_shape: tf.TensorShape) -> None: + def build(self, input_shape: Tuple[Optional[int], ...]) -> None: if self.window_length < self.fft_length: lpad = (self.fft_length - self.window_length) // 2 rpad = self.fft_length - self.window_length - lpad @@ -77,10 +77,11 @@ def padded_window(window_length: int, dtype: tf.dtypes.DType = tf.float32) -> tf self.final_window_fn = padded_window if self.center: + rank = len(input_shape) self.spec = tf.keras.layers.Lambda( lambda x: tf.pad( x, - [[0, 0] for _ in range(input_shape.rank - 1)] + [[self.fft_length // 2, self.fft_length // 2]], + [[0, 0] for _ in range(rank - 1)] + [[self.fft_length // 2, self.fft_length // 2]], mode=self.pad_mode, ) ) @@ -159,9 +160,9 @@ class NormalizedLog(tf.keras.layers.Layer): This layer adds 1e-10 to all values as a way to avoid NaN math. """ - def build(self, input_shape: tf.Tensor) -> None: + def build(self, input_shape: Tuple[Optional[int], ...]) -> None: self.squeeze_batch = lambda batch: batch - rank = input_shape.rank + rank = len(input_shape) if rank == 4: assert input_shape[1] == 1, "If the rank is 4, the second dimension must be length 1" self.squeeze_batch = lambda batch: tf.squeeze(batch, axis=1) diff --git a/basic_pitch/models.py b/basic_pitch/models.py index 867d9b3c..420c2d52 100644 --- a/basic_pitch/models.py +++ b/basic_pitch/models.py @@ -184,7 +184,7 @@ def get_cqt(inputs: tf.Tensor, n_harmonics: int, use_batchnorm: bool) -> tf.Tens bins_per_octave=12 * CONTOURS_BINS_PER_SEMITONE, )(x) x = signal.NormalizedLog()(x) - x = tf.expand_dims(x, -1) + x = tfkl.Lambda(lambda t: tf.expand_dims(t, -1))(x) if use_batchnorm: x = tfkl.BatchNormalization()(x) return x @@ -263,7 +263,7 @@ def model( x_contours = nn.FlattenFreqCh(name=contour_name)(x_contours) # contour output # reduced contour output as input to notes - x_contours_reduced = tf.expand_dims(x_contours, -1) + x_contours_reduced = tfkl.Lambda(lambda t: tf.expand_dims(t, -1))(x_contours) else: x_contours_reduced = x_contours diff --git a/basic_pitch/saved_models/icassp_2022/nmp/saved_model.pb b/basic_pitch/saved_models/icassp_2022/nmp/saved_model.pb index 3a22fc98..9e9cf467 100644 Binary files a/basic_pitch/saved_models/icassp_2022/nmp/saved_model.pb and b/basic_pitch/saved_models/icassp_2022/nmp/saved_model.pb differ diff --git a/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.data-00000-of-00001 b/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.data-00000-of-00001 index a473eb08..ac3c78e3 100644 Binary files a/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.data-00000-of-00001 and b/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.data-00000-of-00001 differ diff --git a/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.index b/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.index index dba4ca40..e5e7fd81 100644 Binary files a/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.index and b/basic_pitch/saved_models/icassp_2022/nmp/variables/variables.index differ diff --git a/basic_pitch/train.py b/basic_pitch/train.py index 50f01900..07586899 100644 --- a/basic_pitch/train.py +++ b/basic_pitch/train.py @@ -150,7 +150,6 @@ def main( model.compile( loss=loss, optimizer=tf.keras.optimizers.Adam(learning_rate), - sample_weight_mode={"contour": None, "note": None, "onset": None}, ) logging.info("--- Model Training specs ---") diff --git a/pyproject.toml b/pyproject.toml index e8dc15c3..696ed933 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,7 @@ name = "basic-pitch" version = "0.4.0" description = "Basic Pitch, a lightweight yet powerful audio-to-MIDI converter with pitch bend detection." readme = "README.md" +requires-python = ">=3.10" keywords = [] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -11,24 +12,21 @@ classifiers = [ "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Programming Language :: Python", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", ] dependencies = [ "coremltools; platform_system == 'Darwin'", "librosa>=0.8.0", "mir_eval>=0.6.0", - "numpy>=1.18", "onnxruntime; platform_system == 'Windows' and python_version < '3.11'", "pretty_midi>=0.2.9", "resampy>=0.2.2,<0.4.3", "scikit-learn", "scipy>=1.4.1", - "tensorflow>=2.4.1,<2.15.1; platform_system != 'Darwin' and python_version >= '3.11'", - "tensorflow-macos>=2.4.1,<2.15.1; platform_system == 'Darwin' and python_version > '3.11'", + "tensorflow>=2.16.0,<2.20.0;python_version >= '3.11'", "tflite-runtime; platform_system == 'Linux' and python_version < '3.11'", "typing_extensions", ] @@ -54,7 +52,7 @@ bp-download = "basic_pitch.data.download:main" [project.optional-dependencies] data = [ "basic_pitch[tf,test]", - "apache_beam", + "apache-beam>=2.50.0", "mirdata>=1.0.0", "smart_open", "sox", @@ -68,8 +66,7 @@ test = [ "mido" ] tf = [ - "tensorflow>=2.4.1,<2.15.1; platform_system != 'Darwin'", - "tensorflow-macos>=2.4.1,<2.15.1; platform_system == 'Darwin' and python_version > '3.7'", + "tensorflow>=2.16.0,<2.20.0" ] coreml = ["coremltools"] onnx = ["onnxruntime"] diff --git a/tests/data/conftest.py b/tests/data/conftest.py index 279f481e..08e1ebe9 100644 --- a/tests/data/conftest.py +++ b/tests/data/conftest.py @@ -12,21 +12,21 @@ SLAKH_TEST_INDEX = json.load(open(RESOURCES_PATH / "data" / "slakh" / "dummy_index.json")) -@pytest.fixture # type: ignore[misc] +@pytest.fixture # type: ignore[untyped-decorator] def mock_slakh_index() -> None: # type: ignore[misc] with mock.patch("mirdata.datasets.slakh.Dataset.download"): with mock.patch("mirdata.datasets.slakh.Dataset._index", new=SLAKH_TEST_INDEX): yield -@pytest.fixture # type: ignore[misc] +@pytest.fixture # type: ignore[untyped-decorator] def mock_medleydb_pitch_index() -> None: # type: ignore[misc] with mock.patch("mirdata.datasets.medleydb_pitch.Dataset.download"): with mock.patch("mirdata.datasets.medleydb_pitch.Dataset._index", new=MEDLEYDB_PITCH_TEST_INDEX): yield -@pytest.fixture # type: ignore[misc] +@pytest.fixture # type: ignore[untyped-decorator] def mock_maestro_index() -> None: # type: ignore[misc] index_with_metadata = MAESTRO_TEST_INDEX metadata = {mdata["midi_filename"].split(".")[0]: mdata for mdata in METADATA_TEST_INDEX} @@ -36,14 +36,14 @@ def mock_maestro_index() -> None: # type: ignore[misc] yield -@pytest.fixture # type: ignore[misc] +@pytest.fixture # type: ignore[untyped-decorator] def mock_guitarset_index() -> None: # type: ignore[misc] with mock.patch("mirdata.datasets.guitarset.Dataset.download"): with mock.patch("mirdata.datasets.guitarset.Dataset._index", new=GUITAR_SET_TEST_INDEX): yield -@pytest.fixture # type: ignore[misc] +@pytest.fixture # type: ignore[untyped-decorator] def mock_ikala_index() -> None: # type: ignore[misc] with mock.patch("mirdata.datasets.ikala.Dataset.download"): with mock.patch("mirdata.datasets.ikala.Dataset._index", new=IKALA_TEST_INDEX): diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 33a3af05..a522c458 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -56,6 +56,6 @@ def test_visualize_callback_on_epoch_end(tmpdir: str) -> None: contours=True, ) - vc.model = MockModel() + vc.set_model(MockModel()) vc.on_epoch_end(1, {"loss": np.random.random(), "val_loss": np.random.random()}) diff --git a/tests/test_nn.py b/tests/test_nn.py index 7e2b1c05..bbb06403 100644 --- a/tests/test_nn.py +++ b/tests/test_nn.py @@ -67,7 +67,6 @@ def test_defaults(self) -> None: model.compile( loss="binary_crossentropy", optimizer=tf.keras.optimizers.Adam(0.1), - sample_weight_mode=None, ) model.fit( @@ -102,7 +101,6 @@ def test_fractions(self) -> None: model.compile( loss="binary_crossentropy", optimizer=tf.keras.optimizers.Adam(0.1), - sample_weight_mode=None, ) model.fit( diff --git a/tox.ini b/tox.ini index e22a6afc..fe8a65d0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38,py39,py310,py311,full,data,manifest,check-formatting,lint,mypy +envlist = py312,py311,py310,full,data,manifest,check-formatting,lint,mypy skipsdist = True usedevelop = True requires =