파이썬을 사용하여 템포 해석을 시도했습니다.

처음에



이번에는 Python을 사용하여 템포 해석을 실시했습니다.
본 기사는 작성한 프로그램의 소개와 결과에 대해 전할 수 있으면 좋겠습니다.
앞으로 몇 번이나 갱신하겠습니다.
※정밀도가 굉장히 나쁘습니다…
※프로그래밍을 할 수 있는 쪽은 개선해 코멘트를 부탁합니다<(_ _)>

사용환경



파이썬 3.9.1
Windows 10 Home Edition
Anaconda Powershell Prompt

템포 분석에 대해 자세히 알아보기



분석 순서


  • wav 파일을 N 초마다 분할
  • Librosa를 사용하여 전체 템포 분석
  • 분할된 wav 파일 마다 템포를 해석
  • 템포의 추이를 matplotlib로 표시

  • 1. wav 파일을 N초마다 분할



    wav 파일을 분할 → def cut_wav():
    output 파일을 자동 생성하고 거기에,
    0.wav -> 1.wav ...로 저장합니다.

    2.Librosa를 사용하여 전체 템포를 분석



    Librosa에서 템포 해석을 해 나갑니다.
    전체 곡을 통과하면 전체 템포가 출력됩니다.
    ※어떻게 템포를 내고 있는지는, 웹사이트를 봐 주세요.
    Librosa

    3. 분할한 wav 파일마다 템포를 해석



    "2.Librosa를 사용하여 전체 템포를 분석"과 마찬가지로,
    분할한 파일마다 템포를 추정해 갑니다.
    상당한 이상치가 나오므로 주의가 필요합니다.
    ※향후 개선 예정

    4. 템포의 추이를 matplotlib로 표시



    추정된 템포를 그래프로 표시합니다. 항상 일정한 템포라면 직선이 표시되어야 합니다.

    실제 프로그램


    import numpy as np
    import librosa
    import wave
    import struct
    import math
    import os
    from scipy import fromstring, int16
    import matplotlib.pyplot as plt 
    
    #----------------------------------------
    # wavファイルの分割
    #---------------------------------------
    
    def cut_wav(filename, time):
        #ファイル読み出し
        wavf = filename + '.wav'
        wr = wave.open(wavf, 'r')
    
        #waveファイルが持つ性質を取得
        ch = wr.getnchannels()
        width = wr.getsampwidth()
        fr = wr.getframerate()
        fn = wr.getnframes()
        total_time = 1.0 * fn / fr
        integer = math.floor(total_time)
        t = int(time)
        frames = int(ch * fr * t)
        num_cut = int(integer//t)
    
        # 確認用
        print("total time(s) : ", total_time)
        print("total time(integer) : ", integer)
        print("time : ", t)
        print("number of cut : ", num_cut)
    
        # waveの実データを取得し数値化
        data = wr.readframes(wr.getnframes())
        wr.close()
        X = np.frombuffer(data, dtype=int16)
    
        print()
    
        for i in range(num_cut):
            print(str(i) + ".wav --> OK!")
            #出力データを生成
            outf = 'output/' + str(i) + '.wav' 
            start_cut = i*frames
            end_cut = i*frames + frames
            Y = X[start_cut:end_cut]
            outd = struct.pack("h" * len(Y), *Y)
    
            # 書き出し
            ww = wave.open(outf, 'w')
            ww.setnchannels(ch)
            ww.setsampwidth(width)
            ww.setframerate(fr)
            ww.writeframes(outd)
            ww.close()
        return num_cut
    
    #----------------------------------------
    # 全体のテンポを求める
    #---------------------------------------
    def totaltempo(filename):
        #検索するファイル名の作成 => output/ i .wav
        name = filename + ".wav"
    
        #wavファイルの読み込み
        y, sr = librosa.load(name)
    
        #テンポとビートの抽出
        tempo , beat_frames = librosa.beat.beat_track(y=y, sr=sr)
    
        #全体のテンポを表示
        print()
        print("total tempo : ", int(tempo))
        print()
    
    #----------------------------------------
    # 分割テンポを求める
    #---------------------------------------
    
    def temposearch(num, time):
        #return用変数の宣言
        l = []
        t = []
        t_time = 0
        before_tempo = 0
    
        print("division tempo")
    
        #音楽の読み込み
        for i in range(0,num,1):
            #検索するファイル名の作成 => output/ i .wav
            name = "output/" + str(i) + ".wav"
    
            #wavファイルの読み込み
            y, sr = librosa.load(name)
    
            #テンポとビートの抽出
            tempo , beat_frames = librosa.beat.beat_track(y=y, sr=sr)
            int_tempo = int(tempo)
    
            #テンポの表示
            print(str(i+1) +  ":" + str(int_tempo))
    
            #return用変数へ代入
            l.append(int_tempo)
            t_time = t_time + int(time)
            t.append(t_time)
    
        return l, t
    
    
    
    #---------------------------------
    # メイン関数
    #---------------------------------
    if __name__ == '__main__':
        # すでに同じ名前のディレクトリが無いか確認
        file = os.path.exists("output")
        print(file)
    
        if file == False:
            #保存先ディレクトリの作成
            os.mkdir("output")
    
        #ファイル名とカット時間を入力しwavファイルを分割
        f_name = input('input filename -> ')
        cut_time = input('input cut time -> ')
        n = int(cut_wav(f_name,cut_time))
    
        #テンポ解析
        totaltempo(f_name)
        tempo, time = temposearch(n, cut_time)
    
        print()
    
        #タイトル用
        name = "テンポ解析 " + cut_time + "秒で分割"
    
        #グラフ描写
        plt.title(name, fontname="MS Gothic")
        plt.xlabel("時間(s)", fontname="MS Gothic")
        plt.ylabel("テンポ(bpm)", fontname="MS Gothic")
        plt.ylim(60, 180)
        plt.plot(time, tempo)
        plt.show()
    

    실행 결과



    이번에는 「위풍 당당」이라는 곡을 해석해 갑니다.
    분석 된 노래는 여기에서 → 무료 Wave,MP3
    (base) PS C:\Users\Name\tempo> python tempo_main.py
    True
    input filename -> ifudoudou
    input cut time -> 10
    total time(s) :  204.56489795918367
    total time(integer) :  204
    time :  10
    number of cut :  20
    
    0.wav --> OK!
    1.wav --> OK!
    2.wav --> OK!
    3.wav --> OK!
    4.wav --> OK!
    5.wav --> OK!
    6.wav --> OK!
    7.wav --> OK!
    8.wav --> OK!
    9.wav --> OK!
    10.wav --> OK!
    11.wav --> OK!
    12.wav --> OK!
    13.wav --> OK!
    14.wav --> OK!
    15.wav --> OK!
    16.wav --> OK!
    17.wav --> OK!
    18.wav --> OK!
    19.wav --> OK!
    
    total tempo :  117
    
    division tempo
    1:123
    2:117
    3:117
    4:117
    5:123
    6:123
    7:123
    8:123
    9:99
    10:99
    11:99
    12:99
    13:117
    14:117
    15:123
    16:117
    17:99
    18:99
    19:99
    20:117
    
    (base) PS C:\Users\Name\tempo>
    

    표시할 그래프↓


    결과 정보



    상당히, 정밀도가 나쁜 것을 알 수 있습니까? ?
    템포가 일정한 곳도 수치에 편차가 보이네요.

    감상



    단순히 곡수가 부족하기 때문에, 정밀도를 잘 모릅니다.
    많이 해석을 해 이 프로그램의 특징을 보고 싶습니다.
    개선점이나 이런 곡을 분석해 주었으면 한다! 라는 의견이 있으면 점점 코멘트 부탁드립니다!

    좋은 웹페이지 즐겨찾기