Last active
December 16, 2015 23:59
-
-
Save leftaroundabout/5517198 to your computer and use it in GitHub Desktop.
Tiny piano – Rondo Alla Turca
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. render 9e6 (\_->0) . Sequence[] | |
$[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=Sequence[][2-^8,1-^8,0-^8,1-^8,3-^4] | |
cM=Sequence[][7-^20,8-^20,9.^4,Sequence[(7,0),(6,1)](map((∡2).(.^4))[6,5,6])%0.75] | |
cMv=Sequence[][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 | |
⋎(Sequence[][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=Sequence[][(1-3*f).^4,lp(5.^4∡(-2-f))%0.75,mMa f%1.5,mMa(f*2)%h,mMa f%1]#7 | |
mTm=Sequence[][mM%1,mM&2%1,mM#4&4%h,mM&7%h,mM&7%1,8.^4,Sequence[][10.^4]%0.2,cM%1,cM%1,cM%0.85,Sequence[][4.^4∡2,5.^2]#6#4%2]#7 | |
mT=p$Sequence[][mTm%8.1⋎(Sequence[][sil%h,mMa 0%4,mMa 1%2.75,2.^4,(-2)-^2]&(-7)%8)] | |
m2=Sequence[][Sequence[](map((∡2).(.^4))[1,2,3,3]++[es[6,5,4,3]%h]++[0-^2∡2])%2 | |
⋎(Sequence[][sil%h,1.^4,8.^4,3.^4,10.^4,5-^2]⊿1.3&(-14)%2)] | |
t2=p$Sequence[][m2&2%1.8,0-^5,m2&2%2,m2#7%1.8,(-2)-^5,m2#7%2,mT%3.5,cMv] | |
m3=Sequence[]$[3-^4,4-^4,5-^2]++map(-^4)[3,4,5,4,3,2,1,2,3,4,2,0] | |
m3a=Sequence[][(Sequence[][sil%(1/8),lp(8.^4)%1]:zw(\d n->Sequence[][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=Sequence[][Sequence[][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,Sequence[(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]=Sequence[][b-^4,lp(c-^4⋎(d-^4))%1]%1 | |
i4a=ḋ[sil%h,Sequence[](map mla[[1,3,5],[2,4,5],[1,3,5],[0,2,5]])#5%4,Sequence[](map mla[[1,3,5],[2,5,7],[2,6,8]])#4%3,5-^2⋎(7-^2)] | |
t4=p$Sequence[][ḋ[i4 1%4,i4 0%2.5,Sequence[][mQ⇅6%h,mQ⇅4%h]#4#2%1,3-^2]%8⋎(i4a&(-9)%8)] | |
mlaa=mla[1,3,5] | |
m5=Sequence[]$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,Sequence[][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(Sequence[][Sequence[][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 | |
⋎(Sequence[][i4a%3.5,Sequence[(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(Sequence[][v-^12]:map(\t->Sequence[][t⊿0%0.04,t%d])c,d) | |
aam=vh 11.am | |
aar=Sequence[][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=ḋ[Sequence[][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⋎(Sequence[](sil%(11/24) : map((%1).(m3a&))[4,4,4,0,4,1,4,4,4,0,4,1])&(-18)%13.1) | |
,p(Sequence[][Sequence[][Sequence[][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⋎(Sequence[](map m3ra[0,0,0,0,1,0,2])&(-7)%7)])%6.75 | |
,Sequence[][p(Sequence[][12-^4])%(1/4),am 1,am 1,cdM&7%1,dm,aam 1%1,eam 4%1,amf,Sequence[][3-^4,1-^υ,5-^4,1-^υ,3-^4,1-^4,3-^4,1-^4,5-^4,1-^2]%3.75∡7,Sequence[][amf∡(-14)]%0.56,Sequence[][amf∡(-14)]⊿0.8%1]%12⋎(Sequence[](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 Volume = Double | |
type SemitoneShift = Double | |
type Duration = Double | |
type Frequency = Double | |
type DiatonicPitch = Int | |
type Accidental = (DiatonicPitch, SemitoneShift) | |
type Accidental'd = DiatonicPitch -> SemitoneShift | |
type TimeBlock = ([Figure], Duration) | |
data Figure | |
= Note { notePitch :: DiatonicPitch | |
, noteVelo :: Volume } | |
| Sequence { accidentals :: [Accidental] | |
, theSequence :: [TimeBlock] } | |
-- | Apply a function to all the sub-sequences, rather than the top sequence as a whole. | |
(⇸) :: (Figure->Figure) -> Figure->Figure | |
φ⇸Sequence a fs=Sequence a$map(\(f,d)->(map φ f,d))fs | |
_⇸Note i d=Note i d | |
-- | Look up the frequency of the diatonic pitch, which may be subject to semitone shifting via accidentals. | |
(##) :: DiatonicPitch -> Accidental'd -> Frequency | |
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;4->5;5->7;6->8;7->10;_->fromIntegral i}) | |
type Audio = [Double] | |
render :: Duration -> Accidental'd -> Figure -> Audio | |
render dur acc (Note n v) = map (harmExcite . oscillate) [1..dur] | |
where harmExcite x = sin $ x + τ x ^ 2 / η | |
oscillate i = v * envelope * sin(i*ω) | |
where envelope = attack * decay * release | |
decay = exp $ -i*η/s | |
attack = τ(i*v) | |
release = 0.8-τ((i-dur)/90) | |
ω = n##acc / 15.5 | |
η = exp $ fromIntegral n/9 | |
render dur acc(Sequence accm fs)=render' dur (foldr(\(q,m)f i->if q==i then m else f i)acc accm) fs | |
render' :: Duration -> Accidental'd -> [TimeBlock] -> Audio | |
render' dur _ _|dur<=0 = [] | |
render' dur _ []=map(\_->0)[1..dur] | |
render' dur acc((f,dr):fs)|n<-min dr dur=transition(round n)(foldr1(\a b->sum a`par`sum b`pseq`zw(+)a b)(map(render(n+99)acc)f))$render'(dur-dr)acc fs | |
transition :: Int -> Audio -> Audio -> Audio | |
transition n a b|(f,ol)<-splitAt n a,(or,l)<-splitAt 99 b=f++zw(+)ol or++l | |
-- | Apply a sharp-accidental to all notes of the specified pitch in the sequence. | |
(#) :: Figure -> DiatonicPitch -> Figure | |
Sequence a fs#q=Sequence((q,1):a)fs | |
(&), (⇅), (⇆) :: Figure -> Int -> Figure | |
-- | Transpose the sequence by /n/ (diatonic) steps. | |
Note i d&n=Note(n+i)d | |
f&n=(&n)⇸f | |
-- | Invert the sequence. | |
Note i d⇅n=Note(n-i)d | |
f⇅n=(⇅n)⇸f | |
-- | Retrograde. | |
Note i d⇆_=Note i d | |
Sequence a fs⇆n=Sequence a.reverse$take n fs | |
-- | Adjust the dynamics/volume. | |
(⊿) :: Figure -> Volume -> Figure | |
Note i d⊿v=Note i$d*v | |
f⊿v=(⊿v)⇸f | |
-- | Play in piano (dynamics). | |
p=(⊿0.3) | |
type InverseDuration = Double | |
(.^), (-^) :: DiatonicPitch -> InverseDuration -> TimeBlock | |
-- | A simple staccato note of the specified note value. | |
n.^q=([Sequence[][([Note n 1],s/2/q)]],s/q) | |
-- | Legato note. | |
n-^q=([Note n 1],s/q) | |
-- | Put two time-blocks in parallel / play them simultaneously. | |
(⋎) :: TimeBlock -> TimeBlock -> TimeBlock | |
(l,d)⋎(r,_)=(l++r,d) | |
-- | Add the nth note above as a harmony to all notes in the block. | |
(∡) :: TimeBlock -> Int -> TimeBlock | |
(l,d)∡j=(l++map(\h->Sequence[][h⊿0%0.01,h&j%100])l,d) | |
-- | Cut a sequence to the specified duration. | |
(%) :: Figure -> Duration -> TimeBlock | |
f%t=([f],s*t) | |
tr n=Sequence[]$cycle[n-^15,(n+1)-^20] | |
ḋ=Sequence$zip[6,3,7][1,1,1] | |
lp=Sequence[].repeat | |
sil=Note 0 0 | |
tev f(l,d)=(map f l,d) | |
h=1/2 | |
υ=4/3 | |
s=4e+4 | |
sq d=Sequence[].map(-^d) | |
es=sq 8 | |
arp8 n@(Note i v)=Sequence[][([n,Sequence[][n⊿0%(1/8),n&7⊿(v/υ)%100]],s)] | |
arp8 f=arp8⇸f | |
hom q=Sequence[].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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This program generates Mozart's Ronda Alla Turca (Piano Sonata #11) as ahem CD-Quality pcm audio on standard output, so it can be piped in real-time (with enormous latency) to
aplay -f cd
. This is a solution to this challenge on codegolf.stackexchange.