python生成特定頻率、特定音量的正弦波wav文件(升級版v1.231)

import wave
import numpy as np
import struct
import matplotlib.pyplot as plt
import math
import ctypes
#from compiler.ast import flatten
import soundfile as sf


def calculate_db(xdb):
    db = math.pow(10, xdb/20)
    return db

def calculate_db_test(xdb):
    #db = 0
    print ("math.pow(10, -12/20) : ", math.pow(10, -12/20)) # 0.251188643150958
    print ("math.pow(10, -6/20) : ", math.pow(10, -6/20)) #0.5011872336272722
    print ("math.pow(10, -3/20) : ", math.pow(10, -3/20)) #0.7079457843841379
    print ("math.pow(10, -1/20) : ", math.pow(10, -1/20)) #0.8912509381337456
    print ("math.pow(10, 0/20) : ", math.pow(10, 0/20)) #1.0
    print ("math.pow(10, 1/20) : ", math.pow(10, 1/20)) #1.1220184543019633
    print ('math.pow(10, 3/20) : ', math.pow(10, 3/20)) #1.4125375446227544
    print ('math.pow(10, 6/20) : ', math.pow(10, 6/20)) #1.9952623149688795
    print ('math.pow(10, 9/20) : ', math.pow(10, 9/20)) #2.8183829312644537
    print ('math.pow(10, 12/20) : ', math.pow(10, 12/20)) #3.9810717055349722
    db = math.pow(10, xdb/20)
    return db
  
def generial_sinewave(samplerate,ch_num,bytes_width,xdb,sinwav_freq_l,sinwav_freq_r,duration, file_name):
    # volume xdb
    db = calculate_db(xdb)
    # sample/every second
    framerate = samplerate
    # channel_num
    channel_num = ch_num
    # bytes needed every sample
    sample_width = bytes_width
    bits_width = sample_width*8
    # seconds, long of data
    duration = duration
    # frequeny of sinewave
    sinewave_frequency_l = sinwav_freq_l
    sinewave_frequency_r = sinwav_freq_r
    # max value of samples, depends on bits_width
    max_val = 2**(bits_width-1) - 1
    print ('max_val : ', max_val)
    #volume = 32767*db #0x7FFF=32767
    volume = max_val*db #2**(bits_width-1) - 1
    #多個聲道生成波形數據
    x = np.linspace(0, duration, num=duration*framerate)
    y_l = np.sin(2 * np.pi * sinewave_frequency_l * x) * volume
    y_r = np.sin(2 * np.pi * sinewave_frequency_r * x) * volume
    # 將多個聲道的波形數據轉換成數組
    y = zip(y_l,y_r)
    ##print("zip y", y)
    y = list(y)
    ##print("list y",y)
    y = np.array(y,dtype=np.int)
    #print("array y",y)
    y = y.reshape(-1)
    ##print("reshap array y",y)
     
    #plt.subplot(122)
    #plt.plot(x, y_l, color='r', linestyle='-', marker='+')
    #plt.show()
 
    # 最終生成的一維數組
    sine_wave = y
    print("type(sine_wave)=",type(sine_wave))
    
    #open wav file
    wf = wave.open(file_name, 'wb')#wf = wave.open("sine.wav", 'wb')
    wf.setnchannels(channel_num)
    wf.setframerate(framerate)
    wf.setsampwidth(sample_width)
    #if (sample_width == 3):
    #    wf.setsampwidth(sample_width+1)
    for v in sine_wave:
        if (sample_width == 4):
            data = struct.pack('<l', int(v))
        elif (sample_width == 3):
            #data = struct.pack('<l', int(v*2**8))
            d = int(v)
            dc = getComplement_24bits(d)
            data = dc.to_bytes(3,'little')
        elif (sample_width == 2):
            data = struct.pack('<h', int(v))
        elif (sample_width == 1):    
            d = int(v)
            dc = getComplement_8bits(d)
            data = dc.to_bytes(1,'little')
            #print("d=%x,dc=%x"%(d,dc), data)
            #data = struct.pack('<c', chr(dc)) #error
        else:
            data = struct.pack('<b', int(v))
 
        #print("v=%x"%(v), data)
        wf.writeframesraw(data)
    #print(struct.pack('<i', int(y[2])))
    #print(struct.pack('<i', int(y[4])))
    wf.close()
    
def readWavFile(file_name):
    sig, sample_rate = sf.read(file_name)
    print("採樣率:%d" % sample_rate)
    #print(sig) #data, 2ch, 2 dimension matrix
    return 0

