用python3制作音乐的探索

,我发现了一个开源模块比较适合入门:【PySynth 】 很有趣。

这里记录一下我的研究笔记。——如果有直接的数学模型就好了,可能得去查声波的书。
下载下来是这样的:这里写图片描述

文件夹里:menv.py是帮助文件;mixfiles.py是混音,pysynth.py是个模型。
我先不看具体怎么来的,试一试应用。
先看看备注:


'song' 是一个被定义的列表或元组,格式是这样 ['音', 长度]

音符是'a','g'这些; 升半音以 '#' 表示,降半音以 'b' 表示;以octave 结束 (默认为四分音符);asterisk 在最后代表重音; 'r' 是空.

音的长度用数字表示:1=全音符; 2=二分音符; 4=四分音符, 等.
浮点音符写法:
 1.33 = -2 = 二分浮点音符
 2.66 = -4 = 四分浮点音符
 5.33 = -8 = 八分浮点音符

 一些参数:
 节奏:每分钟节拍数;  bpm = 95
 八度转变 (neg. 降八度; pos. 升八度); transpose = 0
 音符间停顿 (0. = 连音 ;  0.5 = 断音); pause = 0.05
 Volume boost:音量变高 (1. = 音量无变化);  boost = 1.2
 Output file name 输出文件名;fn = 'pysynth_output.wav'

 其他参数:
 Influences the decay of harmonics over frequency. Lowering the value eliminates even more harmonics at high frequencies.
 Suggested range: between 3. and 5., depending on the frequency response  of speakers/headphones used
harm_max = 4.

直接到应用部分,它用的样品音乐,来源于demosongs.

# 产生样品音乐


if __name__ == '__main__':
    print()
    print("Creating Demo Songs... (this might take about a minute)")
    print()

# Nocturne Op. 9 #2 by F. Chopin的开头
   song3 = (
  ('bb', 8),
  ('g5*', 2), ('f5', 8), ('g5', 8), ('f5', -4), ('eb5', 4), ('bb', 8),
  ('g5*', 4), ('c5', 8), ('c6', 4), ('g5', 8), ('bb5', -4), ('ab5', 4), ('g5', 8),
  ('f5*', -4), ('g5', 4), ('d5', 8), ('eb5', -4), ('c5', -4),
  ('bb*', 8), ('d6', 8), ('c6', 8), ('bb5', 16), ('ab5', 16), ('g5', 16), ('ab5', 16), ('c5', 16), ('d5', 16), ('eb5', -4),

    # SONG 3
    make_wav(song3, bpm = 132/2, pause = 0., boost = 1.1, fn = "pysynth_chopin.wav")

试了周杰伦的晴天,bmp是67下每分钟:

#!/usr/bin/env python

import pysynth

songx=(('e3',8),('d3',8),('f3',8),('e3',4),('c3',8),('g3',8),('b3',8),('c4',8),('b3',8),('c3',8),('c3',4),('c3',8),('a3',8),('a3',8),
       ('r',16),('a3',16),('g3',8),('g3',4),('g3',8),('f3',8),('e3',8),('d3',8),('e3',8),('f3',8),('e3',2.25),('e3',8),('f#3',8),('g#3',8),
       ('e3',4),('f3',8),('g3',8),('b3',8),('d4',8),('b3',8),('c4',8),('c4',6),('c4',16),('c4',8),('g3',8),('g3',8),('a3',8),('g3',8),('f3',8),
       ('a2',8),('b2',8),('c3',8),('d3',8),('e3',8),('d3',3),('e3',8),('c3',2))
pysynth.make_wav(songx, bpm=67, repeat=0, fn="trymusicx.wav")

再看如何定义这个函数的。首先是模块。

#!/usr/bin/env python


import sys
assert sys.version >= '3.3', "不适用于python2.升级至python3."


from mkfreq import getfreq

pitchhz, keynum = getfreq(pr = True)

出现的pitchhz, keynum = getfreq(pr = True)
找到这个函数,估计是钢琴声音:

pitchhz, keynum = {}, {}

keys_s = ('a', 'a#', 'b',  'c',  'c#', 'd', 'd#', 'e',  'f',  'f#', 'g', 'g#')
keys_f = ('a', 'bb', 'b',  'c',  'db', 'd', 'eb', 'e',  'f',  'gb', 'g', 'ab')
keys_e = ('a', 'bb', 'cb', 'b#', 'db', 'd', 'eb', 'fb', 'e#', 'gb', 'g', 'ab')

def getfreq(pr = False):
    if pr:
        print("Piano key frequencies (for equal temperament):")
        print("Key number\tScientific name\tFrequency (Hz)")
    for k in range(88):
        freq = 27.5 * 2.**(k/12.)    #表示该音的频率。**是乘方, 27.5hz是钢琴最左边的第一个按键的频率。
        oct = (k + 9) // 12    #整除,在音阶中排第几。
        note = '%s%u' % (keys_s[k%12], oct)   #如果k=2,note='b0';k=30,note='d#3'
        if pr:
            print("%10u\t%15s\t%14.2f" % (k+1, note.upper(), freq))
        pitchhz[note] = freq    #k=30,pitchhz[d#3]=27.5*2^(30/12)
        keynum[note] = k        #k=30,keynum[d#3]=30
    return pitchhz, keynum


# construct filnames for Salamander piano samples

sampfn = {}
facs = 1, 2**(1/12), 2**(2/12)
nam = "A", "C", "D#", "F#"

def getfn(layer):
    for k in range(88):
        oct = (k + 9) // 12

        sampfn[k] = "%s%uv%u.wav" % (nam[(k // 3) % 4], oct, layer), facs[k % 3]
    return sampfn

继续:


import wave, math, struct

def make_wav(song,bpm=120,transpose=0,pause=.05,boost=1.1,repeat=0,fn="out.wav", silent=False):
    f=wave.open(fn,'w')

    f.setnchannels(1)   #1个轨道
    f.setsampwidth(2)   #?
    f.setframerate(44100)   #采样频率:每秒采样个数,它用赫兹(Hz)来表示。
    f.setcomptype('NONE','Not Compressed')

    bpmfac = 120./bpm   #每个节拍的时长,以120为单位。bmp是每分钟多少节拍,晴天是以四分音符为一拍,那每个四分音符的时长就是bpmfac。2s=1个bpmfac.不知道这里为啥要用两倍的。

    def length(l):   
        return 88200./l*bpmfac #相当于44100/(l*(60/bpm)) 即:每秒采集的信号样本个数/((?)*音符长度(s))——每个音符的被采集的信号个数?不知道l是什么

    def waves2(hz,l):
        a=44100./hz
        b=float(l)/44100.*hz
        return [a,round(b)]

    def sixteenbit(x):
        return struct.pack('h', round(32000*x))   #struct.pack是将数据转化成字节流,这里h是hex(Hexadecimal),十六进制

    def asin(x):
        return math.sin(2.*math.pi*x) #与波形相关,sin(2πx)

    def render2(a,b,vol):
        b2 = (1.-pause)*b  #pause是停顿
        l=waves2(a,b2)  
        ow=b''
        q=int(l[0]*l[1])

        # 旋律与频率相关:
        lf = math.log(a)
        lf_fac = (lf-3.) / harm_max
        if lf_fac > 1: harm = 0
        else: harm = 2. * (1-lf_fac)
        decay = 2. / lf
        t = (lf-3.) / (8.5-3.)
        volfac = 1. + .8 * t * math.cos(math.pi/5.3*(lf-3.))

        for x in range(q):
            fac=1.
            if x<100: fac=x/80.
            if 100<=x<300: fac=1.25-(x-100)/800.
            if x>q-400: fac=1.-((x-q+400)/400.)
            s = float(x)/float(q)
            dfac =  1. - s + s * decay
            ow=ow+sixteenbit((asin(float(x)/l[0])
                +harm*asin(float(x)/(l[0]/2.))
                +.5*harm*asin(float(x)/(l[0]/4.)))/4.*fac*vol*dfac*volfac)
        fill = max(int(ex_pos - curpos - q), 0)
        f.writeframesraw((ow)+(sixteenbit(0)*fill))
        return q + fill


    # 输出wav格式文件


    if silent == False:
        print("Writing to file", fn)
    curpos = 0
    ex_pos = 0.
    for rp in range(repeat+1):
        for nn, x in enumerate(song):    #enumerate是python对于一个可列举的对象,变成索引。nn是序号,x是之前输入的音符,类似于('g5*', 2)这样。假设这个音是('g5*', 2),bpm=60,节奏型4/4
            if not nn % 4 and silent == False:
                print("[%u/%u]\t" % (nn+1,len(song)))
            if x[0]!='r':
                if x[0][-1] == '*':
                    vol = boost
                    note = x[0][:-1]      #如果有音高增强
                else:
                    vol = 1.
                    note = x[0]
                try:
                    a=pitchhz[note]      #a是音符频率值
                except:
                    a=pitchhz[note + '4']   # 若没有则为空拍四分音符。
                a = a * 2**transpose      #是否转调
                if x[1] < 0:                #如果节奏型小于零('g5*', 2),bpm=60
                    b=length(-2.*x[1]/3.)      #b=44100/(((-2)*2/3)*(60/bpm))=33075
                else:
                    b=length(x[1])      #否则 b=44100/(2*(60/bpm))=22050
                ex_pos = ex_pos + b     #ex_pos=22050
                curpos = curpos + render2(a,b,vol)

            if x[0]=='r':
                b=length(x[1])
                ex_pos = ex_pos + b
                f.writeframesraw(sixteenbit(0)*int(b))
                curpos = curpos + int(b)

    f.writeframes(b'')
    f.close()
    print()
  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值