😽

pythonで愛猫達に歌ってもらった

2024/12/20に公開

はじめに

私は2匹の愛猫達と暮らしています。
2匹の声で何か歌ってもらえたら面白いなと思い、今回作ってみました。

実行したコード

それぞれの猫の声を使って、
cat1には第九、cat2には主よ、人の望みの喜びよを歌ってもらいました。

1.cat1 (第九)

import librosa
import numpy as np
import sounddevice as sd
import soundfile as sf  # ファイル保存用

# 1. 猫の声のピッチを変更する関数
def pitch_shift_cat_sound(cat_audio, sr, target_pitch):
    pitches, magnitudes = librosa.piptrack(y=cat_audio, sr=sr)
    avg_pitch = np.mean(pitches[pitches > 0])  # 平均ピッチを計算
    semitones = 12 * np.log2(target_pitch / avg_pitch)  # ピッチシフト量(セミトーン)
    shifted_audio = librosa.effects.pitch_shift(cat_audio, sr=sr, n_steps=semitones)
    return shifted_audio

# 2. 指定された音階に基づいて猫の声を生成
def generate_cat_music(cat_audio, sr, melodies, durations):
    music = []
    for melody, duration_per_note in zip(melodies, durations):
        for note_pitch in melody:
            if note_pitch is not None:
                shifted_audio = pitch_shift_cat_sound(cat_audio, sr, note_pitch)
                num_samples = int(sr * duration_per_note)
                shifted_audio = shifted_audio[:num_samples]
                music.append(shifted_audio)
            else:
                music.append(np.zeros(int(sr * duration_per_note)))  # 無音(休符)
    return np.concatenate(music)

# 3. WAVデータを再生
def play_audio(audio, sr):
    sd.play(audio, sr)
    sd.wait()

# メイン処理
if __name__ == "__main__":
    # 録音した猫の声のファイル
    cat_audio_path = "cat1_sound.wav"
    cat_audio, sr = librosa.load(cat_audio_path, sr=None)

    # 複数のメロディを定義
    melody0 = [1318.51, 1318.51, 1396.913, 1567.982, 1567.982, 1396.913, 1318.51, 1174.659, 1046.502, 1046.502, 1174.659, 1318.51]
    melody1 = [1318.51]
    melody2 = [1174.659]
    melody3 = [1174.659]
    melody4 = [1318.51, 1318.51, 1396.913, 1567.982, 1567.982, 1396.913, 1318.51, 1174.659, 1046.502, 1046.502, 1174.659, 1318.51]
    melody5 = [1174.659]
    melody6 = [1046.502]
    melody7 = [1046.502] 
    melody8 = [1174.659, 1174.659, 1318.51, 1046.502, 1174.659]
    melody9 = [1318.51, 1396.913] 
    melody10 = [1318.51, 1046.502, 1174.659]
    melody11 = [1318.51, 1396.913]
    melody12 = [1318.51, 1174.659, 1046.502, 1174.659]
    melody13 = [783.991]
    melody14 = [1318.51, 1318.51, 1396.913, 1567.982, 1567.982, 1396.913, 1318.51, 1174.659, 1046.502, 1046.502, 1174.659, 1318.51]
    melody15 = [1174.659]
    melody16 = [1046.502]
    melody17 = [1046.502]    
    
    
    # メロディリスト
    melodies = [melody0, melody1, melody2, melody3,melody4, melody5, melody6, melody7, melody8, 
                melody9, melody10,melody11, melody12, melody13, melody14, melody15, melody16, melody17]

    # 各メロディのノートの長さ(秒)
    durations = [0.5, 0.8, 0.4,1.0, 0.5, 0.8, 0.4,1.0, 0.5, 0.4,0.5, 0.4, 0.5, 1.0, 0.5, 0.8, 0.4,1.2]  # melody0: 0.5, melody1: 1, melody2: 0.5, melody3: 1秒

    # 猫の声を音階に基づいて再構成
    cat_music = generate_cat_music(cat_audio, sr, melodies, durations)

    # 再生
    play_audio(cat_music, sr)

    # 保存
    output_path = "cat_music_Btv_S_No9.wav"
    sf.write(output_path, cat_music, sr)
    print(f"音楽データを {output_path} に保存しました。")


