jdan / melopy Goto Github PK
View Code? Open in Web Editor NEWPython music library
Home Page: http://jdan.github.io/Melopy
License: MIT License
Python music library
Home Page: http://jdan.github.io/Melopy
License: MIT License
I would love to see an implementation of a status bar for when a song is rendering. However, I think something like printing "10% done! 20% done!" is ugly. Is it possible to draw a static textbar on console? Similar to how something like wget looks when you fetch data from the web.
I now want to bring to life. The main purpose of Melopy was too allow me to write melodies with python code. This looks great with Melopy's functions, but I'm looking to add something cool to utility
- a parser for a new syntax used for writing music.
It started with the idea of Mary Had a Little Lamb.
4|EDCDEEE-DDD-EGG-EDCDEEEEDDEDC
What does this do? First it read's the octave. 4. Then a pipe - then some notes. The letters will correspond to notes in the pre-defined octave, and dashes represent rests. From here I decided to conjure up a sort of syntax.
From here I decided that each new line could be read as a change in octave. For instance, if we were to play the C major scale...
4|CDEFGAB
5|C
We would need to jump to the 5th octave. We do this by going to the next line, defining the octave again, and continuing to write our notes.
So what kind of notes are these? Well, like music - I wanted the "default" so-to-speak to be quarter notes. From here, we could shorten them, or make them longer. As follows.
example: ABC
meaning A(BC) is a quarter note A followed by two eighth notes, B and C
we can have various levels of parenthesis to make faster notes.
[A] becomes a half-note A and [[D]] becomes a whole note D
((-)) is a sixteenth rest and [-] is a half rest.
{DF#A} would correspond to D major
4|{A||5|E}||4| ...
corresponds to A major (see how we had to jump an octave? (and come back)That's all I have for now as far as syntax goes. We need to add things that allow you to define the waveform, title, etc before the actual notes. This is a design decision that needs to be made. We also need syntax for ties.
The important thing is to be very thorough in your documentation.
As far as a file goes, I believe this should be placed in utility.py as the method parse()
The readme has an example that uses melopy.major_scale('C5')
but there is no major_scale
function. I think this should be something like melopy.generateScale('major', 'C5')
.
I will be happy to fix this if someone can confirm that I'm not just missing something.
If no one replies in a week or so I'll assume I'm right and make the change.
We need to make a play()
or sample()
method which gives Melopy that capabilities of playing sound right from .data
. This shouldn't be terribly hard to achieve, considering programs like Audacity do it without issue. Any ideas?
Output of tests: https://gist.github.com/a433238a7a236a83ceda
This is as of 5b8447e
Would love some momentum on this. A MIDI option is pretty easy to implement and would attract a lot of people.
Do we need it? All it has done for me so far is create a headache.
So I made myself a clone of Melopy and downloaded it onto my windows and Linux machine. Whenever I try to use setup.py it spits back an error!
"Traceback (most recent call last):
File "setup.py", line 7, in
from distutils.core import setup, find_packages
ImportError: cannot import name find_packages"
Anyone know whats going on? I just wanted to mess around with it, I'm not a python wizard!
Consider the following code.
from melopy import *
m = Melopy()
print m.volume
Since volume
defaults to 50, the following should print 50. If we direct ourselves to the project root directory and run this code. It works perfectly.
However, if we navigate into the examples/
directory and run this code, the output is 16383.5. I cannot for the life of me figure out why this is happening.
On my machine __file__
returns just the filename when called from the examples dir so
m.parsefile(os.path.dirname(__file__) + '/meeps/furelise.mp')
results in /meeps/furelise.mp
which does not exist.
A better way would be:
m.parsefile(os.path.abspath('./meeps/furelise.mp'))
When notes are rendered, their wave data is added for some number of samples and then stopped. If the last sample rendered for a note has a large amplitude, this can result in adjacent samples with very large differences. This produces a click in the audio.
Section 2 of this page describes the problem with some helpful images: https://ask.audio/articles/5-mistakes-to-avoid-when-editing-audio
Two potential solutions:
Continue rendering a note until it gets to some low threshold where a click will not occur. This could extend notes by nearly 1/2 the note period in the worst case (around 1/20th of a second for low notes?). Need protection against a situation where a note frequency is high relative to the sampling frequency and it takes multiple periods (if ever) before you get a low amplitude sample.
Fade every note to zero over the last few samples. Depending on how many samples is necessary to fix the problem, this could change the character of the note in an undesirable way.
I posted this on the reddit thread, but I thought I'd dump it in here too just to gauge the response of those not on the reddit thread. It might be a smart idea to allow people to choose an output, as final objects are sometimes useful. It's not really that big of a deal, nor is it a dealbreaker in overhead/runtime, just an extra if statement.
Like so:
def generate_minor_triad(start,returnType="list"):
"""Generates a minor triad using the pattern [3,4] (Returns: List)"""
minor_triad = [3, 4]
output = iterate(start, minor_triad)
if returnType == 'list':
return output
elif returnType == 'tuple':
return tuple([i for i in y])
nice work!
my first thought on reading through the syntax was... how do you do triplets? the halving/doubling thing is a clever simplification and other multiples like 5 etc are more rare, but triplets are pretty essential.
I thought maybe you could get by with just a { }
operator... say you're currently counting in 'quarter notes', then { }
would give you quarter-note triplets and [{ ... }]
would give you eighth-note triplets etc.
In replying to my other comment you suggested a syntax like {3}( ... )
. I would interpret this maybe as a way to specify the multiple of the following brackets, i.e. {2} is the default if not specified. Reading about triplets they actually represent 'three notes in the space of two', i.e. a multiple of 1.5.
But reading more about things like quintuplets, conventional notation allows more awkward fractions such as 5/3... at which point you wouldn't want to use a 'floating point' type of syntax.
So that would suggest (based on your example) something like {3,2}[ ... ]
for triplets. But then what would {3,2}( ... )
mean? If behind the scenes we're converting that syntax to a calculation like (3/2) * x
or (3/2) / x
it turns out only one of those gives a musically relevant answer. To get slower triplets correctly you'd have to do ({3,2}[ ... ])
.
So maybe the 'fractional brackets' should be restricted to use with square 'speed up' brackets only, or not even associated with either [ ]
or ( )
brackets at all, to avoid confusion.
So I would come back around to { ... }
'triplet brackets' as shorthand, with an extended syntax like maybe {5/3: ... }
for weirder subdivisions.
http://audiere.sourceforge.net/
We need to include Audiere in the project in order to allow Live Playing (#22)
Hopefully someone can make this process painless.
How about using 4spaces instead of tab?
>>> # Returns the frequency of the note (key) keys from A0
>>> melopy.frequency_from_key(49)
440
>>> # Returns the frequency of a note represented by a string
>>> melopy.frequency_from_note('A4')
440
As you can see, the README is outdated. We have since divided the project into 3 sub-categories, melopy
, scales
, and utility
.
The README must change accordingly
melopy.py
contains the Melopy class - we use this for generating noisescales.py
contains methods for generating arrays of scales, chords, etc.utility.py
contains operations for determining the frequency of a given note, the note from a given key, etc.We should subdivide the README accordingly.
I am completely unfamiliar with open-source licenses, but I have seen some developers get MIT licenses before anyone else even touches their project. Maybe getting one might not be such a bad idea? What's the process?
The reason I've marked this as minor is that anyone with a good music theory background should be able to fix this pretty quickly.
>>> from melopy.scales import *
>>> minor_scale('C')
['C4', 'D4', 'D#4', 'F4', 'G4', 'G#4', 'A#4']
>>>
As you can see here, Melopy determines that D and D# are both in C minor. This is technically correct, but the real way to write this would be to use D and then Eb. The same goes for Ab instead of G#. Essentially, the rule of thumb is that the same letter can't appear twice in a scale. The error lies in our iterate()
method in scales.py
.
def iterate(start, pattern, rType="list"):
"""Iterates over a pattern starting at a given note"""
start_key = key_from_note(start)
ret = [start_key]
for step in pattern:
ret.append(ret[-1] + step)
ret = map(note_from_key, ret)
return bReturn(ret, rType)
All we need is to implement an error check that prevents the same letter from appearing twice.
Tests are very minmal and seem to bring out a odd nose bug. It would be a good idea to refactor. I'd be more then happy to work on this. This is a prerequisite to #37
The source currently contains a mix of both tabs and spaces. I'd personally prefer spaces (4-space indentation), but either will do as long as we're consistent.
Gist is here: https://gist.github.com/7aed4c886398e10fcec2
So I'm going to start publicizing our music-writing syntax a little more, and I am suggesting a name change from meeps
to something that doesn't sound as dumb. Changes will be reflected in the examples/
directory (also changing the file extensions).
Currently, Melopy works pretty well with single notes. Introduce chords and you've got trouble. You can see my login in the add_wave() method, but it really doesn't work very well. It also currently impossible to generate chords from add_quarter_note(), etc.
Essentially, I do not know how chords work as far as data in the wave file goes. Do you add the value to the one that's currently there? Do you average them?
This needs some fixing, and some reorganizing. Let's rethink chords.
Just following the steps on the README...
airosol:Projects jordan$ mkdir Melopy_ghost
airosol:Projects jordan$ cd Melopy_ghost/
airosol:Melopy_ghost jordan$ git clone [email protected]:prezjordan/Melopy.git .
Cloning into ....
remote: Counting objects: 219, done.
remote: Compressing objects: 100% (159/159), done.
remote: Total 219 (delta 100), reused 176 (delta 57)
Receiving objects: 100% (219/219), 31.09 KiB, done.
Resolving deltas: 100% (100/100), done.
airosol:Melopy_ghost jordan$ ls
MIT-LICENSE melopy tests
README.markdown requirements.txt
examples setup.py
airosol:Melopy_ghost jordan$ python setup.py install
running install
error: can't create or remove files in install directory
The following error occurred while trying to add or remove files in the
installation directory:
[Errno 13] Permission denied: '/Library/Python/2.7/site-packages/test-easy-install-1964.write-test'
The installation directory you specified (via --install-dir, --prefix, or
the distutils default setting) was:
/Library/Python/2.7/site-packages/
Perhaps your account does not have write access to this directory? If the
installation directory is a system-owned directory, you may need to sign in
as the administrator or "root" account. If you do not have administrative
access to this machine, you may wish to choose a different installation
directory, preferably one that is listed in your PYTHONPATH environment
variable.
For information on other options, you may wish to consult the
documentation at:
http://peak.telecommunity.com/EasyInstall.html
Please make the appropriate changes for your system and try again.
...sudo works, however...
airosol:Melopy_ghost jordan$ sudo python setup.py install
Password:
running install
running bdist_egg
running egg_info
creating Melopy.egg-info
writing Melopy.egg-info/PKG-INFO
writing top-level names to Melopy.egg-info/top_level.txt
writing dependency_links to Melopy.egg-info/dependency_links.txt
writing manifest file 'Melopy.egg-info/SOURCES.txt'
reading manifest file 'Melopy.egg-info/SOURCES.txt'
writing manifest file 'Melopy.egg-info/SOURCES.txt'
installing library code to build/bdist.macosx-10.7-intel/egg
running install_lib
running build_py
creating build
creating build/lib
creating build/lib/melopy
copying melopy/__init__.py -> build/lib/melopy
copying melopy/melopy.py -> build/lib/melopy
creating build/lib/tests
copying tests/__init__.py -> build/lib/tests
copying tests/melopy_tests.py -> build/lib/tests
creating build/bdist.macosx-10.7-intel
creating build/bdist.macosx-10.7-intel/egg
creating build/bdist.macosx-10.7-intel/egg/melopy
copying build/lib/melopy/__init__.py -> build/bdist.macosx-10.7-intel/egg/melopy
copying build/lib/melopy/melopy.py -> build/bdist.macosx-10.7-intel/egg/melopy
creating build/bdist.macosx-10.7-intel/egg/tests
copying build/lib/tests/__init__.py -> build/bdist.macosx-10.7-intel/egg/tests
copying build/lib/tests/melopy_tests.py -> build/bdist.macosx-10.7-intel/egg/tests
byte-compiling build/bdist.macosx-10.7-intel/egg/melopy/__init__.py to __init__.pyc
byte-compiling build/bdist.macosx-10.7-intel/egg/melopy/melopy.py to melopy.pyc
byte-compiling build/bdist.macosx-10.7-intel/egg/tests/__init__.py to __init__.pyc
byte-compiling build/bdist.macosx-10.7-intel/egg/tests/melopy_tests.py to melopy_tests.pyc
creating build/bdist.macosx-10.7-intel/egg/EGG-INFO
copying Melopy.egg-info/PKG-INFO -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
copying Melopy.egg-info/SOURCES.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
copying Melopy.egg-info/dependency_links.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
copying Melopy.egg-info/top_level.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/Melopy-0.0.0-py2.7.egg' and adding 'build/bdist.macosx-10.7-intel/egg' to it
removing 'build/bdist.macosx-10.7-intel/egg' (and everything under it)
Processing Melopy-0.0.0-py2.7.egg
Copying Melopy-0.0.0-py2.7.egg to /Library/Python/2.7/site-packages
Adding Melopy 0.0.0 to easy-install.pth file
Installed /Library/Python/2.7/site-packages/Melopy-0.0.0-py2.7.egg
Processing dependencies for Melopy==0.0.0
Finished processing dependencies for Melopy==0.0.0
airosol:Melopy_ghost jordan$ ls
MIT-LICENSE dist setup.py
Melopy.egg-info examples tests
README.markdown melopy
build requirements.txt
Is there a reason for this behavior?
When using the render function, the printing of the progress bar is a significant percentage of the total time of the function. Printing should be optional. For reference, printing the progress was added in response to #28 .
I'd like to try something where a new named argument is passed to render to supply a callback function. The callback function will be called every time the percentage changes. I'll add a new function that will replicate the current behavior and that will be the default. If something that isn't "callable" is passed (like None or "" or False) we'll skip the callback and print nothing resulting in faster performance.
If this isn't workable right now, we should at least end up with a flag to disable printing the progress bar.
I think the logic for generating scales might get big enough to deserve it's own separate module
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.