Mozart golf - mini "Rondo"

13

3

Output "Mozart - Alla Turca" to stdout (see the sample for "reference implementation")

Try to find how to pack both synthesizer and the music into minimal size.

Requirements:

  • Format suitable for feeding into aplay -f cd (signed 16-bit little endian, 2 channels);
  • Entire music should be played (no skipped notes or parts, at least not less than in sample program), polyphony is not required though;
  • Can't just call /usr/bin/timidity, /usr/bin/sox or something like that (e.g. require to install a special music module);
  • Can't access network or assume that the music is available locally;

"Reference implementation" with parsable score: https://gist.github.com/vi/5478693
(Old sample Perl program: https://gist.github.com/vi/5447962)

Vi.

Posted 2013-04-23T22:45:39.593

Reputation: 2 644

Do you have a link to sheet music? – beary605 – 2013-04-23T23:44:49.347

No currently, I was typing the sample program by listening and trial&error. Now looking for... – Vi. – 2013-04-24T00:56:43.640

For example, this.

– Vi. – 2013-04-24T00:59:03.100

Also, I assume you've already realised this, but everyone is going to use square waves. – Peter Taylor – 2013-04-24T07:12:42.153

Related question – Peter Taylor – 2013-04-24T08:44:12.317

@Peter Taylor, Polyphony and trying it to make sound nicer can be bonus points. – Vi. – 2013-04-24T11:07:17.990

Having issue with your example - getting: Bareword found where operator expected at ./rondo.pl line 39, near "s![^ #]!!gr" syntax error at ./rondo.pl line 39, near "s![^ #]!!gr" – lochok – 2013-04-25T06:45:53.673

@lochok, Does your Perl understand s/$pat/$sub/r expression? You may substitute it with my $tmp = $1; $tmp =~ s![^ #]!!g; my $n = length($tmp); – Vi. – 2013-04-25T08:35:03.433

@Vi., /r was only introduced in 5.16. Perhaps you should update the code to get rid of it, so more people can run it. – None – 2013-04-25T12:27:45.150

@dan1111, Done. (Note: I feel the music itself is a bit wrong in one place, maybe I'll fix it later also). – Vi. – 2013-04-25T16:25:09.913

2Note: the arrangement in the linked score is pretty useless for anyone trying to implement this with only one voice. I've downloaded various MIDI files and they don't agree on all of the notes - they can be in the same key, but disagree on some notes by 4 semitones! To make this a well-specified problem it really needs a single canonical score (preferably in some easily parsed format, so that implementers can convert it to a format convenient for their implementation without introducing transcription errors). – Peter Taylor – 2013-04-27T07:35:23.910

@Peter Taylor, Done (based on my MIDI file). – Vi. – 2013-04-28T22:36:26.727

Answers

11

Polyphonic, Haskell, 2826 3177 4719

Audio output: https://www.dropbox.com/s/nba6489tfet740r/hs-golf-turca.ogg

Features:

  • All notes from the right hand. I could of course add the left hand, too (done that).
  • Proper articulation of the staccato notes etc.
  • Reasonably nice sound with dynamics. Not just simple volume modulation, but proper morphing of the attack character and overtone content, like you get on a real piano actually, rather more... hey this piece is supposed to be imitating Turkish Janissary bands, right?
  • Reverb. Doesn't sound amazingly great, but not too bad, either.
  • Dynamic compression. Don't ask...
  • Dithering of the output. This is kind of ridiculous: with proper 16-bit resolution, hardly anybody would hear the quantisation artifacts, but to avoid including the binary library, I effectively use only 7-bits resolution, which I can cover with ASCII output. The dither itself is rather loud, no noise-shaping...
  • Multithreaded calculation of polyphonic chords.

import Control.Parallel
main=mapM_ (\(d,s)->(\p->p>>p>>p>>p).putChar.toEnum.round.(+d).(*62).min 2.abs$s+1).zip(dθ 1).lim.rev.hgp 9. pl 9e6 (\_->0) . ä
 $[mT%8.1,t2%16.1,t3(∡7)%8,t4%8,t5%16,t3(∡7)%8,mT%8.1,t2%16.1,t3(tev arp8)%8,cdT%99] >>= \e->[e,e]
mM=ä[2-^8,1-^8,0-^8,1-^8,3-^4]
cM=ä[7-^20,8-^20,9.^4,F[(7,0),(6,1)](map((∡2).(.^4))[6,5,6])%0.75]
cMv=ä[10-^2,8.^4,9.^4,hom(.^4)[[24,5],[23,8,12],[22,4],[21,6,9],[22,3],[19,5,8],[20,4],[18,6,9],[17]]#7&(-14)%2.5,tr 2%0.4,1-^9,2-^12,1-^1]%4.5
 ⋎(ä[6-^4,lp(8.^4∡3)%(3/4),sil%2,lp(5.^4∡3)%h,lp(5.^4∡2)%h,1-^1∡7]&(-14)#7#4%5)
mMa f=ä[(1-3*f).^4,lp(5.^4∡(-2-f))%0.75,mMa f%1.5,mMa(f*2)%h,mMa f%1]#7
mTm=ä[mM%1,mM&2%1,mM#4&4%h,mM&7%h,mM&7%1,8.^4,ä[10.^4]%0.2,cM%1,cM%1,cM%0.85,ä[4.^4∡2,5.^2]#6#4%2]#7
mT=p$ä[mTm%8.1⋎(ä[sil%h,mMa 0%4,mMa 1%2.75,2.^4,(-2)-^2]&(-7)%8)]
m2=ä[ä(map((∡2).(.^4))[1,2,3,3]++[es[6,5,4,3]%h]++[0-^2∡2])%2
 ⋎(ä[sil%h,1.^4,8.^4,3.^4,10.^4,5-^2]⊿1.3&(-14)%2)]
t2=p$ä[m2&2%1.8,0-^5,m2&2%2,m2#7%1.8,(-2)-^5,m2#7%2,mT%3.5,cMv]
m3=ä$[3-^4,4-^4,5-^2]++map(-^4)[3,4,5,4,3,2,1,2,3,4,2,0]
m3a=ä[(ä[sil%(1/8),lp(8.^4)%1]:zw(\d n->ä[sil%(d/24),n-^1]⊿cos d)[0..][1,3,5],s),m3a%1]
m3ra=(map((%1). \[a,b,c]->es[a,c,b,c,a,c,b,c])[[1,3,5],[1,4,6],[-2,0,5]]!!)
t3 o=ä[ä[o$ḋ[m3%4,m3%2.5,1-^4,4-^4,2-^4,0-^4]&(-2)%7.5,1-^2∡7]%8
 ⋎(ḋ[sil%(3/8),m3a&4%2,m3a%h,m3a#4%h,m3a&1%1,m3a&4%2,m3a%h,m3a&1%(5/8),5-^2]&(-18)%8)]
mQ=es[2,1,0,2]
m4=mM⇆4
i4 e=ḋ[m4⇅11%h,m4⇅9%h,mQ⇆4⇅8%h,F[(5,e),(4,1)][mQ⇅7%h,mQ⇅5%h,m4&5%h,m4&7%h]%2,es[10,9,10,9]#2%h ]
mla[b,c,d]=ä[b-^4,lp(c-^4⋎(d-^4))%1]%1
i4a=ḋ[sil%h,ä(map mla[[1,3,5],[2,4,5],[1,3,5],[0,2,5]])#5%4,ä(map mla[[1,3,5],[2,5,7],[2,6,8]])#4%3,5-^2⋎(7-^2)]
t4=p$ä[ḋ[i4 1%4,i4 0%2.5,ä[mQ⇅6%h,mQ⇅4%h]#4#2%1,3-^2]%8⋎(i4a&(-9)%8)]
mlaa=mla[1,3,5]
m5=ä$map(-^8)[1..4]
i5=ḋ[m5⇅6%h,m5%h,m5&4%h,m5⇅9%h]
i5d=hom(-^4)[[2],[4,5],[0],[4,5]]%1
i5a=ḋ[sil%h,mlaa,i5d,mlaa,mla[-2,0,4],mlaa,i5d,sq 4[1,-1,-3,-2,-6,1]%2]&(-7)
t5=ḋ[ḋ[i5%2,i5%1.5,ä[8-^4,9-^4]#1%h,i5%2,ḋ[es[5,4,3,2,3,5,1,3,2,4,0,2]%2]%1.5,1-^2]%8⋎(i5a%8)
 ,p(ä[ä[i4 1%4,es[3,2,3,1,4,3,4,3,4,3,4,3]#2#1&7%1.5,m5⇅13%h,mQ⇅8%h,m5&7%(3/8),6-^8,mQ⇅7#5%h,6-^2]%8
 ⋎(ä[i4a%3.5,F[(1,-1),(7,0),(6,1)][hom(-^4)[[-2],[3,5],[2,5],[1,5]]%1]%1,mla[-3,1,4],mla[-3,2,4],hom(-^4)[[-2],[1,3],[-2],[2,4],[1,3]]%1.5]&(-9)%8)])%8]⊿0.8
am d=3-^d∡2∡5
amf=1-^υ∡2⋎(5-^1∡3)
vh v(c,d)=lp(ä[v-^12]:map(\t->ä[t⊿0%0.04,t%d])c,d)
aam=vh 11.am
aar=ä[1-^10,4-^10,6-^1]&4
eam=vh 10.em
dm=6-^1∡2⋎(11-^1)
em d=5-^d∡2⋎(9-^1)
cdM=ḋ[4-^8,3-^8,2.^8,3.^8,cdM%1]
cdT=ḋ[ä[3-^(8/3)∡7,10-^6,am 1,am 1,cdM&7%1,dm,aam 4.05%1,em(4/3),12-^4,am 1,am 1,cdM&7%1,dm,aam 1%1,eam 4%1]%12.5⋎(ä(sil%(11/24) : map((%1).(m3a&))[4,4,4,0,4,1,4,4,4,0,4,1])&(-18)%13.1)
 ,p(ä[ä[ä[8-^2]⊿2%h,aar%(3/8),10-^8,aar%1,aar%1,cdM&7%1,11-^1,vh 11(10-^4)%1,9-^(4/3)]%7⋎(ä(map m3ra[0,0,0,0,1,0,2])&(-7)%7)])%6.75
 ,ä[p(ä[12-^4])%(1/4),am 1,am 1,cdM&7%1,dm,aam 1%1,eam 4%1,amf,ä[3-^4,1-^υ,5-^4,1-^υ,3-^4,1-^4,3-^4,1-^4,5-^4,1-^2]%3.75∡7,ä[amf∡(-14)]%0.56,ä[amf∡(-14)]⊿0.8%1]%12⋎(ä(sil%(1/8):map((%1).(m3a&))[4,4,4,0,4,1,4,4,4]++[m3a&4%h,m3a&4%h,5-^(8/5)])&(-18)%12)]
type D=Double
data F=N Int D|F[(Int,D)][([F],D)]
φ⇸F a fs=F a$map(\(f,d)->(map φ f,d))fs
_⇸N i d=N i d
i##c
 |i<1=(i+7)##c/2
 |i>7=(i-7)##c*2
 |1>0=1.06**(c i+case i of{1->0;3->3;4->5;5->7;6->8;7->10;_->fri i})
pl dur acc(N n v)=(\ω η->map(sin.(\x->x+τ x^2/η). \i->v*exp(-i*η/s)*τ(i*v)*(0.8-τ((i-dur)/90))*sin(i*ω))[1..dur])(n##acc/15.5).exp$fri n/9
pl dur acc(F accm fs)=pl' dur (foldr(\(q,m)f i->if q==i then m else f i)acc accm) fs
pl' dur _ _|dur<=0 = []
pl' dur _ []=map(\_->0)[1..dur]
pl' dur acc((f,dr):fs)|n<-min dr dur=trans(round n)(foldr1(\a b->sum a`par`sum b`pseq`zw(+)a b)(map(pl(n+99)acc)f))$pl'(dur-dr)acc fs
trans n a b|(f,ol)<-splitAt n a,(or,l)<-splitAt 99 b=f++zw(+)ol or++l
fri=fromIntegral
F a fs#q=F((q,1):a)fs
N i d&n=N(n+i)d
f&n=(&n)⇸f
N i d⇅n=N(n-i)d
f⇅n=(⇅n)⇸f
N i d⇆_=N i d
F a fs⇆n=F a.reverse$take n fs
N i d⊿v=N i$d*v
f⊿v=(⊿v)⇸f
p=(⊿0.3)
n.^q=([F[][([N n 1],s/2/q)]],s/q)
n-^q=([N n 1],s/q)
(l,d)⋎(r,_)=(l++r,d)
(l,d)∡j=(l++map(\h->ä[h⊿0%0.01,h&j%100])l,d)
f%t=([f],s*t)
tr n=F[]$cycle[n-^15,(n+1)-^20]
ä=F[];ḋ=F$zip[6,3,7][1,1,1]
lp=ä.repeat
sil=N 0 0
tev f(l,d)=(map f l,d)
h=1/2
υ=4/3
s=4e+4
sq d=ä.map(-^d)
es=sq 8 
arp8 n@(N i v)=F[][([n,ä[n⊿0%(1/8),n&7⊿(v/υ)%100]],s)]
arp8 f=arp8⇸f
hom q=ä.map(foldr((⋎).q)$sil%1)
dθ l=2*asin l/pi:dθ(abs.sin$l*1e+9)
rev ls=(\z->z id(foldr(\m sg->(\v->z(*v)(map(*0)[0..m*14349]++sg)sg)$abs(cos$(m*3)^2)-0.6)ls.take 9$dθ 1)ls)$(.lwp 3 0).zw.((+).)
lwp ω c(x:l)=c:lwp ω((x+c*ω)/(ω+1))l
lwp _ _ _=[]
hgp ω l=zw(-)l$lwp ω 0 l
lime e(x:l)
 |abs(e*x)>1,e'<-((e*8+abs(1/x))/9)=e':lime e' l
 |1>0=e:lime((e*49999+1)/5e4)l
lime _[]=[]
lim ls=zw(\a u->τ$a/9+max(-2)(min 2$a*u)/6)(map(*0)[0..500]++ls).lwp 9 0.lime 1$hgp 9 ls
zw=zipWith
τ=tanh

$ make
ghc -o bin/def0-hs def0.hs -O2 -fllvm -threaded
[1 of 1] Compiling Main ( def0.hs, def0.o )
Linking bin/def0-hs ...
time sh -c 'bin/def0-hs +RTS -N4 > hsoutp.pcm'
189.39user 138.41system 2:06.62elapsed 258%CPU (0avgtext+0avgdata 6440240maxresident)k
0inputs+0outputs (0major+403037minor)pagefaults 0swaps
ffmpeg -loglevel panic -y -f s16le -ar 44.1k -ac 2 -i hsoutp.pcm hsoutp.ogg


Here's a partially ungolfed and commented version: https://gist.github.com/leftaroundabout/5517198.

ceased to turn counterclockwis

Posted 2013-04-23T22:45:39.593

Reputation: 5 200

Nice try. 2970 UTF-8 bytes, 2826 code points. As it is not a competitor for <600 python version, it could be better geared it towards nicer sound/polyphony (keeping it under 5000 bytes, for example). – Vi. – 2013-04-26T15:35:18.943

1@Vi. If you consider as "length of a program" the number of bytes when UTF-8 encoded then I think you should state that in the question. Just to make clear, since some people do not use this definition(e.g. every APL programmer...) – Bakuriu – 2013-04-26T16:43:28.053

@Bakuriu Yeah, right LOL. – Soham Chowdhury – 2013-04-27T17:35:01.380

That was insane! I would love to get some idea how this program works. – shiona – 2013-05-04T09:30:31.650

@shiona: it's actually not that obfuscated, with type signatures it should be easy enough to understand for anyone familiar with Haskell and basic DSP.

– ceased to turn counterclockwis – 2013-05-04T11:22:24.060

@leftaroundabout: Thanks, I'll have to take a look at that. I have some experience with both. I also tried to run this on my system, but the program doesn't seem to output anything, am I doing something wrong / should I just wait for it to start? – shiona – 2013-05-04T11:29:48.173

Yes, it takes quite a while to get going – it first needs to render the whole first part before anything is sent to output. Make sure you compile with -O2 -threaded and run with +RTS -N<number of cores> to get as good performance as possible. – ceased to turn counterclockwis – 2013-05-04T11:48:51.530

7

Python, 331+286 = 617 (0.548 bytes per note)

My solution uses a data file and a python script. The data file should be used as input to the script. I don't have aplay, but it works when I import it as raw data in Audacity with signed 16-bit PCM, little-endian, and 2 channels.

The data file is 331 bytes. Here's a python script which outputs it:

import sys
sys.stdout.write('\x08\x1c\x9d\xb9"\xc7\xea\xf0\xb7)\xc0D!u\x0bB~\'\x91S\xb2\x0c\xe9\xf8T;\xfd\xc13\xcf\xb9\xa6r>\xbc\xc5\xb4\xbb\xf8\xa4\x9a\x05H\xa0\x1d\x0eIq\t\\+\t\xdbn\x03\xc3&\x98\xa0\x11\xc5\xaa\xef\xbcSR^\x13\xe7\xc7\x0e\xc0\xa9^\x91Z\xfc\x02\x11\xb9\x1bE\xfc/=\xb8\xaf5<\x12\xa2\xc4\x02\xec\xdcO\xc2a\x04<Q\xfd\xe9L\xbc\xab%\xf5wX1F\xa6\x88\xddP\xfec(_#\xb4\x0bN\xba&m\xe3\xa4\x08Q\xdb\xd9\xf3<Q\xc6\xf6\x0e\xd7\xacd\x1f"g\xce\xae.\xb0\x90{|\x04\xc5X\xe6x>\xefE\xc8\xb0\xd2?N\x83?\x04\x86"a\xcc\x9b\x8fq\x9c\xce\xa2\xb6f\x9ab\x92\x9e:\xc0S\xcd\th\xb1\x87\xecT\x9d\xf4\n\xaf\xc9$`E5\xcc\xc5\xa0m\xcc\n8\xf8:\x03\xf5\x02H\xf3k\xe5\x86\xa64\x90\xa2\xc2w\xfa\xb7\xc0\x1e*2\x93\xca\x12\xe3^!\xd5yQ,LXW\xb4\x96D\x8dB\x9c`\xbf\x96`s;\xb7}\xeb\x8c\xebI\xa0o\x00\x08\xfe\xf1\xd2M3}\x8e\xd0\xda\x97\'\xca\x83-\x14\xda\xa1ET\n\xe8\xc7@\x1c\xa2a\xbb\xa7\x1b\x014\xdcz\xc7\xa6\xc4\x1d\x18\x04\r\xb1\x9e\xe3\xd0\x18<\x98`N?a\xe4\x8e\x9d\xd5\r\xe7Z[\xf4\xed\xf1PQ')

Here's the python script:

import sys
k=0
m=185
p=[]
q=[]
for c in sys.stdin.read():k=k*256+ord(c)
while k:
    v=k%m;k/=m
    if v<184:q+=[v]
    elif v-m+1:q+=p[v-184]
    else:m+=1;p+=[q];q=[]
for n in q:r=[1,2,3,4,6,8,12,15,16][n%9]*2000;sys.stdout.write(''.join(chr(int(i*1.06**(n/9)/4.3)%99)for i in range(r*3))+'\0'*r)

Note: If you're running Windows, use the -u switch for both scripts since stdin and stdout are dealing with binary data.

cardboard_box

Posted 2013-04-23T22:45:39.593

Reputation: 5 150

Good job. Considering 331 + 286 + 10(for tying together the file and the script) == 627. – Vi. – 2013-04-26T03:07:35.277

You can shorten a bit using os.read/write instead of sys.stdin/stdout. – Bakuriu – 2013-04-26T16:39:45.180

+50 for the beautiful compression scheme. Using a grammar-based approach without entropy encoding I can't get that short just on the note values, without taking into account the lengths. – Peter Taylor – 2013-05-01T22:32:59.930

Can you describe how you compressed the data? I'm interested in knowing how you got it so small. – Sir_Lagsalot – 2013-05-03T16:37:02.773

1@Sir_Lagsalot: it's basically a nested / recursive dictionary, i.e. you have motifs consisting of notes (pitch and length encoded in one number), then you have themes that contain these motifs and/or single notes, then parts consisting of themes etc.. My program uses essentially the same principle (expanded by transpositions, inversions etc.), just not further compressed into a tight binary file; I've simply made everything top-level variable definitions instead. – ceased to turn counterclockwis – 2013-05-03T18:52:22.140

@Sir_Lagsalot, my take on it is that it's a Lempel-Ziv style grammar code but that unlike LZ it uses explicit tokens to indicate the boundaries of the parts to repeat; and then it uses arithmetic encoding on top of that. – Peter Taylor – 2013-05-04T10:51:39.660

4

GolfScript (129 + 369 = 498 bytes)

Both program and data file include unprintable characters, so I'm going to give Base64 and xxd representations.

Program (129 bytes):

MjU2YmFzZSA2OWJhc2VbMDpOXS8oNDMse1xbMSQpXS9cMiQ9Kn0vXCwpey19KyUuLDIvL3ppcHt+
TisyNSU6Tid7goqSm6SuuMPP2+j2/0FFSU1SV1xcYWdtdCc9OmY7MTc2MCosey41MD4qZioxNy8u
Li59JScnOm4rcHV0c30v

0000000: 3235 3662 6173 6520 3639 6261 7365 5b30  256base 69base[0
0000010: 3a4e 5d2f 2834 332c 7b5c 5b31 2429 5d2f  :N]/(43,{\[1$)]/
0000020: 5c32 243d 2a7d 2f5c 2c29 7b2d 7d2b 252e  \2$=*}/\,){-}+%.
0000030: 2c32 2f2f 7a69 707b 7e4e 2b32 3525 3a4e  ,2//zip{~N+25%:N
0000040: 277b 828a 929b a4ae b8c3 cfdb e8f6 ff41  '{.............A
0000050: 4549 4d52 575c 5c61 676d 7427 3d3a 663b  EIMRW\\agmt'=:f;
0000060: 3137 3630 2a2c 7b2e 3530 3e2a 662a 3137  1760*,{.50>*f*17
0000070: 2f2e 2e2e 7d25 2727 3a6e 2b70 7574 737d  /...}%'':n+puts}
0000080: 2f                                       /

Data (369 bytes):

LoDJFvCRQqNdL7+JDvjtSkX4HBS2FwgvjfdxAHrF1/DcMIBtG/g7QZBLLYHpzgaWaM1TaHwbtxG+
l1lqsL3A8nuprtpPI20YbHm3lf7NxmYNdEIMTlhwTG+TlSn802DzN3YgIwbcKbtty9gWmF2nVS55
iJHQZd4HCcokoLRwH1g2XqP8Yo5xj5/YQm9DH85obUv47mii5n+PwsoJZ6yaz4eSpGps6dQMl+Pa
YP/WC6cVDBBGs3vq5cGe51H2u7oVArFuHrsI2sHkGNYHlhWudKn5RRvJhe3sxfrtQE/MekKRuZBt
f4B9qdyss66vFipSi1zf2MXF9A/CzwvMQ/t9PEtxw8kzxxikp2Ek3kc9TiamLl+iG2vjdWp84JzY
Mg6cE+3bFI4kVdn+d1NEnBR/S9HMnksgEc9sdAcyWsbSaGjwetwGTr7UXkpKO9aHF01D2i5pCO40
/keR0+a+NsBEOXZfatpXav44AJjalywtLeWu

0000000: 2e80 c916 f091 42a3 5d2f bf89 0ef8 ed4a  ......B.]/.....J
0000010: 45f8 1c14 b617 082f 8df7 7100 7ac5 d7f0  E....../..q.z...
0000020: dc30 806d 1bf8 3b41 904b 2d81 e9ce 0696  .0.m..;A.K-.....
0000030: 68cd 5368 7c1b b711 be97 596a b0bd c0f2  h.Sh|.....Yj....
0000040: 7ba9 aeda 4f23 6d18 6c79 b795 fecd c666  {...O#m.ly.....f
0000050: 0d74 420c 4e58 704c 6f93 9529 fcd3 60f3  .tB.NXpLo..)..`.
0000060: 3776 2023 06dc 29bb 6dcb d816 985d a755  7v #..).m....].U
0000070: 2e79 8891 d065 de07 09ca 24a0 b470 1f58  .y...e....$..p.X
0000080: 365e a3fc 628e 718f 9fd8 426f 431f ce68  6^..b.q...BoC..h
0000090: 6d4b f8ee 68a2 e67f 8fc2 ca09 67ac 9acf  mK..h.......g...
00000a0: 8792 a46a 6ce9 d40c 97e3 da60 ffd6 0ba7  ...jl......`....
00000b0: 150c 1046 b37b eae5 c19e e751 f6bb ba15  ...F.{.....Q....
00000c0: 02b1 6e1e bb08 dac1 e418 d607 9615 ae74  ..n............t
00000d0: a9f9 451b c985 edec c5fa ed40 4fcc 7a42  ..E........@O.zB
00000e0: 91b9 906d 7f80 7da9 dcac b3ae af16 2a52  ...m..}.......*R
00000f0: 8b5c dfd8 c5c5 f40f c2cf 0bcc 43fb 7d3c  .\..........C.}<
0000100: 4b71 c3c9 33c7 18a4 a761 24de 473d 4e26  Kq..3....a$.G=N&
0000110: a62e 5fa2 1b6b e375 6a7c e09c d832 0e9c  .._..k.uj|...2..
0000120: 13ed db14 8e24 55d9 fe77 5344 9c14 7f4b  .....$U..wSD...K
0000130: d1cc 9e4b 2011 cf6c 7407 325a c6d2 6868  ...K ..lt.2Z..hh
0000140: f07a dc06 4ebe d45e 4a4a 3bd6 8717 4d43  .z..N..^JJ;...MC
0000150: da2e 6908 ee34 fe47 91d3 e6be 36c0 4439  ..i..4.G....6.D9
0000160: 765f 6ada 576a fe38 0098 da97 2c2d 2de5  v_j.Wj.8....,--.
0000170: ae                                       .

Explanation

I've mangled the (updated) supplied score (more on that later) into a single string containing bytes with values from 0 to 24. The note lengths come first; then the note values, represented mod 25 and difference-encoded. The reason for the difference encoding is so that passages which are repeated in transposition will be reduced to the same sequence and can be compressed.

I then ran this through a string-to-GolfScript compression program which I've mentioned before (and which I improved in order to be competitive in this golf) to get the data file, which is decompressed by the first part of the program:

256base 69base[0:N]/(43,{\[1$)]/\2$=*}/\,){-}+%

It's a simple grammar expansion of a type which is familiar to anyone who's looked at many questions tagged .

I then split this string into pairs [length note] and iterate through the pairs. The non-printable characters come from a magic string which contains frequency parameters for the notes: I'm using GolfScript's implicit truncation mod 256 of integer arrays which are converted to strings to produce a triangle wave*, so the base frequency is 22050/256 Hz. I wrote a program to find integer ratios which give a good tuning; the magic string contains numerators, and the denominator 17 is the same for all notes. The average tuning error is about 3.4 cents.

The note lengths are represented as is, and are much more plausible than the previous version of the score. As I suspected, the rounding has increased the redundancy in the string and shortened the compressed data file by 30 bytes, not to mention saving the lookup array. However, there are still some passages which I find suspicious:

72 13

or

71 9
69 2
71 2

give bars which are a sixth of a crochet longer than the rest of the bars in the score, and

85 9
85 4
85 24
85 23

or

83 18
88 7
85 24
85 23

are an integral number of bars, but with some dubious offsets.

The program could be slightly shorter. I have deliberately chosen to trade in shortness for execution time. With some speed improvements to the GolfScript interpreter which I've submitted to Darren Smith and which I believe he plans to publish at some point, the current version runs in less than 15 minutes on my computer. If I don't puts each note after generating it then it runs much slower.

* I hereby confess that my comment about everyone using square waves was wrong.

Peter Taylor

Posted 2013-04-23T22:45:39.593

Reputation: 41 901

How to properly run GolfScript? I try base64 -d <<< 'MjU2Y.....9Lw==' | golfscript and it says golfscript:405:in 'scan': invalid byte sequence in UTF-8 (ArgumentError) (the same if I save the program to file, of course)

– Vi. – 2013-05-03T22:48:34.130

Note lengths are extracted from MIDI file using crude algorithm (see the comment to the play.pl). I'll fix the notes lengths to be sane. – Vi. – 2013-05-03T22:54:40.997

Updated the gist. Now minimal length of the note is 1. – Vi. – 2013-05-03T23:15:48.937

If I try to run using online interpreter (the inserted code looks like 㔲戶獡⁥㌷慢敳せ为⽝㐨ⰴ屻ㅛ⤤⽝㉜㴤紪尯⤬⵻⭽⸥㈬⼯楺筰乾㈫┵为笧誂鮒꺤쎸��䇿䥅前屗慜浧❴㨽㭦∩ĦĂ༃ጔ؏༆ณؕḧ⸘研��⒖✏㰢⭻⩽㐴Ⱚ⹻〵⨾⩦㜱ⸯ⸮╽✧渺瀫瑵絳/), I get undefined methodclass_id' for nil:NilClass`

– Vi. – 2013-05-03T23:29:32.913

@Vi., the program has to be extracted to a file (e.g. rondo.gs), and the data to another file (e.g. x), and then it's run as golfscript rondo.gs<x – Peter Taylor – 2013-05-04T08:30:05.737

1And there's no way it will run in the online interpreter - that will time out. – Peter Taylor – 2013-05-04T08:30:24.033

md5sum golf.gs -> 9106c2fce9e03371b9b8cfbe14e1be2a golf.gs; golfscript golf.gs < golf.dat -> /home/vi/bin/golfscript:405:in 'scan': invalid byte sequence in UTF-8 (ArgumentError) from /home/vi/bin/golfscript:405:in 'compile' from /home/vi/bin/golfscript:498:in '<main>'; md5sum /home/vi/bin/golfscript -> 31bc95e867d0771443208ca29fc824ca /home/vi/bin/golfscript – Vi. – 2013-05-04T18:57:25.260

@Vi., those md5sums are wrong: should be 2e62bb812b1f0428839a3e7ea18f384f codegolf11463.data // d5b641687fbf4218d42ed4c7c14abc20 codegolf11463.gs – Peter Taylor – 2013-05-04T19:28:14.113

Re-saved files, now md5sums are as you specified. Still the same error. Can you give md5sum and/or link to your GolfScript interpreter itself? Can Ruby version (1.9.3p194 i486-linux) matter? – Vi. – 2013-05-04T19:41:24.820

@Vi., I saw the md5sums were wrong and didn't look closer. UTF-8?! It seems that Ruby 9 has added support for encodings other than ISO-8859-1, and that breaks a lot of assumptions that were previously safe. I'm using Ruby 1.8.7. – Peter Taylor – 2013-05-04T20:06:02.383

Fixed shebang from #!/usr/bin/ruby to #!/usr/bin/ruby1.8, now generating the music... MD5sums were probably for previous version of the script and datafile. – Vi. – 2013-05-04T22:22:22.350

"However, there are still some passages which I find suspicious" -> patches to the gist are welcome. (still generating the output BTW) – Vi. – 2013-05-04T22:50:57.473

2

x86 Machine Code - 513 Bytes

This doesn't fully meet the challenge, since instead of outputting in a format suitable for feeding into aplay, it plays the midi.

Executable .COM file and asm source code - It may take up to 14 seconds for the music to start. It will also play a little slow, since the timer resolution is 1/18th of a second.

The music is encoded in 375 bytes using Fibonacci coding and a dictionary composed of the previously decoded music.

Pseudocode Decoding Algorithm:

store_pos=0;
if ( !readBit() ){
    note = FibonacciDecode() + 63;
    time = FibonacciDecode();
    store(note, time);
    store_pos++;
} else {
    pos = FibonacciDecode();
    run = FibonacciDecode();
    copy(store_pos-pos,store_pos,run);
    store_pos+=run;
}

Once the music is decoded, it is a simple matter of outputting it to the Midi port.

Sir_Lagsalot

Posted 2013-04-23T22:45:39.593

Reputation: 4 898

1It relies on existing synthesizer (MIDI inside audio card in this case) instead of providing it's own. – Vi. – 2013-05-05T17:26:20.950

You can hack up synthesizer and make it output samples to to the respective port or to stdout/file (using DOS or Linux syscalls). As a separate challenge you can make a version with fully fledged polyphonic MIDI (still with compression into single COM file). – Vi. – 2013-05-05T17:54:18.543

I'm just interested in the 'compact Mozart music' aspect of the challenge, not the synthesizer. I'm posting this because it's fun and should be interesting to others rather than to win the challenge. – Sir_Lagsalot – 2013-05-05T18:03:02.033

OK. Waiting for Arduino version... – Vi. – 2013-05-05T21:13:50.257