2.cat2(主よ、人の望みの喜びよ)

import librosa
import numpy as np
import sounddevice as sd
import soundfile as sf  # ファイル保存用

# 1. 猫の声のピッチを変更する関数
def pitch_shift_cat_sound(cat_audio, sr, target_pitch):
    pitches, magnitudes = librosa.piptrack(y=cat_audio, sr=sr)
    avg_pitch = np.mean(pitches[pitches > 0])  # 平均ピッチを計算
    semitones = 12 * np.log2(target_pitch / avg_pitch)  # ピッチシフト量(セミトーン)
    shifted_audio = librosa.effects.pitch_shift(cat_audio, sr=sr, n_steps=semitones)
    return shifted_audio

# 2. 指定された音階に基づいて猫の声を生成
def generate_cat_music(cat_audio, sr, melodies, durations):
    music = []
    for melody, duration_per_note in zip(melodies, durations):
        for note_pitch in melody:
            if note_pitch is not None:
                shifted_audio = pitch_shift_cat_sound(cat_audio, sr, note_pitch)
                num_samples = int(sr * duration_per_note)
                shifted_audio = shifted_audio[:num_samples]
                music.append(shifted_audio)
            else:
                music.append(np.zeros(int(sr * duration_per_note)))  # 無音(休符)
    return np.concatenate(music)

# 3. WAVデータを再生
def play_audio(audio, sr):
    sd.play(audio, sr)
    sd.wait()

# メイン処理
if __name__ == "__main__":
    # 録音した猫の声のファイル
    cat_audio_path = "cat2_sound.wav"
    cat_audio, sr = librosa.load(cat_audio_path, sr=None)

    # 複数のメロディを定義
    melody0 = [1567.982, 1760, 1975.533, 2349.318, 2093.005, 2093.005, 2637.02, 2349.318, 2349.318, 3135.963, 2959.955, 3135.963, 2349.318, 1975.533, 1567.982, 1760, 1975.533, 2093.005 ]
    melody1 = [2349.318, 2637.02, 2349.318, 2093.005, 1975.533, 1760, 1975.533, 1567.982, 1479.978, 1567.982, 1760, 1174.659, 1479.978, 1760, 2093.005, 1975.533, 1760, 1975.533 ]
    melody2 = [1567.982, 1760, 1975.533, 2349.318, 2093.005, 2093.005, 2637.02, 2349.318, 2349.318, 3135.963, 2959.955, 3135.963, 2349.318, 1975.533, 1567.982, 1760, 1975.533, 1318.51 ]
    melody3 = [2349.318, 2093.005, 1975.533, 1760, 1567.982, 1174.659, 1567.982, 1479.978, 1567.982, 1975.533, 2349.318, 3135.963, 2349.318, 1975.533, 1567.982, 1975.533, 2349.318 ]
    melody4 = [3135.963]
    # メロディリスト
    melodies = [melody0, melody1, melody2, melody3, melody4]

    # 各メロディのノートの長さ(秒)
    durations = [0.7, 0.7, 0.7,0.7, 1.2]  # melody0: 0.5, melody1: 1, melody2: 0.5, melody3: 1秒

    # 猫の声を音階に基づいて再構成
    cat_music = generate_cat_music(cat_audio, sr, melodies, durations)

    # 再生
    play_audio(cat_music, sr)

    # 保存
    output_path = "cat_music_Bach_JJoMD.wav"
    sf.write(output_path, cat_music, sr)
    print(f"音楽データを {output_path} に保存しました。")

まとめ

ちょっとした息抜きに猫に歌ってもらいました。

Discussion