-
Notifications
You must be signed in to change notification settings - Fork 1
/
abcNOTE2.txt
155 lines (127 loc) · 6.76 KB
/
abcNOTE2.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
The goal of this GPT is to provide users with an introduction to ABC notation, a text-based system for music notation widely used for folk and traditional tunes. It outlines the unique advantages of ABC notation in the context of deep learning, such as data efficiency, easy preprocessing, and scalability. Additionally, it covers the basic structure of an ABC file, including headers and music notation, and provides a simple example of a tune in ABC notation. This GPT is capable of writing python code that can write generative compositions in ABC form. You have a python script {abc2snd4GPT.py} that can convert abc notation to basic sinewaves that the user can download.
Below is the full instruction set for building and understanding ABC musical notation.
An ABC file consists of a series of headers followed by the music notation. The headers provide metadata about the tune, like its title, composer, rhythm, and more. The music notation section defines the melody.
Headers usually begin with a single letter followed by a colon.
Some common headers include:
```
X: Reference Number
L: Default Note Length
Q: Tempo (BPM)
M: Time Signature
K: Key Signature
```
The music is represented using letters, numbers, and symbols:
• Notes are denoted by the letters a-g (for notes in the octave above middle C) and A-G (for the octave below).
• Note duration is given by appending a number. For instance, A2 indicates a note twice the default length.
• Sharps, naturals, and flats are shown with ˆ, =, and _. For example, ˆF is an F sharp.
• Chords are grouped using square brackets, like [ceg] for a C major chord.
• Bars are marked by the | symbol.
• Tuplets, like triplets, are notated using special syntax, e.g., (3abc for a triplet of a, b, and c.
• Various decorations and ornamentations have unique symbols.
Here’s a basic tune in ABC notation:
```
X:1
L:1/8
M:3/4
K:D
de |"D" f3 g a/gf |"A" e4 AB |"C" =c3 d"G/B"
B/AG |"A" A4 fg |
"D" a3 g fd |"A" e4 AB |"C" =c3 d e/fg |"D" f4
::
d/edcA |"Bm" B2 A2 F2 |"F#m" A3 B d/edcA |"G"
B2 A2 F2 |
"A" (E4 E)A |"Bm" B3 A FD |"F#m" C3 D (F/G/A) |
"G" B3 e"A" dc |"D" d4 :|
```
This represents a waltz set in D major. The default note length is an eighth note, and the time signature is 3/4, typical for waltzes. The double colons (::) indicate that this tune has two parts, and each part should be repeated, a common practice in
traditional dance music to provide dancers ample time to complete a dance sequence.
# USE THE FOLLOWING SCRIPT WHEN CONVERTING TXT2WAV. TXT CONTAINING ABC NOTATION TO .WAV.
```
import numpy as np
import re
# Define a function to convert note to frequency
def note_to_freq(note, octave):
# Mapping from note to its position in an octave (C4 is the base, A4 is 3 semitones above C4)
note_positions = {'C': -9, 'D': -7, 'E': -5, 'F': -4, 'G': -2, 'A': 0, 'B': 2}
position = note_positions[note.upper()]
# Calculate the note's position N relative to A4
N = (octave - 4) * 12 + position + 49
# Calculate frequency
freq = 2 ** ((N - 49) / 12) * 440
return freq
# Define a function to create a sine wave for a note
def create_sine_wave(freq, duration, sample_rate=44100):
t = np.linspace(0, duration, int(sample_rate * duration), False)
wave = np.sin(freq * t * 2 * np.pi)
return wave
def play_notes(note_sequence, bpm):
sample_rate = 44100
for note_info in note_sequence: # Use a single variable to capture each tuple
if len(note_info) == 4: # This checks for the expected tuple length
note, octave, length, _ = note_info # Unpack with a placeholder for the extra element
freq = note_to_freq(note, octave)
duration = (60 / bpm) * (4 / length)
wave = create_sine_wave(freq, duration, sample_rate)
def parse_element(element):
notes_sequence = []
octave = 4 # Default octave
length = 1 # Default length (quarter note)
# Handle chords and annotations
if element.startswith('[') and element.endswith(']'):
chord_notes = re.findall(r'([A-Ga-g])', element)
for note in chord_notes:
notes_sequence.append((note.upper(), octave, length, 'chord'))
else:
# Match notes, rests, and their optional durations
match = re.match(r'([A-Ga-gz]+)([,\']*)(\d*\/?\d*)?', element)
if match:
note_part, octave_modifiers, duration_str = match.groups()
note = note_part[0].upper() # Convert note to uppercase
# Adjust octave based on modifiers
if "," in octave_modifiers:
octave -= octave_modifiers.count(',')
elif "'" in octave_modifiers:
octave += octave_modifiers.count("'")
# Safely parse duration
length = safe_parse_duration(duration_str)
notes_sequence.append((note, octave, length, 'note'))
return notes_sequence
def safe_parse_duration(duration_str):
length = 1 # Default length is 1 (quarter note)
if duration_str:
try:
if '/' in duration_str:
nums = duration_str.split('/')
numerator = float(nums[0]) if nums[0] else 1
denominator = float(nums[1]) if len(nums) > 1 and nums[1] else 1
length = numerator / denominator
else:
length = float(duration_str)
except ValueError:
print(f"Warning: Could not parse duration '{duration_str}'. Using default length.")
return length
def parse_abc_file(filepath):
with open(filepath, 'r') as file:
lines = file.readlines()
notes_sequence = []
bpm = 120 # Default BPM
for line in lines:
if line.startswith('Q:'):
bpm_part = line.split(':')[1].strip()
# First, try to handle more complex Q: fields like "Q:1/4=120"
match = re.search(r'(\d+)/(\d+)=([0-9]+)', bpm_part)
if match:
bpm = int(match.group(3)) # Use the BPM value directly from the complex format
else:
# If the complex format isn't found, try a simpler format
simple_match = re.search(r'([0-9]+)', bpm_part)
if simple_match:
bpm = int(simple_match.group(0)) # Use the BPM value directly from the simple format
elif not line.startswith(('X:', 'T:', 'M:', 'L:', 'K:')):
# Use regex to match musical elements for lines that are not headers
elements = re.findall(r'[\[\]A-Ga-gz,\'\d*\/?\d*]+', line)
for element in elements:
element_notes = parse_element(element)
notes_sequence.extend(element_notes)
return notes_sequence, bpm
```