improve script berikut ini agar dapat menghasilkan suara midi yang merdua dan sempurna dari file audio yang di download dari spotify, bunyi untuk midi nya tidak harus untuk semua musik yg ada pada audio, setidak nya midi yang di hasilkan dapat melambangkan bunyi dari musik audio tersebut, jadi setidak nya mendengar bunyi midi yang di dapatkan sudah merasa mendengarkan musik tersebut lewat piano, buatkan dengan benar dan lengkap : import os
import subprocess
import shutil
import tempfile
import librosa
import soundfile as sf
from basic_pitch.inference import predict_and_save
from basic_pitch import ICASSP_2022_MODEL_PATH
import warnings
import pretty_midi  # For advanced MIDI processing

warnings.filterwarnings("ignore")  # Suppress warnings for cleaner output

def download_song_from_spotify(song_query):
    """Download song from Spotify using spotdl."""
    tmpdirname = tempfile.mkdtemp()
    try:
        print("Starting song download...")
        # Update the spotdl command to match the latest syntax
        subprocess.run([
            'spotdl', song_query,
            '--output', os.path.join(tmpdirname, '{artist} - {title}.{output-ext}'),
            '--format', 'mp3' # Ensure the output format is mp3
        ], check=True)
        
        # Fetch the downloaded song
        downloaded_files = os.listdir(tmpdirname)
        if downloaded_files:
            song_file = os.path.join(tmpdirname, downloaded_files[0])
            print(f"Song downloaded successfully: {downloaded_files[0]}")
            return song_file, downloaded_files[0], tmpdirname  # Return song path, song file name, and temp directory
        else:
            print("Failed to download song.")
            return None, None, tmpdirname
    except subprocess.CalledProcessError as e:
        print(f"An error occurred while downloading the song: {e}")
        return None, None, tmpdirname

def convert_audio_to_wav(input_audio_file):
    """Convert audio file to WAV format with standard settings."""
    try:
        print("Converting audio to WAV format...")
        y, sr = librosa.load(input_audio_file, sr=44100, mono=True)
        wav_file = os.path.splitext(input_audio_file)[0] + '.wav'
        sf.write(wav_file, y, sr, subtype='PCM_16')
        print(f"Audio converted to WAV: {wav_file}")
        return wav_file, sr
    except Exception as e:
        print(f"An error occurred during audio conversion: {e}")
        return None, None

def normalize_audio(y):
    """Normalize the audio signal to have consistent volume."""
    try:
        print("Normalizing audio...")
        y_norm = librosa.util.normalize(y)
        print("Audio normalization complete.")
        return y_norm
    except Exception as e:
        print(f"An error occurred during audio normalization: {e}")
        return y

def reduce_noise(y):
    """Reduce noise in the audio signal."""
    try:
        print("Reducing noise...")
        # Apply harmonic-percussive source separation to emphasize harmonic content
        y_harmonic, _ = librosa.effects.hpss(y)
        print("Noise reduction complete.")
        return y_harmonic
    except Exception as e:
        print(f"An error occurred during noise reduction: {e}")
        return y

def audio_to_midi_with_basic_pitch(audio_file, output_dir, song_name):
    """Convert audio file to MIDI using Basic Pitch."""
    if not os.path.exists(audio_file):
        print(f"Error: Audio file {audio_file} not found.")
        return
    
    try:
        print(f"Converting audio to MIDI using Basic Pitch...")
        # Load audio
        y, sr = librosa.load(audio_file, sr=None, mono=True)
        
        # Normalize audio
        y = normalize_audio(y)
        
        # Reduce noise
        y = reduce_noise(y)
        
        # Save preprocessed audio
        sf.write(audio_file, y, sr, subtype='PCM_16')
        print("Preprocessed audio saved.")
        
        # Set Basic Pitch parameters to improve accuracy
        onset_threshold = 0.6
        frame_threshold = 0.4
        minimum_note_length = 50  # In milliseconds
        
        # Run inference and save MIDI file
        predict_and_save(
            [audio_file],  # List of audio files
            output_directory=output_dir,  # Where to save outputs
            save_midi=True,  # Save the MIDI file
            sonify_midi=False,  # Disable sonification
            save_model_outputs=False,  # Skip saving model outputs
            save_notes=False,  # Skip saving detected notes
            model_or_model_path=ICASSP_2022_MODEL_PATH,
            onset_threshold=onset_threshold,
            frame_threshold=frame_threshold,
            minimum_note_length=minimum_note_length,
        )
        
        # The actual MIDI file created will have the format: original_file_name_basic_pitch.mid
        base_name = os.path.splitext(os.path.basename(audio_file))[0]  # Extract the base name of the audio file
        midi_file = os.path.join(output_dir, f"{base_name}_basic_pitch.mid")
        
        # Remove any file extension (e.g., .mp3) from the song name before renaming
        clean_song_name = os.path.splitext(song_name)[0]
        new_midi_file = os.path.join(output_dir, f"{clean_song_name}_piano.mid")
        os.rename(midi_file, new_midi_file)
        
        print(f"Conversion complete. MIDI file saved as {new_midi_file}.")
        
        # Optional: Post-process MIDI to enhance quality
        post_process_midi(new_midi_file)
        
        # Delete temporary WAV file
        if os.path.exists(audio_file):
            os.remove(audio_file)
            print(f"Deleted temporary WAV file: {audio_file}")
        
    except Exception as e:
        print(f"An error occurred during audio to MIDI conversion: {e}")

