НЕ ПИШИТЕ НИЧЕГО, ПОКА Я ВАМ НЕ СКАЖУ!!!!!!!!!:

import torch
from torch import nn, optim
from torch.utils.data import DataLoader, Dataset
import torchaudio
import torchaudio.transforms as transforms
import librosa
import numpy as np
import crepe
import madmom
from music21 import stream, note, converter
from sklearn.preprocessing import normalize
from transformers import Wav2Vec2ForCTC, Wav2Vec2Tokenizer, HubertForCTC, HubertTokenizer, GPT2LMHeadModel, GPT2Tokenizer
from scipy import signal
import openunmix
from demucs import pretrained, Demucs
import openai
import yamnet as yamnet_model
import openl3
from onsets_and_frames import NonBinaryClassification, inference
import pydub
import parselmouth
from spleeter.separator import Separator
import soundfile as sf

# Установка ключа API OpenAI
openai.api_key = “your_openai_api_key_here”

# 1. Предобработка аудио
def preprocess_audio(file_path):
    “”“
    Загрузка и нормализация аудиосигнала
    “””
    y, sr = librosa.load(file_path, sr=None)
    y = librosa.util.normalize(y)
    return y, sr

# 2. Преобразование форматов аудио с использованием pydub
def convert_audio_format(file_path, target_format=“wav”):
    audio = pydub.AudioSegment.from_file(file_path)
    new_file_path = file_path.replace(file_path.split(‘.’)[-1], target_format)
    audio.export(new_file_path, format=target_format)
    return new_file_path

# 3. Воспроизведение аудио с использованием soundfile
def play_audio(file_path):
    data, sr = sf.read(file_path)
    import sounddevice as sd
    sd.play(data, sr)
    sd.wait()

# 4. Извлечение признаков
def extract_features(y, sr):
    “”“
    Извлечение признаков из аудиосигнала
    “””
    cqt = librosa.cqt(y, sr=sr)
    cqt_db = librosa.amplitude_to_db(np.abs(cqt), ref=np.max)
    
    # Pitch detection using CREPE
    _, frequency, confidence, _ = crepe.predict(y, sr)
    
    return cqt_db, frequency, confidence

# Дополнительное извлечение признаков с использованием Essentia
def additional_features_essentia(y, sr):
    from essentia.standard import MusicExtractor, MonoLoader
    loader = MonoLoader(sampleRate=sr)
    y = loader(y)
    extractor = MusicExtractor(lowlevelStats=[‘mean’, ‘stdev’])
    features, _ = extractor(y)
    return features

# Визуализация аудиоспектрограммы
def plot_spectrogram(y, sr, output_image_path):
    import matplotlib.pyplot as plt
    import librosa.display
    
    D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)
    plt.figure(figsize=(12, 8))
    librosa.display.specshow(D, sr=sr, x_axis=‘time’, y_axis=‘log’)
    plt.colorbar(format=‘%+2.0f dB’)
    plt.title(‘Spectrogram’)
    plt.savefig(output_image_path)
    print(f’Spectrogram saved to {output_image_path}‘)
    plt.close()

# Визуализация хромограммы
def plot_chromagram(y, sr, output_image_path):
    import matplotlib.pyplot as plt
    import librosa.display
    
    chromagram = librosa.feature.chroma_stft(y=y, sr=sr)
    plt.figure(figsize=(12, 8))
    librosa.display.specshow(chromagram, sr=sr, x_axis=‘time’, y_axis=‘chroma’)
    plt.colorbar(format=’%+2.0f’)
    plt.title(‘Chromagram’)
    plt.savefig(output_image_path)
    print(f’Chromagram saved to {output_image_path}‘)
    plt.close()

# Использование Transformer моделей для извлечения признаков
def transformer_features_wav2vec2(y, sr):
    tokenizer = Wav2Vec2Tokenizer.from_pretrained(“facebook/wav2vec2-base-960h”)
    model = Wav2Vec2ForCTC.from_pretrained(“facebook/wav2vec2-base-960h”)
    
    input_values = tokenizer(y, return_tensors=“pt”, padding=True).input_values
    logits = model(input_values).logits
    predicted_ids = torch.argmax(logits, dim=-1)
    
    transcription = tokenizer.batch_decode(predicted_ids)[0]
    return transcription