def getComplement_24bits(v): #求24bis數據的補碼
    v_int = int(v)
    if (v_int >= 0):
        #print('param is positive')
        return v_int
    c  = (-1)*v
    c  = ~c + 1
    c &= 0xFFFFFF
    c |= 0x800000 
    return c
 
def getComplement_8bits(v): #求8bis數據的補碼
    #todo, ???
    if(v<0):
        v = v + 0x80
    else:
        v = v - 0x80
        
    v_int = int(v)
    if (v_int >= 0):
        #print('param is positive')
        return v_int
    c  = (-1)*v
    c  = ~c + 1
    c &= 0xFF
    c |= 0x80
      
    return c

def readWavFileAndPlotIt():
     return 0

def float2split(fdata):
    #print(type(fdata))
    #print("fdata_string=", str(fdata))
    #print("fdata_value=", fdata)
    split = math.modf(fdata)
    fraction_part = split[0]
    decimal_part = split[1]
    #print ("fraction_part=",fraction_part)
    #print ("decimal_part=",decimal_part)
    return split

#Keep 9 DecimalPlaces
def float2crop_str(fdata):
    b = round(fdata, 9)
    print("round(b)=",b)
    b_str = str(b)
    print("bstr=",b_str)
    c = float(b_str)
    print("c=",c)
    if(fdata>c):
        print("fdata>c,c=",c)
    else:
        c = fdata
        print("fdata<=c,c=",c)
    return str(c)

def float2crop(fdata):
    b = round(fdata, 9)
    #print("round(b)=",b)
    b_str = str(b)
    #print("bstr=",b_str)
    c = float(b_str)
    #print("c=",c)
    if(fdata==c):
        c = fdata
    return c	 

def struct_pack_test(v):
    v = -0x41aa
    print("test:%x"%(v), struct.pack('<h', int(v)))
    v = -54
    print("test:%x"%(v), struct.pack('<b', int(v)))
    v = -0x6a1aa
    print("test:%x"%(v), struct.pack('<i', int(v)))
    return 0

def sinewave_info_print(framerate,channel_num, sample_width, xdb, sinewave_frequency_l, sinewave_frequency_r, duration, file_name):
    print("wav_file_name: ", file_name)
    print("samplerate: ", framerate)
    print("channel_num: ", channel_num)
    print("bits_width: ", sample_width*8)
    print("volume: %db"%(xdb))
    print("sine_freq_ch_l: ", sinewave_frequency_l)
    print("sine_freq_ch_r: ", sinewave_frequency_r)
    print("sine_freq_duration: ", duration)    
    return 0

def general_sinewave_filename(framerate,channel_num,sample_width, xdb, sinewave_frequency_l, sinewave_frequency_r, duration):
    # wav file_name
    bits_width = sample_width*8
    duration_cropped = float2crop(duration)
    file_name = "sine_"+str(framerate)+"_"+str(channel_num)+"ch_"+str(bits_width)+"bits_"+str(xdb)+"db"
    file_name += "_l"+str(sinewave_frequency_l)+"_r"+str(sinewave_frequency_r)+"_"+str(duration_cropped)+"s.wav"

    print("samplerate: ", framerate)
    print("channel_num: ", channel_num)
    print("bits_width: ", sample_width*8)
    print("volume: %db"%(xdb))
    print("sine_freq_ch_l: ", sinewave_frequency_l)
    print("sine_freq_ch_r: ", sinewave_frequency_r)
    print("sine_freq_duration: ", duration)
    
    print ('file_name: ', file_name)
    return file_name

def main():
 
    # volume x_db
    db = -3
    #db = calculate_db(db_v)#db = math.pow(10, -3/20)
    # sample/every second
    framerate = 48000
    # channel_num
    channel_num = 2
    # bytes needed every sample
    sample_width = 4
    bits_width = sample_width*8
    # frequeny of sinewave
    sinewave_frequency_l = 2000
    sinewave_frequency_r = 2000
    # seconds, long of data
    #duration = 40/framerate #used to test float2crop() function
    duration = (2*(framerate/sinewave_frequency_r))/framerate

    if sample_width not in [1,2,3,4]: ##[1,3]has some errors,resolved
        return 0
    file_name = general_sinewave_filename(framerate, channel_num, sample_width, db, sinewave_frequency_l, sinewave_frequency_r, duration)
    #sinewave_info_print(framerate,channel_num, sample_width, db, sinewave_frequency_l, sinewave_frequency_r, duration, file_name)
    generial_sinewave(framerate,channel_num, sample_width, db, sinewave_frequency_l, sinewave_frequency_r, duration, file_name)

    return 0
    
main()

 

發佈了28 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章