def post_process_midi(midi_file):
    """Adjust MIDI velocities and quantize notes using pretty_midi."""
    try:
        print("Post-processing MIDI file to enhance quality...")
        midi_data = pretty_midi.PrettyMIDI(midi_file)
        
        # Get tempo changes (if any)
        tempo_changes_times, tempo_changes = midi_data.get_tempo_changes()
        
        # For simplicity, we're going to assume the first tempo
        if len(tempo_changes) > 0:
            bpm = tempo_changes[0]
        else:
            bpm = 120  # Default tempo if no tempo changes are detected
        
        # Compute time per quarter note (seconds per quarter note)
        spq = 60 / bpm  # Seconds per quarter note
        
        # Compute time per 64th note
        quantization_unit = spq / 16  # 64th note duration in seconds
        
        for instrument in midi_data.instruments:
            # Optionally, set all instruments to a piano sound
            instrument.program = pretty_midi.instrument_name_to_program('Acoustic Grand Piano')
            instrument.is_drum = False
            for note in instrument.notes:
                # Quantize note start and end times
                note.start = round(note.start / quantization_unit) * quantization_unit
                note.end = round(note.end / quantization_unit) * quantization_unit
                # Ensure note.end is after note.start
                if note.end <= note.start:
                    note.end = note.start + quantization_unit  # Set to at least one quantization unit
                # Adjust velocities
                note.velocity = min(127, max(60, note.velocity))  # Set a minimum velocity
                
        # Remove unnecessary control changes
        midi_data.control_changes = []
        
        # Save the modified MIDI file
        midi_data.write(midi_file)
        print("MIDI post-processing complete. Enhanced MIDI saved.")
        
    except Exception as e:
        print(f"An error occurred during MIDI post-processing: {e}")

def clean_up(temp_dirs):
    """Clean up temporary directories."""
    for tmpdirname in temp_dirs:
        if os.path.exists(tmpdirname):
            shutil.rmtree(tmpdirname)
            print(f"Deleted temporary directory: {tmpdirname}")

# ---- Main Program ----
if __name__ == "__main__":
    song_query = input("Enter the song title or Spotify link: ")
    
    temp_dirs = []  # Keep track of temporary directories to clean up
    
    # Step 1: Download the song
    downloaded_song, song_name, tmpdirname = download_song_from_spotify(song_query)
    temp_dirs.append(tmpdirname)
    
    if downloaded_song:
        # Step 2: Convert audio to WAV
        wav_file, sr = convert_audio_to_wav(downloaded_song)
        if wav_file:
            # Step 3: Convert audio to MIDI
            current_dir = os.path.dirname(os.path.abspath(__file__))
            output_dir = os.path.join(current_dir, 'songs')
            
            if not os.path.exists(output_dir):
                os.makedirs(output_dir)  # Create the 'songs' directory if it doesn't exist
        
            audio_to_midi_with_basic_pitch(wav_file, output_dir, song_name)
        else:
            print("Failed to convert audio to WAV.")
        
        # Clean up temporary directories
        clean_up(temp_dirs)
    else:
        print("Failed to download the song.")