def transformer_features_hubert(y, sr):
    tokenizer = HubertTokenizer.from_pretrained(“facebook/hubert-large-ls960-ft”)
    model = HubertForCTC.from_pretrained(“facebook/hubert-large-ls960-ft”)
    
    input_values = tokenizer(y, return_tensors=“pt”, padding=True).input_values
    logits = model(input_values).logits
    predicted_ids = torch.argmax(logits, dim=-1)
    
    transcription = tokenizer.batch_decode(predicted_ids)[0]
    return transcription

# Глубокие сверточные нейронные сети (CNN) для анализа аудиоспектрограммы
class AudioCNN(nn.Module):
    def init(self, in_channels, out_channels):
        super(AudioCNN, self).init()
        self.conv1 = nn.Conv2d(in_channels, 16, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32 * 16 * 16, 128)
        self.fc2 = nn.Linear(128, out_channels)
        
    def forward(self, x):
        x = torch.relu(self.conv1(x))
        x = self.pool(x)
        x = torch.relu(self.conv2(x))
        x = self.pool(x)
        x = x.view(-1, 32 * 16 * 16)
        x = torch.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# Алгоритмы сепарации источников (OpenUnmix)
def source_separation_openunmix(y, sr):
    separator = openunmix.separate(
                audio_signal = y.tolist(),
                sample_rate = sr,
                targets=[“vocals”, “drums”, “bass”, “other”]
            )
    return {
        “vocals”: separator[“vocals”],
        “drums”: separator[“drums”],
        “bass”: separator[“bass”],
        “other”: separator[“other”],
    }

# Сепарация источников (Demucs v3)
def source_separation_demucs_v3(y, sr):
    model = pretrained.get_model(‘demucs’)
    
    waveform = torch.tensor(y).unsqueeze(0)
    waveform = waveform.to(model.device)
    
    sources = model(waveform)
    return sources

# Сепарация источников с использование Spleeter
def source_separation_spleeter(file_path):
    separator = Separator(‘spleeter:2stems’)
    separator.separate_to_file(file_path, output_path=‘output’)
    return ‘output/vocals.wav’, ‘output/accompaniment.wav’

# Использование Generative Adversarial Networks (GAN) для генерации музыкальных нотаций
class MusicGAN(nn.Module):
    def init(self, input_size, hidden_size, output_size):
        super(MusicGAN, self).init()
        self.generator = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(True),
            nn.Linear(hidden_size, output_size),
            nn.Tanh()
        )
        self.discriminator = nn.Sequential(
            nn.Linear(output_size, hidden_size),
            nn.ReLU(True),
            nn.Linear(hidden_size, 1),
            nn.Sigmoid()
        )
    
    def forward(self, x):
        return self.generator(x)

# Использование Wave-U-Net для аудиоинверсии
class WaveUNet(nn.Module):
    def init(self, in_channels, out_channels):
        super(WaveUNet, self).init()
        self.encoder = nn.ModuleList([
            nn.Sequential(
                nn.Conv1d(in_channels, 32, kernel_size=15, padding=7),
                nn.ReLU(True)
            ),
            nn.MaxPool1d(2)
        ])
        self.decoder = nn.ModuleList([
            nn.Sequential(
                nn.ConvTranspose1d(32, out_channels, kernel_size=15, padding=7),
                nn.ReLU(True)
            )
        ])
    
    def forward(self, x):
        for layer in self.encoder:
            x = layer(x)
        for layer in self.decoder:
            x = layer(x)
        return x

# Самообучающиеся модели (Reinforcement Learning Based Music Generation)
class MusicRLModel(nn.Module):
    def init(self, input_size, hidden_size, output_size):
        super(MusicRLModel, self).init()
        self.hidden_size = hidden_size
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        h0 = torch.zeros(1, x.size(0), self.hidden_size).to(x.device)
        c0 = torch.zeros(1, x.size(0), self.hidden_size).to(x.device)
        out, _ = self.lstm(x, (h0, c0))
        out = self.fc(out[:, -1, :])
        return out

