In which the author reviews software to generate MIDI files
Posted by 02/08/2007
Someone once told me that all programmers fit into one of these 4 groups:
- Musicians (20%) - they deal with abstraction well
- Artists (20%) - they make the code look good (and deal with abstraction well)
- Gear-heads (50%) - they are good at writing device drivers
- Other (10%)- everybody else
I think I'm probably in the Musican/Artist category. They are not that distinguishable. But now, instead of using my Music/Art sense to write code - I want to use my code to write music. Is this possible? My requirements are these:
- I want to get started quickly - make the rudiments of a chord progression and drum track given a 4 hour chunk of time
- I want it to run on my laptop - which is running Windows™. Why I'm running Windows™ is a long story - but try running Linux on a Dell Inspiron 8500 and see how long that lasts
- I merely want to output midi files that I can then import into my software. I don't need real-time processing or anything. And the files will just serve as skeletons
Most packages I have found are not friendly to the musician in me. The musician in me wants as few impediments as humanly possible between me and the music. It's a buzz kill. CSound looks like it could possibly be cool - but if I tried to write music with that it would take me 100 years. Then there is Common Music. But getting it running on Windows is problematic. It looks interesting but there are too many obstacles getting from point A to point B - which involves a combination of my own personal laziness and the basic unfriendliness of Common Lisp. But I may revisit this one. Remember, one of my requirements was get started in one evening.
Another requirement was that it has to run on Windows™. There are a whole host of packages that only run on Linux (and possibly Macintosh). If I were not stuck with my current laptop I might possiby use one of these. My previous experience with installing Linux software on my laptop fills me with dread though. It's hard to imagine this being a pleasant experience. I don't think it's unreasonable to require something that works across platforms. Everything I really like does - Emacs, Vim, PostgreSQL, Python, Ruby, Mzscheme etc...
I found something called MMA in Python, which has, quite possibly, the ugliest web site on the internet. However, it meets some of my requirements. For instance, I want to just write the names of chords to play chords. And I would like to organize those chords by measure. MMA offers this but it is a DSL in Python, which means it's just it's own language (as opposed to an EDSL - Embedded DSL). So I can't really use Python idioms within the code. Here is an example from the tutorial:
// Sample tutorial file // Fella Bird, try 1 Tempo 120 Groove Rhumba 1 F 2 F 3 C7 4 F 5 F 6 F 7 C7 8 F 9 Gm 10 F 11 C7 12 F 13 Gm 14 F 15 C7 16 F / / z!
As you can see it is fairly easy to understand - and easy to make something quickly. The main problem I have with it is the line that says 'Rhumba'. It comes with all these named 'grooves' for the drum beats and bass lines. Which makes it something akin to a karoke generator. You can make your own grooves, but it starts getting a little more complicated at that point:
SeqClear SeqSize 1 Timesig 4 4 Begin Drum Define D1 1 8 90 ; 2.5 8 90 ; 4 8 90 S1 2 8 90 ; 4 8 90 CH1 1 8 90 C1 CH1 * 8 End Begin Bass Define B1 1 4+8 1 90 ; 2.5 8 1 90 ; 3 8 1 90 ; 3.5 4 1 90 ; 4.5 8 1 90 L1 1 2+4 1 90 End Begin Chord Define C1 1 2+4 80; 4 4 80 End Begin Drum-Kick Tone KickDrum1 Sequence D1 End Begin Drum-Snare Tone SnareDrum1 Sequence S1 End Begin Drum-HH Tone ClosedHiHat Sequence C1 End Begin Bass-Simple Voice AcousticBass Sequence B1 Begin Bass-LeftHandPiano Voice Piano1 Sequence L1 Octave 3 // This a new command, but simple to understand End Begin Chord-RightHandPiano Voice Piano1 Sequence C1 End DefGroove Myrock1
That is not too bad in and of itself - but at this point I feel like I'm just learning someone's personal language - which is not all the flexible and doesn't have a lot of features and has limited me to their own conception of the universe. So I cannot do things like loops or organize code in modules or anything I would normally do in Python. And this is a basic complaint I have with Python in general. Whenever anyone uses Python to make something resembling a DSL - it is never just Python extended. It is always a brand new language. I can't say it is impossible to make what I'm thinking of in Python - it just doesn't seem to happen. And if something happens again and again I tend to think that is telling you something.
Brief Aside about CommunismWhich brings me to another point. Someone I know once said something to the effect "When are people going to give up on Communism? It never works". A lot of people still hold on to the idea that Communism is a good idea, but it just has never been implemented correctly.
It sounds good on paper, right? Everyone is equal. Everyone is working together for the community. It's all sharing and holding-hands and everyone is happy. How come it never actually happens that way? Why does it always deteriorate into the proto-typical fascist police state? Maybe it's because that particular concept (Communism) just doesn't fly on a large scale. And how long does it take until you ascribe failure to the entire concept as opposed to the specific implementations?
How it relates to Python
In much the same way I see 2 recurring themes with Python that could possibly be problems with the language, as opposed to specific implementations 1) DSLs deteriorate into personal languages rather than extended Python 2) Libraries are hard to follow on the macro scale. The code itself is easy to read and follow - everything is in accord with the Zen of Python - but the readability of the code from 1000 miles up is never the same as the code itself. The adherence to the principal of readability does not scale to the level of libraries and collections of modules. Try to follow the ElementTree documentation for instance. This is a top-dollar Python package - that is so good it is now included in the standard library. Go to the documentation and try to figure these things out:
- how to add an xml declaration to an outputted file
- how to process an xml file with namespaces
- how to output a file that looks like this:
<field type="text"> <bookmark>Text1</bookmark> <status /> <help /> </field> <field type="text"> <bookmark>Text2</bookmark> <status>Some status</status> <help /> </field>instead of this:
<field type="text"> <bookmark>Text1</bookmark> <status /> <help /> </field><field type="text"> <bookmark>Text2</bookmark> <status>Some status</status> <help /> </field>
It is all very possible. But it's not easy to figure out. The code just doesn't organize it's complexity well. And this is true of FormEncode, Django, SQLObject, SQLAlchemy. The list goes on and on because it includes all Python libraries actually. I can't think of one that is __not__ like that. I don't know what the answer is. I use ElementTree. I get things done and I can force it to do what I need it too. It's quite possible that covers it. If it can be done then it's good enough. Maybe. But I'm still not happy about it. Ruby, I have to say, is better at this complexity thing. Most likely because of the emphasis on objects. If nothing else that helps organize code. And if your brain expects to be referring to classes instead of modules - it is easier to conceptualize the code. The problem with Ruby is it doesn't have a decent XML library either. REXML is weird and slow. Ruby also suffers a bit from the "how can I do this with the fewest number of characters possible" syndrome as well as receiving my personal award for most cloying book on programming ever written. I think I'm the only person in the world that doesn't like the writings of Why. Call me a grumpus.
DSL is just a buzzword
While I'm on the subject - I'm divided over the term DSL anyway. For 1) it it too similar to DSL (Digital Subscriber Line). And for 2) it's just a glorified name for a usable library of functions. That being said, a well written library of functions is thing of beauty so I'm not knocking it entirely. And it might count as a Neologism.
Another package I looked at is PythonMidi. Here is an example:
from MidiOutFile import MidiOutFile out_file = 'midiout/minimal_type0.mid' midi = MidiOutFile(out_file) # non optional midi framework midi.header() midi.start_of_track() # musical events midi.update_time(0) midi.note_on(channel=0, note=0x40) midi.update_time(192) midi.note_off(channel=0, note=0x40) # non optional midi framework midi.update_time(0) midi.end_of_track() # not optional! midi.eof()Which, as you can see, it pretty low level. That not necessarily a bad thing. But it does require some work on my part. For instance, I'd like to just play a note for given period of time - rather than have to say
note_off. So I have to wrap those functions up - and give notes like
0x40friendly names and then figure out how to play more then one note at the same time. What I'm envisioning is code like this:
play_chord('em7', QUARTER), but there is still a good deal of work I have to do before my dream code works. Like a lot of Python libraries - it leaves all the more useful details to me. Call it 'flexibility' or call it 'half-assed'. One man's trash is another one's treasure.
MidiScripter to the Rescue
So both of these packages meet my basic requirements of producing Midi and doing so fairly quickly. But then I found MidiScripter - with which I was able to generate music - and with which I can actually program out the abstractions I need myself (since it's all just Ruby).
#melody.rb program Violin velocity 64 note [C3, E3, G3], Whole note [A3, C3, E3], Whole
#drums.rb velocity 127 7.times do drum Bass_Drum_1 rest Quarter drum Closed_Hi_Hat rest Eighth drum Closed_Hi_Hat, Velocity => 64 rest Eighth drum Electric_Snare rest Quarter drum Closed_Hi_Hat rest Quarter drum Bass_Drum_1 rest Quarter drum Closed_Hi_Hat rest Quarter drum Electric_Snare rest Quarter drum Closed_Hi_Hat rest Quarter endand since it is in actually Ruby I can easily do things like this:
Em7 = [E3, B3, D3, G3] Cmaj = [C3, E3, G3, C4] Dmaj = [D3, Fs3, A3, D4]and
require 'chord_library' def main_progression note Em7, Quarter note Dmaj, Quarter note Cmaj, Quarter note Em7, Quarter endand
4.times do main_progression endand finally
tempo 200 load_track "drums.rb", Channel => Drum_Channel load_track "chords.rb", Channel => 1or something to that effect, and I've got a song. Now That's what I'm talking about .
So this is exactly what I wanted. Within a few hours I was up and running creating Midi files and organizing a little library of 'riffs' and 'chords' and had written a routine to generate each song in the 'album'. This experience reinforced my feeling about a lot of things and led me to the following over-generalization:
Ruby libraries are better at organizing themselves then Python libraries
They are easier to use. Easier to extend. And easier to follow. It is just a tendency, but if it happens over and over again then it's a quality.
Before I go Further - Some Background Information
If anyone is reading this and is not familiar with Python and/or Ruby I have to explain a few things. The languages are more similiar than different, which is why people spend an inordinate amount of time comparing the 2 languages. It's something along the lines of the Bikeshed effect. I find myself slipping into comparing the languages again and again even though I think it's largely a waste of time. I can't explain why.
A lot of languages these days have the means of producing html directly
from code. This was made famous by Java with
RDoc. Python has
pydoc. Sometimes this
is all the documentation you have. And a lot of times, at least with
Java and Ruby, it is enough to get by. This is largely because Java and
Ruby both have the motto "everything is an object". So you can browse
through the conceptual units (objects) - and then browse through what
those conceptual units do (methods). And, if everything is named well,
you can pretty much guess what it does and how it all works together.
Ruby Documentation vs Python Documentation
So why is Ruby code better at handling it's complexity on a larger scale then Python - and thus better able to create useful libraries (which is my contention)? In Python you can have objects (classes) with methods or you can have a bunch of related functions that are not attached to an object. In Ruby that is not true, because everything is an object. Maybe that is one reason why. It's also possible that it's just me. Because I programmed in Java for such a long time I'm looking for Objects and Classes and Methods. And because the automatically generated documentation for Objects organizes itself in a way that's easy to follow.
Why would a module system such as Python employs, which inclusively allows classes or functions within it's organizational unit tend more to produce an inherently disorganized library? The module system seems like such a great idea. And it doesn't seem like classes are __always__ a good idea at all. But there is a huge grey area between deciding a set of functions needs to be encapsulated in an object, and just leaving them as namespaced functions. And Ruby solves the quandry by eliminating the decision all together. Not necessarily the best approach. But it does seem to pay off in self-documentation, usability and clarity.
Programming needs more facts - less religion
I have no favorite language here. I'm a grumpus. I'm a mediocre programmer with limited faculties. I just kludge together programs and then make observations. I know Ericsson supposedly did a study that showed programming in a functional language was more productive - which is why they invented Erlang. I'd like to know where that study is and the methodology used. Most everything at this point in the field of programming is nothing but religion and subjectivity. Is there a way to say quantitatively what's best? "It has been concluded that if you use a namespace based module system that does not enforce class-level encapsulation of methods, then it takes 15% more time to document, and 11% more time to use effectively". Is there a way to test that I wonder? Because that would be much more useful than "Ruby is better than Python".
I Actually Like Python
Don't get me wrong. I still like Python. In fact I like it better than Ruby. I get very tired of
end end end endlines - and cute tricks with
:&and the like. I like the significant whitespace, even though it's annoying sometimes. I don't mind the limited
lambdastatement, because if a function is long enough for 2 lines it's probably worth naming, I can live with
selfall over the place, I can muddle through the documentation and code, and it's only a programmer that wants a DSL in actual Python anyway. Most people would rather work with the 'mini-language', the most obvious success story of that being Django. So I have nothing useful to say. Just that as a musician looking for code to output Midi, I found the most useful package in Ruby, rather than Python. What I should do is write a wrapper around PythonMidi to emulate MidiScripter - which is all MidiScripter is anyway. I'm just lazy.
One more thing - Haskcore
- but is a pain to compile and install on Windows. I had to get the
latest version from darcs and comment out various entries in the
Setup.lhs file until it finally installed. It seems to have
a few Posix dependencies in there.
And once you've compiled it and installed it I have to say there is nothing intuitive about Haskell. In fact I'd say the best approach to Haskell is to learn the language first, and then try to do something with it. It's not "hacker friendly" in that respect. At the same time I am drawn to this language for a number of reasons which I will later expound upon. I have a set of criteria for programming languages too - and Haskell meets a great number of those criteria.