midd-media 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.3
2
+ Name: midd-media
3
+ Version: 0.1.0
4
+ Summary:
5
+ Author: Christopher Andrews
6
+ Author-email: candrews@middlebury.edu
7
+ Requires-Python: >=3.9
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Dist: pillow (>=11.1.0,<12.0.0)
15
+ Requires-Dist: pygame (>=2.6.1,<3.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ # midd-media
19
+ Libraries and examples for CSCI 0145
20
+
21
+
22
+ ## middsound
23
+
24
+ A library for interacting with sound. This provides a thin wrapper around the wave library. This also requires pygame for sound playback.
25
+
26
+
27
+ ##middimage
28
+
29
+ Another thin wrapper to simplify the interface. This wraps pillow while providing an interface very close to that of middsound.
30
+
31
+
@@ -0,0 +1,13 @@
1
+ # midd-media
2
+ Libraries and examples for CSCI 0145
3
+
4
+
5
+ ## middsound
6
+
7
+ A library for interacting with sound. This provides a thin wrapper around the wave library. This also requires pygame for sound playback.
8
+
9
+
10
+ ##middimage
11
+
12
+ Another thin wrapper to simplify the interface. This wraps pillow while providing an interface very close to that of middsound.
13
+
@@ -0,0 +1,22 @@
1
+ [project]
2
+ name = "midd-media"
3
+ version = "0.1.0"
4
+ description = ""
5
+ authors = [
6
+ {name = "Christopher Andrews",email = "candrews@middlebury.edu"},
7
+ {name = "Andrea Vaccari",email = "vaccari@middlebury.edu"}
8
+ ]
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ dependencies = [
12
+ "pygame (>=2.6.1,<3.0.0)",
13
+ "pillow (>=11.1.0,<12.0.0)"
14
+ ]
15
+
16
+ [tool.poetry]
17
+ packages = [{include = "midd_media", from = "src"}]
18
+
19
+
20
+ [build-system]
21
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
22
+ build-backend = "poetry.core.masonry.api"
File without changes
@@ -0,0 +1,110 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module provides a thin wrapper around pillow allowing us to easily create and manipulate images.
4
+
5
+ @author: C. Andrews, A. Vaccari
6
+
7
+ 2022-04-20 Modified __repr__ to fit an entire line if 8-bit mono
8
+ 2025-01-06 Removed the dependency on `numpy` and added a `Row` class to allow for 2D array notation.
9
+ """
10
+
11
+
12
+ from PIL import Image
13
+
14
+
15
+ class MiddImage:
16
+ """
17
+ This class provides a thin wrapper around the PIL Image class. It boils access down to some basic functionality and also allows for direct access to the pixels.
18
+
19
+ Access to the pixels can be done using 2D array notation (e.g., img[x][y]) or using a tuple (e.g., img[x,y]). The former will feel more natural to new Python programmers, but is 50% slower.
20
+ """
21
+ class Row:
22
+ """
23
+ Hidden helper class that allows us to access a row of pixels using 2D array notation.
24
+ """
25
+ def __init__(self, image, row):
26
+ self.image = image
27
+ self.row = row
28
+
29
+ def __getitem__(self, index):
30
+ return self.image[self.row, index]
31
+
32
+ def __setitem__(self, index, value):
33
+ self.image[self.row, index] = value
34
+
35
+ def __init__(self, data=None, width=None, height=None, mode='RGB' ):
36
+ if data is None:
37
+ self._data = Image.new(mode, (width, height))
38
+ else:
39
+ self._data = data
40
+ self._pixels = self._data.load()
41
+
42
+ def __getitem__(self, index):
43
+ """
44
+ Get the pixel at the specified 'index'.
45
+ The 'index' should be a tuple.
46
+ """
47
+ if type(index) == int:
48
+ return MiddImage.Row(self, index)
49
+ else:
50
+ return self._pixels[index]
51
+
52
+
53
+ def __setitem__(self, index, value):
54
+ """
55
+ Set the pixel at location 'index'.
56
+ The 'value' should be a three channel tuple.
57
+ """
58
+ self._pixels[index] = value
59
+
60
+
61
+ def __getattr__(self, attr):
62
+ """
63
+ Passes through attributes to the underlying image object.
64
+
65
+ Most important attributes are `width`,`height`, and `mode`.
66
+ """
67
+ return getattr(self._data, attr)
68
+
69
+
70
+ def __repr__(self):
71
+ """
72
+ Show the pixels of the image.
73
+ """
74
+ return repr(list(self._data.getdata()))
75
+
76
+ def save(self, filename):
77
+ """
78
+ Save the image as 'filename' into the current working directory.
79
+ """
80
+ self._data.save(filename)
81
+
82
+ def show(self):
83
+ """
84
+ Show the image.
85
+ """
86
+ self._data.show()
87
+
88
+ def copy(self):
89
+ """
90
+ Returns a new MiddImage with identical contents.
91
+ """
92
+ new_image = MiddImage(self._data.copy())
93
+ return new_image
94
+
95
+
96
+ def new(width, height, mode='RGB'):
97
+ """
98
+ Create a new image with the specified width and height.
99
+ """
100
+ return MiddImage(width=width, height=height, mode=mode)
101
+
102
+
103
+ def open(filename):
104
+ """
105
+ Open an image file located in 'filename' (which is assumed to be in the working directory
106
+ or an absolute path.
107
+ """
108
+ image_data = Image.open(filename)
109
+ new_image = MiddImage(data=image_data)
110
+ return new_image
@@ -0,0 +1,136 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module provides a thin wrapper around the wave library allowing us to
4
+ easily create and manipulate wav files.
5
+
6
+ @author: C. Andrews, A. Vaccari
7
+
8
+ Update: 2024-10-18 - Included the allowedchanges=0 option in pygame.mixer.init()
9
+ to solve an issue with double speed in Windows.
10
+ Update: 2024-03-26 - Updated to use pygame (simpleaudio is no longer maintained)
11
+ Update: 2022-04-20 - Added ValueError exception when trying to play an empty sound
12
+ Update: 2019-11-03 - Added support for sharps: capital letters will produce sharps
13
+ Update: 2019-04-02 - Added support for len(), removed extraneous functions
14
+ Update: 2016-08-23 - Changed play to use system tools
15
+ Update: 2016-08-22 - Converted to use __getitem__ and __setitem__
16
+
17
+ """
18
+
19
+ import wave
20
+ import array
21
+ import pygame
22
+
23
+
24
+ # These two constants are the maximum and minimum values allowed for our samples
25
+ MAXVALUE = 32677
26
+ MINVALUE = -32768
27
+
28
+
29
+ class Sound:
30
+ """
31
+ This class is a container that holds one 'sound'. It provides an interface
32
+ mirroring the wave library that hides some of the details such as setting the
33
+ framerate and the sample width, as well as the interactions with the array
34
+ library which allows us to build the sound before writing it out to a file on
35
+ disk.
36
+ """
37
+
38
+ def __init__(self, framerate=44100):
39
+ """
40
+ Initialize the sound with a data store for our samples and a default
41
+ sample rate of 44kHz
42
+ """
43
+ self.data = array.array('h')
44
+ self.numchannels = 1
45
+ self.framerate = framerate
46
+ pygame.mixer.init(frequency=self.framerate, size=-16, channels=self.numchannels, allowedchanges=0)
47
+
48
+
49
+
50
+ def __getitem__(self, index):
51
+ """
52
+ Get the sample at location index.
53
+ """
54
+ return self.data[index]
55
+
56
+ def __len__(self):
57
+ """
58
+ Support the len() function.
59
+ """
60
+ return len(self.data)
61
+
62
+
63
+ def __setitem__(self, index, value):
64
+ """
65
+ Set the value of a particular sample.
66
+ """
67
+ self.data[index] = value
68
+
69
+ def append(self, sample):
70
+ """
71
+ Append one sample on to the end of the sound.
72
+ """
73
+ self.data.append(sample)
74
+
75
+ def save(self, filename):
76
+ """
77
+ Save the sound as 'filename' into the current working directory.
78
+
79
+ Under the hood, this is the point when the wav file is actually created
80
+ and the samples are written into it.
81
+ """
82
+ snd = wave.open(filename, 'wb')
83
+ # nchannels, sampwidth, framerate, nframes, comptype, compname
84
+ snd.setparams((self.numchannels, 2, self.framerate, len(self.data), 'NONE', 'not compressed'))
85
+ snd.writeframes(self.data.tobytes())
86
+ snd.close()
87
+
88
+ def play(self):
89
+ """
90
+ Play the sound.
91
+ """
92
+ # Check if the data is empty. If it is, the backend would
93
+ # crash with an obscure message.
94
+ if len(self.data) == 0:
95
+ raise ValueError("Sound data empty. The sound should contain at least one sample")
96
+ sound = pygame.mixer.Sound(self.data)
97
+ sound.play()
98
+
99
+
100
+
101
+ def new(framerate=44100):
102
+ """
103
+ A convenience function for creating a new Sound object.
104
+ """
105
+ return Sound(framerate=framerate)
106
+
107
+
108
+ def open(filename):
109
+ """
110
+ Open a wave file located in 'filename' (which is assumed to be in
111
+ the working directory, or an absolute path).
112
+ """
113
+ soundObj = Sound()
114
+
115
+ snd = wave.open(filename, 'rb')
116
+ soundObj.numchannels = snd.getnchannels()
117
+ soundObj.framerate = snd.getframerate()
118
+ soundObj.data.frombytes(snd.readframes(snd.getnframes()))
119
+
120
+ snd.close()
121
+ return soundObj
122
+
123
+ def get_frequency(note):
124
+ """
125
+ A quick converter for getting notes of various frequencies. This covers the
126
+ piano keyboard.
127
+ The sharp version of a, c, d, f, and g can be obtained using capital letters.
128
+ """
129
+ d = {'a':27.5,'b':30.8677,'c':32.7032,'d':36.7081,'e':41.2035,'f':43.6536,'g':48.995,
130
+ 'A':29.1353,'C':34.6479,'D':38.8909,'F':46.2493,'G':51.913}
131
+ frequency = d[note[0]]
132
+ octave = int(note[1])
133
+ if note[0] > 'b':
134
+ octave -= 1
135
+ frequency *= 2**octave
136
+ return frequency