# Продвинутое обучение автоэнкодеров (Variational Autoencoders for Music)
class MusicVAE(nn.Module):
    def init(self, input_size, hidden_size, latent_size):
        super(MusicVAE, self).init()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2_mean = nn.Linear(hidden_size, latent_size)
        self.fc2_logvar = nn.Linear(hidden_size, latent_size)
        self.fc3 = nn.Linear(latent_size, hidden_size)
        self.fc4 = nn.Linear(hidden_size, input_size)

    def encode(self, x):
        h1 = torch.relu(self.fc1(x))
        return self.fc2_mean(h1), self.fc2_logvar(h1)

    def decode(self, z):
        h3 = torch.relu(self.fc3(z))
        return torch.sigmoid(self.fc4(h3))

    def reparameterize(self, mu, logvar):
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        return mu + eps * std

    def forward(self, x):
        mu, logvar = self.encode(x)
        z = self.reparameterize(mu, logvar)
        return self.decode(z), mu, logvar

def vae_loss(recon_x, x, mu, logvar):
    BCE = nn.functional.binary_cross_entropy(recon_x, x, reduction=‘sum’)
    KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
    return BCE + KLD

# Энкодер на основе трансформера для ожидания и формирования различий (Onsets and Frames)
def onsets_frames(y):
    # Пример применения onsets-and-frames
    device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)
    model = NonBinaryClassification(num_layers=4).to(device)
    
    frames, onsets, offsets, velocity = inference(y, model, device)
    
    return frames, onsets, offsets, velocity

# Гармонический анализ и ритм
def harmonic_rhythm_analysis(y, sr):
    tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr)
    chords = librosa.effects.harmonic(y)
    
    # Анализ ритма с использованием madmom
    proc = madmom.features.beats.RNNBeatProcessor()(y)
    beat_times = madmom.features.beats.DBNBeatTrackingProcessor(beats_per_bar=[3, 4])(proc)
    
    return tempo, beat_frames, chords, beat_times

# Генерация текстовых описаний музыкальных фрагментов с использованием ChatGPT
def generate_text_descriptions(features):
    response = openai.Completion.create(
        engine=“davinci-codex”,
        prompt=f"Generate a coherent description based on the following features: {features}“,
        max_tokens=100
    )
    return response.choices[0].text.strip()

# Распознавание музыкальных инструментов с использованием YAMNet
def recognize_instruments(y, sr):
    params = yamnet_model.Params()
    yamnet = yamnet_model.yamnet(params)
    yamnet.load_weights(‘yamnet.h5’)

    waveform = np.reshape(y, [len(y), 1])
    
    class_scores, embeddings, spectrogram = yamnet(waveform)
    
    classes = yamnet_model.class_names(params)
    top_classes = np.argsort(class_scores, axis=-1)[0, -5:][::-1]
    recognized_classes = [classes[i] for i in top_classes]
    
    return recognized_classes

# Распознавание музыкальных инструментов с использованием OpenL3
def recognize_instruments_openl3(y, sr):
    model = openl3.models.load_audio_embedding_model(input_repr=“mel256”, content_type=“music”, embedding_size=512)
    embeddings, timestamps = openl3.get_audio_embedding(y, sr, model=model, hop_size=0.1)
    
    return embeddings

# Анализ высоты тона с использованием Parselmouth
def analyze_pitch_with_parselmouth(file_path):
    snd = parselmouth.Sound(file_path)
    pitch = snd.to_pitch()
    pitch_values = pitch.selected_array[‘frequency’]
    return pitch_values

### Дополнительные улучшенные функции

#### Фильтрация шумов, изменение тембра и скорости, нормализация громкости

