-
Notifications
You must be signed in to change notification settings - Fork 0
/
pixelnoise.py
119 lines (93 loc) · 3.04 KB
/
pixelnoise.py
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
#! /usr/local/bin/python3
import sys
import math
from pyknon.genmidi import Midi
from pyknon.music import Note
from pyknon.music import Rest
from pyknon.music import NoteSeq
from PIL import Image
# TODO:
# * add commandline argument support (ie -i for image -> midi, -m for midi ->
# image, -l to limit the maximum number of notes)
#
# * add error handling
#
# * implement auxilary functions:
# - note -> pixel:
# /!\ Important prerequisite: figure out how to open & read a midi
# file in python /!\
# - note -> R
# - note -> G
# - note -> B
# - note -> etc...
def pix2note(pixel):
"""
Converts an RGB pixel into a PyKnon Note
Red -> duration
Green -> octave
Blue -> exact note
Overall Brightness -> volume
Use:
pix2note(pixel)
Arguments:
pixel: a 3-uple representing the RGB values of the pixel
"""
letters = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#']
r, g, b = pixel
# Red is the duration in beats, a full red value (of 255) will last 4
# beats, or one measure of 4:4
duration = r / 64
# Green is the octave number, it is calculated assuming 8 octaves (from 0
# to 7)
octave = g // 32 + 1
# Blue is the note letter
letter = letters[math.floor(b // (256 / 12))]
# Overall Brightness is the volume
volume = 0.375*r + 0.5*g + 0.125*b
return Note(letter + str(octave), duration)
def pix2noteseq(pixelmap, width, height):
"""
Convert a PIL pixel map to a PyKnon NoteSeq
Use:
pix2noteseq(pixelmap)
Arguemnts:
pixelmap: the PIL pixel map of the image
width: the width in pixels
height: height in pixels
This function presumes the pixel map is in RGB and correct behavior when
otherwise is not at all guaranteed.
"""
notes = NoteSeq()
# Iterate over the pixels, starting at the top left and working
# colomn by colomn
for y in range(height):
for x in range(width):
notes.append(pix2note(pixelmap[x,y]))
if y == math.ceil(height/2) and x == math.ceil(width/2):
print("50% done...")
return notes
if __name__ == "__main__":
# mode == '-i' | '-m'
modes = ['-i', '-m']
mode = sys.argv[1]
if mode not in modes:
raise Exception('wrong mode')
filepath = sys.argv[2]
if mode == '-i':
image = Image.open(filepath)
width, height = image.size
print("size: {0} * {1}".format(width, height))
pixels = image.load()
# Main Loop, generates notes from pixel RGB values
print("Generating note sequence...")
notes = pix2noteseq(pixels, width, height)
# Generate and write the midi file to disk
print("Generating the Midi file...")
midi = Midi(number_tracks=1, tempo=90)
midi.seq_notes(notes, track=0)
print("Writing Midi file...")
midi.write(sys.argv[2][:-3] + 'mid')
print("Done.")
elif mode == '-m':
# TODO
pass