Über Web, Tech, Games, Art,
Musik, Code & Design

20. November 2024

Sampler für lange Samples

Auf der Suche nach einem Sampler / Sampleplayer bzw. Rompler, der lange Samples abspielen kann musste ich überrascht feststellen: Es gibt kaum ein Gerät, das diese Anforderungen erfüllt.

Gesucht habe ich ein Gerät, das mit Hilfe eines MIDI-Keyboards gesteuert werden kann und mehrere Audiofiles – idealerweise mp3s – gleichzeitig abspielen kann. Dabei sollte es keine Rolle spielen, ob die Sounds 3 Sekunden oder 10 Minuten lang sind. Eines der wenigen Geräte, das mit langen Audiofiles umgehen kann, scheint die „1010music Blackbox“ zu sein. Die ist allerdings mit 650€ nicht gerade ein Schnäppchen.

Bei meiner weiteren Suche bin auf ein spannendes Projekt namens „SamplerBox“ gestoßen.

„SamplerBox“ ist eine Software, die auf einem Raspberry Pi installiert werden kann. Samples können direkt von einem USB-Stick aus abgespielt werden. Hierfür müssen Sie lediglich durchnummeriert werden. Das C3 hat z.B. die MIDI-Note-Nr. 48 – also lautet der Dateiname 48.wav.

Eigentlich genau das, was ich gesucht habe – auch wenn SamplerBox eher als „Instrument“ gedacht ist und die Samples im Sinne des Entwicklers nur aus einem einzigen Ton bestehen. Problem ist hierbei – zumindest in meinem Fall -, dass die Samples nur so lange gespielt werden, wie die Taste gehalten wird.

Also: Selber machen! 🙂

Mit Hilfe von ChatGPT habe ich ein Python-Skript entwickelt, das schon ziemlich genau meinen Anforderungen entspricht:

  • Samples können als mp3 geladen werden
  • auch lange Samples können abgespielt werden
  • Steuerung erfolgt über ein MIDI-Keyboard
  • Samples sind nach Notennamen benannt (z.B. C3.mp3)
  • Samples können gestoppt werden (Doppelklick)

Auf dem Mac funktioniert das Skript bislang ganz gut. Geplant ist, das Skript auf einem Raspberry Pi laufen zu lassen. Sobald ich das gemacht habe, werde ich die Erfahrungen hier teilen.

So kannst du das Skript auf dem Mac nutzen:

Öffne das Terminal und installiere „pygame“.

pip3 install pygame

Speichere das untenstehende Skript als sampler.py. Kopiere die Samples in das gleiche Verzeichnis wie „sampler.py“. Navigiere im Terminal in das Verzeichnis. z.B.:

cd musik/dev

Starte das Skript mit dem Befehl

python3 sampler.pyCode-Sprache: CSS (css)

Wenn ein MIDI-Keyboard angeschlossen ist, kannst du damit jetzt Samples abspielen.

import pygame.midi
import pygame.mixer
import os
import time

DOUBLE_CLICK_THRESHOLD = 0.3

note_channels = {}
note_last_press_time = {}

loaded_sounds = {}

def initialize_midi():
    pygame.midi.init()
    midi_device_id = None
    for i in range(pygame.midi.get_count()):
        info = pygame.midi.get_device_info(i)
        if info[2] == 1:  # Überprüfen, ob das Gerät ein Eingabegerät ist
            midi_device_id = i
            print(f"MIDI-Gerät gefunden: {info[1].decode()} (ID: {i})")
            break

    if midi_device_id is None:
        raise Exception("Kein MIDI-Eingabegerät gefunden.")
    
    return pygame.midi.Input(midi_device_id)

def initialize_audio(buffer_size=1024):
    pygame.mixer.pre_init(44100, -16, 2, buffer_size)  # Erhöht die Puffergröße
    pygame.mixer.init()
    pygame.mixer.set_num_channels(64)  # Mehr Kanäle erlauben
    print("Audio-System initialisiert.")

def preload_samples():
     
    for file in os.listdir():
        if file.endswith(".mp3"):
            note_name = os.path.splitext(file)[0]
            loaded_sounds[note_name] = pygame.mixer.Sound(file)
            print(f"Geladen: {file}")

def play_or_stop_sample(note_name, note):
    current_time = time.time()
    if note in note_last_press_time:
        elapsed_time = current_time - note_last_press_time[note]
        if elapsed_time <= DOUBLE_CLICK_THRESHOLD:
            # Doppelklick erkannt -> Stoppe das zugehörige Sample
            if note in note_channels:
                channel = note_channels[note]
                channel.stop()
                print(f"Doppelklick erkannt: Stoppe {note_name}.")
                del note_channels[note]  # Entferne die Zuordnung der Note
            note_last_press_time.pop(note, None)
            return

    if note_name in loaded_sounds:
        sound = loaded_sounds[note_name]

        if note in note_channels:
            channel = note_channels[note]
            channel.stop()

        channel = sound.play()
        note_channels[note] = channel  # Speichere den neuen Kanal
        print(f"Spiele Datei: {note_name}.mp3")
    else:
        print(f"MP3-Datei nicht gefunden: {note_name}.mp3")

    note_last_press_time[note] = current_time

def midi_note_to_name(note):
    octave = (note // 12) - 1
    note_names = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
    name = note_names[note % 12]
    return f"{name}{octave}"

def main():
    try:
        midi_input = initialize_midi()
        initialize_audio()
        preload_samples()

        print("Drücke eine Taste auf dem MIDI-Keyboard (Strg+C zum Beenden).")

        while True:
            if midi_input.poll():
                midi_events = midi_input.read(10)
                for event in midi_events:
                    data = event[0]
                    status, note, velocity, _ = data

                    if status == 144 and velocity > 0:  # Note On
                        note_name = midi_note_to_name(note)
                        play_or_stop_sample(note_name, note)

    except KeyboardInterrupt:
        print("\nProgramm beendet.")
    finally:
        pygame.midi.quit()
        pygame.mixer.quit()

if __name__ == "__main__":
    main()
Code-Sprache: PHP (php)

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert