איך לייצר באזז

אני: כרמל נכון שאת אוהבת לנגן ולשמוע מוזיקה ?

כרמל: מאוד

אני: מה דעתך שנלמד את המיקרופייתון לנגן ?

כרמל: מזה המיקרופון הזה שוב ?

אני: ?!?!?!?

הפעם נתמקד בהפעלה של זמזם, כאשר ניתן לו זרם הוא משמיע צפצוף, ושוב נשתמש ב PWM בכדי לשלוט הפעם על התדר, ולא רק על ה"עומצה" (duty cycle)

במוזיקה לכל תו בכלי נגינה ישנו תדר מסויים, אנחנו נחשב את התדרים של פסנתר ונייצר פונקציה שתביא לנו את התדר הרצוי לפי סימן התו

הנוסחא נראית כך:

והקוד כך:

# רשימת התווים
names = ("c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b")

# התו הסטנדרטי - או תו שטוטגרד
A4 = 440

# ממנו נחשב את התדר לתו הראשון באוקטבה אפס
C0 = A4 * pow(2, -4.75)

#משם אנחנו יכולים לחשב את שאר התווים (וקצת מעגלים אותם)
def note_freq(note):
    n, o = note[:-1], int(note[-1])
    index = names.index(n)
    return int(round(pow(2, (float(o * 12 + index) / 12.0)) * C0, 2))

print(note_freq("c4"))

עכשיו שאנחנו יודעים לחשב את התדר של כל תו, נתחיל מהשיר הכי פשוט שאני יודע לנגן על פסנתר, "יונתן הקטן". (מודה, אני רק יודע את ההתחלה, אחר כך זה נהיה מסובך(

מאחר שמוזיקה היא לא ממש התחום החזק שלי, הלכתי לחפש את התווים, וויקיפדיה לא איכזב, והנה הם:

עכשיו מה שנשאר זה לקרוא את התווים, פשוט לא ? , גוגל קצר, ועוד שעה בערך ועכשיו אני יודע שהתו הראשון באוקטבה C4 נמצא בקו מתחת לחמשת הקווים ומשם אחד בין הקווים ואחד על הקווים.

מה חסר לנו עכשיו ?, אופס לחבר את הזמזמם, זה כבר פשוט, פין אחד לצד שמסומן ב +, וצד שני להארקה

אחרי שחיברנו את הכל, ננסה להריץ את התוכנית הבאה, שבה יש לנו פונקציה אחת שיודעת לנגד תדר לזמן מסויים, ופונקצייה שנייה שעוברת על רשימת התווים ומנגנת כל תו למשך 500 מילי-שניות (חצי שנייה, בכל שנייה יש 1000 מילי-שניות)

import utime as time
from machine import PWM, Pin

buzzer_pin = PWM(Pin(13, Pin.OUT), freq=1000)

def tone(freq, duration=0, duty=100):
    # נכוון את התדר
    buzzer_pin.freq(int(freq))
    buzzer_pin.duty(duty)

    # נמתין את אורך רוב התו לעשרים במילי-שניות
    time.sleep_us( int(duration * 0.9 * 1000) )

    # נכבה את הזרם לחלוטין לזמן שנשאר
    buzzer_pin.duty(0)
    time.sleep_us(int(duration * 0.1 * 1000))

def play_yonatan():
    notes = [ "g4", "e4", "e4", 
              "f4", "d4", "d4", 
              "c4", "d4", "e4", "f4", 
              "g4", "g4", "g4", 
              "c4", "e4", "g4", "g4", 
              "c4", 
              "d4" , "d4", "d4", "d4", 
              "d4", "e4" , "f4" ,
              "e4", "e4", "e4", "e4", 
              "e4", "f4", "g4", 
              "g4", "e4", "e4",
              "f4", "d4", "d4",
              "c4", "e4", "g4", "g4",
              "c4"
              ]

    for n in notes:
        print("%s : %d" % (n, note_freq(n)))
        # ננגן את התווים בקצב אחיד
        tone(note_freq(n), 500)

play_yonatan()

טוב, זה היה דיי גרוע

משהו לא לגמרי הסתדר שם, אבל תודו שזה דיי קרוב

בוא ננסה לעשות משהו לשפר את זה, בואו נוסיף הפסקות בין המקטעים, כך:

def play_yonatan_breaks():
    notes = [ "g4", "e4", "e4", "-",
              "f4", "d4", "d4", "-",
              "c4", "d4", "e4", "f4", "-",
              "g4", "g4", "g4", "-",
              "c4", "e4", "g4", "g4", "-",
              "c4", "-",
              "d4" , "d4", "d4", "d4", "-",
              "d4", "e4" , "f4" , "-",
              "e4", "e4", "e4", "e4",  "-",
              "e4", "f4", "g4", "-",
              "g4", "e4", "e4", "-",
              "f4", "d4", "d4", "-",
              "c4", "e4", "g4", "g4", "-",
              "c4"
              ]

    for n in notes:
        # ננגן את התווים בקצב אחיד
        # אך הפעם עם הפסקות בין המקטעים
        if n == "-":
            time.sleep_us(500 * 1000)
            print("-")
        else:
            print("%s : %d" % (n, note_freq(n)))
            tone(note_freq(n), 500)

play_yonatan_breaks()

משתפר, אבל עדיין לא שם. חסר לנו מרכיב חשוב מאוד במוזיקה (שכנראה אין לי יותר מידי ממנו) והוא קצב. כל תו במנגינה מנגנים לזמן מסויים, אם נחזור לתווים נראה שלא כל התווים זהים.

אנחנו מנגנים במקצב של ארבע רבעים, ויש לנו תו אחד שנקרא רבע, עיגול מלא בעל רגל אחת, ויש לנגן אותו רבע שנייה במקצב שלנו.

תו אחד שנרא חצי, בעל עיגול ריק ורגל, ויש לנגן אותו למשך חצי שנייה.

ותו אחד שנקרא שלם, שיש לנגן אותו שנייה שלמה.

ישנם עוד תווים כמו שמינית ושש-עשרית, אבל נתרכז קודם בשלושה שהזכרנו

נחבר את כל הידע המוזיקלי שצברנו עד כה:


def play_yonatan_rhythm():
    notes = [ "g4", "e4", "e4",
              "f4", "d4", "d4",
              "c4", "d4", "e4", "f4",
              "g4", "g4", "g4", "-",
              "c4", "e4", "g4", "g4",
              "c4", "-",
              "d4" , "d4", "d4", "d4",
              "d4", "e4" , "f4" ,
              "e4", "e4", "e4", "e4",
              "e4", "f4", "g4",
              "g4", "e4", "e4",
              "f4", "d4", "d4",
              "c4", "e4", "g4", "g4",
              "c4"
              ]
    durations = [ 250, 250, 500,
              250, 250, 500,
              250, 250, 250, 250,
              250, 250, 500, 500,
              250, 250, 250, 250,
              1000, 500,
              250, 250, 250, 250,
              250, 250, 500,
              250, 250, 250, 250,
              250, 250, 500,
              250, 250, 500,
              250, 250, 500,
              250, 250, 250, 250,
              1000
              ]

    # נעבור על רשימת התווים, והזמנים במקביל
    for n, d in zip(notes, durations):
        # נעצור להפסקה כשיש צורך
        if n == "-":
            time.sleep_us(d * 1000)
            print("-")
        # ובאם לא, נשמיע את התו לזמן המתאים לו
        else:
            print("%s : %d" % (n, note_freq(n)))
            tone(note_freq(n), d)

play_yonatan_rhythm()

בראבו, עכשיו זה ממש נשמע טוב.

אבל זה ממש ארוך ודיי מתיש הקטע של לזכור בדיוק איפה אנחנו אוחזים שכותבים ככה את המנגינה, בוא ננסה לשפר את זה במעט.

נשתמש בצורת כתיבת שהומצאה ע"י חברת נוקיה להעברת רינגטונים אל הטלפונים הניידים שלהם, והיא נקראת בשם המאוד מקורי RTTTL, או בתרגום חופשי לעברית "העברת צילצול בצורה כתובה"

https://en.wikipedia.org/wiki/Ring_Tone_Transfer_Language

הנה דוגמא שננסה לעבוד איתה:

# הנה המנגינה שלנו שמורכבת משלושה חלקים, השם, ההגדרות, התויים
tune = "Simpsons:d=4,o=5,b=160:32p,c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g"

name, settings, notes = tune.split(':')
print("going to play %s" % name)

# נפרק את ההגדרות, לאורך ברירת המחדל, אוקטבת ברירת המחדל, ולקצב בביט לדקה
for i in settings.split(','):
    k, v = i.split('=')
    if k == 'd':
        default_duration = int(v)
    if k == 'o':
        default_octave = int(v)
    if k == 'b':
        bpm = int(v)
        # נחשב את הקצב לפי מילישניות לתו מלא אחד
        # שממנו נוכל לחשב את החצי, רבע, שמינית, וכן הלאה
        # 240000 = 60 sec/min * 4 beats/whole-note * 1000 msec/sec
        msec_per_whole_note = 240000.0 / bpm

for n in notes.split(','):
    n = n.strip()
    # נבדוק האם יש אורך מוגדר לתו, באם לא נשתמש בברירת המחדל
    if n.startswith(('16', '32')):
        duration, n = int(n[:2]), n[2:]
    elif n.startswith(('1', '2', '4', '8')):
        duration, n = int(n[:1]), n[1:]
    else:
        duration = default_duration

    # נבדוק האם יש אוקטבה מוגדרת, באם לא נשתמש בברירת המחדל
    if n[-1].isdigit():
        octave, n = int(n[-1]), n[:-1]
    else:
        octave = default_octave

    # נבדוק האם התו מנוקד, באם הוא מנוקד, יש להאריך אותו בחצי
    duration_multiplier = 1.0
    if n[-1] == ".":
        duration_multiplier = 1.5
        n = n[:-1]

    # נחשב את אורך התו, לפי הקצב ולפי הניקוד
    duration = int(msec_per_whole_note / duration * duration_multiplier)

    # ננגן את התו
    if n == "p":
        time.sleep_us(duration * 1000)
        print("-")
    else:
        note = "%s%d" % ( n , octave)

        print("%s, %dhz, %dms" % (note, note_freq(note), duration))
        tone(note_freq(note), duration)

עכשיו שאנחנו נגנים מקצועיים, בוא נראה מי יזהה את השיר הבא ?


tune = '''??????:d=8,o=4,b=240:4e, a,p,b,c5,b,a,c5, b,a,g#,f,4e,4p,e, 
a,p,b,c5,b,a,c5, 2b,4p,b,c5, e,d5,c5,e,c5,b,a,c5, b5,c5,g#,f4,4e4, 
g#,f,f,e,d#,e,f,g#, e,f4,g#,a,4b,b,c5, d5,c5,b,d5,c5,b,a,c5, b,a,g#,f,2e, 
g#4,f,f4,e,d#,e,f,g#, 2e4,4p,4e4, 2p, 2e
'''

results matching ""

    No results matching ""