# Оцифровка и снижение уровня шума с использованием pydub
def reduce_noise(file_path, noise_reduction_level=5):
    audio = pydub.AudioSegment.from_file(file_path)
    reduced_noise_audio = audio - noise_reduction_level
    reduced_noise_file_path = file_path.replace(”.wav", “_reduced_noise.wav”)
    reduced_noise_audio.export(reduced_noise_file_path, format=“wav”)
    return reduced_noise_file_path

# Изменение тембра и скорости с использованием pydub
def change_timbre_and_speed(file_path, pitch_shift=2, speed_change=1.5):
    audio = pydub.AudioSegment.from_file(file_path)
    new_timbre_audio = audio._spawn(audio.raw_data, overrides={‘frame_rate’: int(audio.frame_rate * pitch_shift)})
    new_timbre_speed_audio = new_timbre_audio.speedup(playback_speed=speed_change)
    new_file_path = file_path.replace(“.wav”, “_modified.wav”)
    new_timbre_speed_audio.export(new_file_path, format=“wav”)
    return new_file_path

# Нормализация уровней громкости с использованием pydub
def normalize_volume_level(file_path):
    audio = pydub.AudioSegment.from_file(file_path)
    normalized_audio = audio.apply_gain(-audio.dBFS)
    normalized_file_path = file_path.replace(“.wav”, “_normalized.wav”)
    normalized_audio.export(normalized_file_path, format=“wav”)
    return normalized_file_path

# Очищение шумов с использованием scipy
def denoise_audio(file_path, noise_reduction_level=0.5):
    y, sr = librosa.load(file_path, sr=None)
    reduced_noise_y = signal.wiener(y, noise_reduction_level)
    reduced_noise_file_path = file_path.replace(“.wav”, “_reduced_noise.wav”)
    sf.write(reduced_noise_file_path, reduced_noise_y, sr)
    return reduced_noise_file_path

# Вычисление спектрального центроида
def compute_spectral_centroid(y, sr):
    spectral_centroids = librosa.feature.spectral_centroid(y, sr=sr)[0]
    return spectral_centroids

# Вычисление тональности и тональной стабильности
def compute_tonality_and_stability(y, sr):
    chroma_stft = librosa.feature.chroma_stft(y=y, sr=sr)
    key = librosa.estimate_tuning(y=y, sr=sr)
    return key, chroma_stft

# Построение MusicXML
def create_musicxml(frequencies, beats, frames, onsets, offsets, text_descriptions, instruments):
    score = stream.Score()
    part = stream.Part()
    
    for freq, beat, frame, onset, offset, description, instrument in zip(frequencies, beats, frames, onsets, offsets, text_descriptions, instruments):
        if onset > 0:
            n = note.Note()
            n.pitch.frequency = freq
            n.quarterLength = beat
            n.offset = offset
            n.addLyric(description)
            n.addLyric(instrument)
            part.append(n)
    
    score.append(part)
    return score

def visualize_score(score, output_image_path):
    “”“
    Визуализация партитуры и сохранение в виде изображения
    “””
    s = converter.parse(score)
    s.show(‘text’)  # Показать партитуру в текстовом виде (можно удалить, если не нужно)
    
    fp = s.write(‘musicxml.png’, fp=output_image_path)
    print(f’Партитура сохранена в {fp}’)

# Класс данных для обучения (например, для глубоких нейронных сетей)
class MusicDataset(Dataset):
    def init(self, features, labels):
        self.features = features
        self.labels = labels
    
    def len(self):
        return len(self.features)
    
    def getitem(self, idx):
        return self.features[idx], self.labels[idx]

# Основная функция для обучения и тестирования моделей (например, CNN)
def train_cnn_model(train_loader, in_channels, out_channels, epochs=10):
    model = AudioCNN(in_channels, out_channels)
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    
    for epoch in range(epochs):
        for inputs, labels in train_loader:
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            
        print(f"Epoch {epoch+1}/{epochs}, Loss: {loss.item()}")
    
    return model

# Дополнительная функция для распознавания жанров с помощью ChatGPT
def recognize_genre(y, sr):
    features = extract_features(y, sr)
    genre_description = generate_text_descriptions(features)
    return genre_description

# Логика добавления и удаления нот на основе ChatGPT
def modify_notes_with_chatgpt(score, action=“add”, instrument=“piano”):
    notes_data = [(n.pitch.frequency, n.quarterLength, n.offset) for n in score.flat.notes]
    prompt = f"The score contains the following notes data:\n{notes_data}\nPlease provide necessary modifications to {‘add’ if action == ‘add’ else ‘remove’} notes for the instrument ‘{instrument}’.“
    
    response = openai.Completion.create(
        engine=“davinci-codex”,
        prompt=prompt,
        max_tokens=150
    )
    
    modified_notes = response.choices[0].text.strip().split(”\n")
    
    for note_data in modified_notes:
        freq, length, offset = map(float, note_data.split(‘,’))
        if action == “add”:
            new_note = note.Note()
            new_note.pitch.frequency = freq
            new_note.quarterLength = length
            new_note.offset = offset
            new_note.addLyric(instrument)
            score.append(new_note)
        else:
            for n in score.flat.notes:
                if abs(n.pitch.frequency - freq) < 1e-3 and abs(n.quarterLength - length) < 1e-3 and abs(n.offset - offset) < 1e-3:
                    score.remove(n)
    
    return score

def main(file_path, output_path, output_image_path):
    # Конвертация аудио в подходящий формат
    converted_file_path = convert_audio_format(file_path)
    
    # Предварительная обработка
    y, sr = preprocess_audio(converted_file_path)
    
    # Извлечение признаков
    cqt_db, frequency, confidence = extract_features(y, sr)

    # Дополнительные признаки с использованием Essentia
    additional_features = additional_features_essentia(y, sr)

    # Признаки с использованием Transformer моделей
    transcription_wav2vec2 = transformer_features_wav2vec2(y, sr)
    transcription_hubert = transformer_features_hubert(y, sr)

    # Сепарация источников (OpenUnmix и Demucs v3)
    sources_openunmix = source_separation_openunmix(y, sr)
    sources_demucs_v3 = source_separation_demucs_v3(y, sr)
    
    # Сепарация источников с использование Spleeter
    vocals_path, accompaniment_path = source_separation_spleeter(converted_file_path)
    
    # Применение фильтрации шумов
    noise_reduced_path = reduce_noise(converted_file_path)
    
    # Изменение тембра и скорости
    modified_audio_path = change_timbre_and_speed(noise_reduced_path)
    
    # Нормализация уровня громкости
    normalized_audio_path = normalize_volume_level(modified_audio_path)
    
    # Модели глубокого обучения и автоэнкодеры (VAE)
    frames_vae, onsets_vae, offsets_vae, velocity_vae = onsets_frames(y)
    
    # Гармонический анализ и ритм
    tempo, beat_frames, chords, beat_times = harmonic_rhythm_analysis(y, sr)
    
    # Генерация текстовых описаний музыкальных фрагментов с использованием ChatGPT
    text_descriptions = generate_text_descriptions(additional_features)
    
    # Распознавание музыкальных инструментов
    instruments = recognize_instruments(y, sr)
    embeddings_openl3 = recognize_instruments_openl3(y, sr)

    # Распознавание жанра музыки
    genre_description = recognize_genre(y, sr)
    
    # Спектральный центроид
    spectral_centroid = compute_spectral_centroid(y, sr)
    
    # Тональность и тональная стабильность
    key, chroma_stft = compute_tonality_and_stability(y, sr)
    
    # Генерация нотных данных (вместо реального обучения, тут пример как это можно использовать)
    # Подготовка данных и обучение модели (например, CNN)
    # features = …
    # labels = …
    # train_dataset = MusicDataset(features, labels)
    # train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
    # cnn_model = train_cnn_model(train_loader, in_channels=…, out_channels=…)

    # Пример Generating MusicXML из результатов VAE и анализов
    score = create_musicxml(frequency, beat_times, frames_vae, onsets_vae, offsets_vae, text_descriptions, instruments)
    
    # Логика добавления или удаления нот с использованием ChatGPT
    score = modify_notes_with_chatgpt(score, action=“add”, instrument=“piano”)
    
    # Сохранение и визуализация партитуры в формате MusicXML и изображения
    score.write(‘musicxml’, fp=output_path)
    visualize_score(score, output_image_path)

    # Визуализация спектрограммы аудиофайла
    spectrogram_image_path = output_image_path.replace(“.png”, “_spectrogram.png”)
    plot_spectrogram(y, sr, spectrogram_image_path)

    # Визуализация хромограммы аудиофайла
    chromagram_image_path = output_image_path.replace(“.png”, “_chromagram.png”)
    plot_chromagram(y, sr, chromagram_image_path)
    
    # Демонстрация воспроизведения аудио
    play_audio(normalized_audio_path)
    
    # Анализ высоты тона с использованием parselmouth
    pitch_analysis = analyze_pitch_with_parselmouth(normalized_audio_path)
    print(“Pitch analysis (Parselmouth):”, pitch_analysis)
    
    # Окончательное описание всех источников
    full_description = {
        “Transcription (Wav2Vec2)”: transcription_wav2vec2,
        “Transcription (HuBERT)”: transcription_hubert,
        “Genre Description (ChatGPT)”: genre_description,
        “Instrument Recognition (YAMNet)”: instruments,
        “Additional Features (Essentia)”: additional_features,
        “Pitch Analysis (Parselmouth)”: pitch_analysis,
        “Spectral Centroid”: spectral_centroid,
        “Key (Tuning)”: key,
        “Chroma (Tonality Stability)”: chroma_stft
    }
    
    print(“Full Description:”)
    for key, value in full_description.items():
        print(f"{key}: {value}“)

# Запуск скрипта
if name == “main”:
    file_path = ‘path_to_your_mp3_file.mp3’
    output_path = ‘output_musicxml_file.xml’
    output_image_path = ‘output_image.png’
    main(file_path, output_path, output_image_path)


### Дополнительные улучшенные функции

#### Фильтрация шумов, изменение тембра и скорости, нормализация громкости

# Оцифровка и снижение уровня шума с использованием pydub
def reduce_noise(file_path, noise_reduction_level=5):
    audio = pydub.AudioSegment.from_file(file_path)
    reduced_noise_audio = audio - noise_reduction_level
    reduced_noise_file_path = file_path.replace(”.wav", “_reduced_noise.wav”)
    reduced_noise_audio.export(reduced_noise_file_path, format=“wav”)
    return reduced_noise_file_path

# Изменение тембра и скорости с использованием pydub
def change_timbre_and_speed(file_path, pitch_shift=2, speed_change=1.5):
    audio = pydub.AudioSegment.from_file(file_path)
    new_timbre_audio = audio._spawn(audio.raw_data, overrides={‘frame_rate’: int(audio.frame_rate * pitch_shift)})
    new_timbre_speed_audio = new_timbre_audio.speedup(playback_speed=speed_change)
    new_file_path = file_path.replace(“.wav”, “_modified.wav”)
    new_timbre_speed_audio.export(new_file_path, format=“wav”)
    return new_file_path

# Нормализация уровней громкости с использованием pydub
def normalize_volume_level(file_path):
    audio = pydub.AudioSegment.from_file(file_path)
    normalized_audio = audio.apply_gain(-audio.dBFS)
    normalized_file_path = file_path.replace(“.wav”, “_normalized.wav”)
    normalized_audio.export(normalized_file_path, format=“wav”)
    return normalized_file_path


#### Очищение шумов с использованием scipy

from scipy import signal

# Очищение шумов с использованием scipy
def denoise_audio(file_path, noise_reduction_level=0.5):
    y, sr = librosa.load(file_path, sr=None)
    reduced_noise_y = signal.wiener(y, noise_reduction_level)
    reduced_noise_file_path = file_path.replace(“.wav”, “_reduced_noise.wav”)
    sf.write(reduced_noise_file_path, reduced_noise_y, sr)
    return reduced_noise_file_path


#### Вычисление спектрального центроида

def compute_spectral_centroid(y, sr):
    spectral_centroids = librosa.feature.spectral_centroid(y, sr=sr)[0]
    return spectral_centroids


#### Тональность и тональная стабильность

def compute_tonality_and_stability(y, sr):
    chroma_stft = librosa.feature.chroma_stft(y=y, sr=sr)
    key = librosa.estimate_tuning(y=y, sr=sr)
    return key, chroma_stft


#### Визуализация хромограммы

def plot_chromagram(y, sr, output_image_path):
    import matplotlib.pyplot as plt
    import librosa.display
    
    chromagram = librosa.feature.chroma_stft(y=y, sr=sr)
    plt.figure(figsize=(12, 8))
    librosa.display.specshow(chromagram, sr=sr, x_axis=‘time’, y_axis=‘chroma’)
    plt.colorbar(format=‘%+2.0f’)
    plt.title(‘Chromagram’)
    plt.savefig(output_image_path)
    print(f’Chromagram saved to {output_image_path}')
    plt.close()