29
\$\begingroup\$

Output the following excerpt from Pachelbel's Canon in D as audio:

Pachelbel's Canon in D

Rules

  • Any format/encoding may be used, so long as a codec existed prior to the creation of this challenge
  • Any instrument (a real instrument, MIDI synthesis, etc.) may be used
  • The tempo must be 65 BPM (as notated in the sheet music) - if you cannot get exactly 65 BPM, you may use any tempo within the (inclusive) range 64.75 - 65.25
  • The sheet music is in concert pitch
  • Equal temperament tuning must be used (specifically 12-ET with A4 = 440 Hz)

For reference, here is a Stack Snippet that will play the excerpt:

<audio controls><source src="https://a.clyp.it/h3y3isar.mp3"></audio>

\$\endgroup\$
  • \$\begingroup\$ Would that snippet be competitive as it downloads it from elsewhere \$\endgroup\$ – Blue Dec 25 '16 at 17:09
  • 4
    \$\begingroup\$ @muddyfish No, because fetching the output from an external source is a standard loophole. \$\endgroup\$ – Mego Dec 25 '16 at 17:10
  • \$\begingroup\$ Would outputting a midi file instead of audio be acceptable? \$\endgroup\$ – DJMcMayhem Dec 25 '16 at 17:31
  • \$\begingroup\$ @DJMcMayhem Yes, MIDI files are acceptable. \$\endgroup\$ – Mego Dec 25 '16 at 17:32
  • \$\begingroup\$ I assume the final (only) chord is required in full? That effectively prohibits non-polyphonic languages / API's \$\endgroup\$ – Level River St Dec 25 '16 at 19:39
9
\$\begingroup\$

JavaScript (ES7), 249 242 241 bytes

with(new AudioContext)for(t=i=0;n=parseInt('l43l431db98643o86ogfdbdfdgfdzbdzgigikigfdbzbdv98db9864311480'[i++],36);)with(createOscillator())frequency.value=880*2**(-~-n%20/12),connect(destination),start(t),stop(i>56?t+q*8:t+=n>20?q=6/13:q/2)

Thanks to @Neil and @PatrickRoberts for some byte savings!

Explanation

The notation is packed into the string where each character is a single note as a base-36 digit. The note values are determined by the formula (19 - pitch) * time + 1 where pitch is the number of semitones less than A5 and time is 1 for a semiquaver or 20 for a quaver. The 0 at the end stops the for loop.

The tempo is 65.22bpm Edit: exactly 65bpm now, for 2 more bytes.

This explanation/demo uses Math.pow instead of ** for browser compatibility. It also sets the gain of the oscillators to .3 so that the final chord does not make your ears bleed (default gain is 1).

with(new AudioContext)            // use HTML5 audio
  for(                            // iterate through the note pitches and lengths
    t=i=0;                        // t = current time to place the note
    n=parseInt(                   // n = note pitch/length

      // Packed notation string
      'l43l431db98643o86ogfdbdfdgfdzbdzgigikigfdbzbdv98db9864311480'

    [i++],36);
  )
    with(createOscillator())      // create the note oscillator

      // Set the note frequency (using Math.pow for the demo).
      //frequency.value=880*2**(-~-n%20/12),
      frequency.value=880*Math.pow(2,-~-n%20/12),

      // Send the note's sound through the speakers (for the demo, we'll connect it to
      // a gain node so we can reduce the volume).
      //connect(destination),
      connect((g=createGain(),g.gain.value=.3,g.connect(destination),g)),

      start(t),                     // schedule the note to sound
      stop(                         // schedule the end of the note
        i>56?                       // if we are in the final chord
          t+                        //   do not increment the time
            q*8                     //   hard-code the length to a semibreve
        :t+=n>20?q=6/13:q/2         // else update the length based on the note value
      )

You can press the button above to test it in any browser that supports the HTML5 Web Audio API.

\$\endgroup\$
  • \$\begingroup\$ 880*2**(-~-n%20/12) should save you a few bytes. \$\endgroup\$ – Neil Dec 26 '16 at 12:42
  • \$\begingroup\$ @Neil Thanks. I suspect I can get rid of the -~- too by altering the format of my note packing (The + 1 in the formula is only there because I need 0 to stop the for loop and I was too lazy to think about it a lot before I posted). \$\endgroup\$ – user81655 Dec 26 '16 at 12:46
  • \$\begingroup\$ Saw this after I posted my answer, lol \$\endgroup\$ – Patrick Roberts Feb 5 '17 at 11:27
  • \$\begingroup\$ You can save another byte by replacing c=new AudioContext; with with(new AudioContext) and removing the two occurrences of c. in the program. \$\endgroup\$ – Patrick Roberts Feb 5 '17 at 12:18
  • 1
    \$\begingroup\$ @kamoroso94 No. The exponent can be a fraction, so if I used << it would cast it to an integer. \$\endgroup\$ – user81655 Feb 9 '18 at 0:24
8
\$\begingroup\$

Mathematica, 212 152 139 135 bytes

{#~(s=##~SoundNote~41&)~1&/@LetterNumber@"uursuursuikmnprsrrnprrfgikigifgiggkiggfdfdbdfgikggkikkmnikmnprsu",{14,18,21}~s~16}~Sound~18.5

Outputs a Sound object that plays Pachelbel's Canon in D when the Play button is pressed. The instrument is MIDI instrument #41 "Violin".

The Audio

Click me!

Explanation

LetterNumber@"uursuursuikmnprsrrnprrfgikigifgiggkiggfdfdbdfgikggkikkmnikmnprsu"

Find the letter numbers of each character in the string ("a" -> 1, "b" -> 2, and so on), wrapped with a List. (This string represents Pachelbel's Canon in D)

#~(s=##~SoundNote~41&)~1&/@...

Set s to SoundNote function whose instrument is #41. Set the duration to 1 and map that function to each element in the List (thus making SoundNote primitive objects).

{14,18,21}~s~16

Make the last triad. (The duration 16 is there to make the last note 16 times longer--a whole note is sixteen times a sixteenth note.)

... ~Sound~18.5

Make a Sound object, 18.5 seconds long (because the tempo is 65 bpm [5 measures of 4/4 with tempo 65 bpm = approximately 18.5 seconds]).

126 byte version, non-competing

Sound[{(s=SoundNote)/@LetterNumber@"uursuursuikmnprsrrnprrfgikigifgiggkiggfdfdbdfgikggkikkmnikmnprsu",{14,18,21}~s~16},240/13]

Non-competing because the output contains two sixteenth notes instead of an eighth note, and the separation is quite noticeable.

\$\endgroup\$
  • 14
    \$\begingroup\$ Really, Mathematica doesn't have a built-in for Pachelbel's Canon? \$\endgroup\$ – Stewie Griffin Dec 25 '16 at 23:06
  • 1
    \$\begingroup\$ @StewieGriffin Only the whole thing probably, and the cost of clipping it would be too high. \$\endgroup\$ – Mego Dec 26 '16 at 0:09
  • \$\begingroup\$ @StewieGriffin Surprisingly, it doesn't. \$\endgroup\$ – JungHwan Min Dec 26 '16 at 1:53
6
\$\begingroup\$

Bubblegum, 203 bytes

00000000: e002 2800 c35d 0026 9509 6f34 76f2 ffad  ..(..].&..o4v...
00000010: 4150 0893 a735 bd02 a1eb 1237 18fe 5498  AP...5.....7..T.
00000020: 120a 83e1 6662 8a5e 9709 fe8a 3430 0f48  ....fb.^....40.H
00000030: 5008 54af d19a b44f 2be9 fb3b bf9d 206d  P.T....O+..;.. m
00000040: abbf 12f0 2151 6dae 4712 8c18 4d8e f5cd  ....!Qm.G...M...
00000050: eb85 404c 17cd bd5c 2775 38bd eb50 ab88  ..@L...\'u8..P..
00000060: e015 fb7e 4b1e 5ddb 515b 144c fc5e c1be  ...~K.].Q[.L.^..
00000070: 3d5d 20cd e950 4a1d 256e b56e d364 188b  =] ..PJ.%n.n.d..
00000080: 6fa1 afcc 2100 0235 ada0 2f23 411d 95dd  o...!..5../#A...
00000090: 6665 3b45 041d cbe2 8e3b 2456 fb8d 4e4c  fe;E.....;$V..NL
000000a0: 1a7f b814 a6cf 850e 9b6c 9285 3a6f 1ec3  .........l..:o..
000000b0: 02ed 505c 996b eb4d 209c 2776 a8aa 8380  ..P\.k.M .'v....
000000c0: 42cc b779 218e e75e 8000 00              B..y!..^...

Try it online!

This is a hexdump (reverse with xxd -r) of the source code. The MIDI file it produces is as follows (also a hexdump):

00000000: 4d54 6864 0000 0006 0001 0002 01e0 4d54  MThd..........MT
00000010: 726b 0000 0019 00ff 5902 0200 00ff 5804  rk......Y.....X.
00000020: 0402 1808 00ff 5103 0e15 c500 ff2f 004d  ......Q....../.M
00000030: 5472 6b00 0001 f200 c000 00ff 0405 5069  Trk...........Pi
00000040: 616e 6f00 9051 5f81 5880 5100 1890 4e5f  ano..Q_.X.Q...N_
00000050: 6c80 4e00 0c90 4f5f 6c80 4f00 0c90 515f  l.N...O_l.O...Q_
00000060: 8158 8051 0018 904e 5f6c 804e 000c 904f  .X.Q...N_l.N...O
00000070: 5f6c 804f 000c 9051 5f6c 8051 000c 9045  _l.O...Q_l.Q...E
00000080: 5f6c 8045 000c 9047 5f6c 8047 000c 9049  _l.E...G_l.G...I
00000090: 5f6c 8049 000c 904a 5f6c 804a 000c 904c  _l.I...J_l.J...L
000000a0: 5f6c 804c 000c 904e 5f6c 804e 000c 904f  _l.L...N_l.N...O
000000b0: 5f6c 804f 000c 904e 5f81 5880 4e00 1890  _l.O...N_.X.N...
000000c0: 4a5f 6c80 4a00 0c90 4c5f 6c80 4c00 0c90  J_l.J...L_l.L...
000000d0: 4e5f 8158 804e 0018 9042 5f6c 8042 000c  N_.X.N...B_l.B..
000000e0: 9043 5f6c 8043 000c 9045 5f6c 8045 000c  .C_l.C...E_l.E..
000000f0: 9047 5f6c 8047 000c 9045 5f6c 8045 000c  .G_l.G...E_l.E..
00000100: 9043 5f6c 8043 000c 9045 5f6c 8045 000c  .C_l.C...E_l.E..
00000110: 9042 5f6c 8042 000c 9043 5f6c 8043 000c  .B_l.B...C_l.C..
00000120: 9045 5f6c 8045 000c 9043 5f81 5880 4300  .E_l.E...C_.X.C.
00000130: 1890 475f 6c80 4700 0c90 455f 6c80 4500  ..G_l.G...E_l.E.
00000140: 0c90 435f 8158 8043 0018 9042 5f6c 8042  ..C_.X.C...B_l.B
00000150: 000c 9040 5f6c 8040 000c 9042 5f6c 8042  ...@_l.@...B_l.B
00000160: 000c 9040 5f6c 8040 000c 903e 5f6c 803e  ...@_l.@...>_l.>
00000170: 000c 9040 5f6c 8040 000c 9042 5f6c 8042  ...@_l.@...B_l.B
00000180: 000c 9043 5f6c 8043 000c 9045 5f6c 8045  ...C_l.C...E_l.E
00000190: 000c 9047 5f6c 8047 000c 9043 5f81 5880  ...G_l.G...C_.X.
000001a0: 4300 1890 475f 6c80 4700 0c90 455f 6c80  C...G_l.G...E_l.
000001b0: 4500 0c90 475f 8158 8047 0018 9049 5f6c  E...G_.X.G...I_l
000001c0: 8049 000c 904a 5f6c 804a 000c 9045 5f6c  .I...J_l.J...E_l
000001d0: 8045 000c 9047 5f6c 8047 000c 9049 5f6c  .E...G_l.G...I_l
000001e0: 8049 000c 904a 5f6c 804a 000c 904c 5f6c  .I...J_l.J...L_l
000001f0: 804c 000c 904e 5f6c 804e 000c 904f 5f6c  .L...N_l.N...O_l
00000200: 804f 000c 9051 5f6c 8051 000c 904a 5f00  .O...Q_l.Q...J_.
00000210: 904e 5f00 9051 5f8e 4c80 4a00 0080 4e00  .N_..Q_.L.J...N.
00000220: 0080 5100 8360 ff2f 00                   ..Q..`./.
\$\endgroup\$
6
\$\begingroup\$

BBC BASIC, 141 ASCII characters (65.217BPM)

*TEMPO1
F.i=2TO71j=i>65SOUND1-j*(479+i/2),-9,ASCM." \\VX\\VX\DHLNRVXVVNRVV>@DHD@D>@D@@HD@@>:>:6:>@DH@@HDHHLNLDHLNRVXNNVV\\",i)*2,23-j*161N.

Revised to accomodate limit on tempo. Will update explanation later.

BBC BASIC, 123 ASCII characters (noncompeting as 60BPM)

Download interpreter at http://www.bbcbasic.co.uk/bbcwin/download.html

Plays the song directly when run.

F.i=1TO67j=i>64SOUND1-j*(447+i),-9,ASCM."\\VX\\VX\DHLNRVXVVNRVV>@DHD@D>@D@@HD@@>:>:6:>@DH@@HDHHLNLDHLNRVXNV\",i)*2,5-j*75N.

Ungolfed

  FOR i = 1 TO 67
    j = i > 64: REM j=0 for the first four bars composed of 16th notes, j=-1 for the final chord (whole note)
    SOUND 1 - j * (447 + i), -9, ASC(MID$("\\VX\\VX\DHLNRVXVVNRVV>@DHD@D>@D@@HD@@>:>:6:>@DH@@HDHHLNLDHLNRVXNV\", i)) * 2, 5 - j * 75
  NEXT i

Explanation

j is a flag indicating whether we are in the first 4 bars or the final chord. TRUE is -1 in BBC BASIC.

The SOUND statement takes 4 parameters:

CHANNEL: for the first 4 bars this is channel 1. For the 3 notes of the chord in the 5th bar, the channel numbers are 201, 202 and 203 hex (513,514 and 515 decimal.) This means play on channels 1,2 and 3, the initial 2 meaning play simultaneously with 2 notes on other channels (i.e play a 3 note chord).

VOLUME: Given as a negative value because positive values represent other effects (sound envelopes.). Set at -9 (will go up to -15 which is loudest.)

PITCH: For this tune, ranges from D4=108 to A5=184. Each integer step is 1/4 of a semitone. Values are stored as ASCII codes in the range 54 to 92 and doubled to regenerate the correct value. 1/8th notes are stored as duplicate 1/16th notes. The final chord is stored as 3 separate pitches and the note length varied to whole note as below.

DURATION: in 1/20 of a second. Duration of 1/16th note is 5/20 of a second so 60 1/4 notes per minute (there is insufficient resolution to make the tempo more precise.) The whole note is 5-(-75)=80 units (4 seconds) long.

\$\endgroup\$
  • \$\begingroup\$ Shouldn't this be labeled non-competing, then? \$\endgroup\$ – JungHwan Min Dec 26 '16 at 2:41
  • \$\begingroup\$ @JungHwanMin To answer the original text of your comment: BPM is within 10% of 65. There is no bound on the precision in the question. OP's call. \$\endgroup\$ – Level River St Dec 26 '16 at 2:43
  • \$\begingroup\$ 60 bpm is audibly distinct from 65 bpm. I'll allow this to be non-competing since it's a restriction imposed by the language. \$\endgroup\$ – Mego Dec 26 '16 at 3:16
  • \$\begingroup\$ @Mego I initially thought BBC Basic counted in 100ths of a second by default, I didn`t realise it was as coarse as 20ths of a second. I have adjusted my code to 100ths, so now I can do 65.215BPM, is that OK? It took a little extra fiddling as the whole note exceeds 256/100 so I had to play it as two half notes. 50ths of a second would be shorter code but 62.5BPM is not much of an improvement. \$\endgroup\$ – Level River St Dec 26 '16 at 4:06
  • \$\begingroup\$ By the way, there were some minor transcription errors in the original post. You might need to change your code a little bit. (The current code has the wrong version) \$\endgroup\$ – JungHwan Min Dec 26 '16 at 4:06
4
\$\begingroup\$

Befunge, 242 bytes

The tune is written to stdout in the format of a MIDI file. You'll need to redirect that output to a .mid file in order to play the excerpt.

<v:"MThd"0006000101"MTrk"001+"~e"0*3"UQ"30*5"-\"9
v>9#:-#,_0"QONLJIGEJIGEGCGECB@>@B@BCEGCECBECEGECBNLJNONLJIGEQONQONQ"0\:
_v#:\%+77+1,"@",\,*8*82,+3*4!*-3::\,"@",:,*:*62,1
v>"QNJQNJ"0\:
_v#:\+1,"@",\,-**82/3\*:*62:,+!\**97!-3::\
@>,\"/U"3*,,,

Try it online!, although I don't think it's currently possible to save the output in such a way that it'll retain the binary integrity of the data.

Explanation

The first line is essential just a hard coded MIDI header, which is output at the start of line two. The rest of line two encodes the sequence of notes as their MIDI values, which are conveniently ASCII. The third line writes out the MIDI commands for playing those notes, with the duration being automatically calculated (every note is a semiquaver unless i%14 == 0). The final chord is handled as a special case on lines four and five (since that requires multiple keys being pressed simultaneously), and the sixth line writes out the final MIDI end of track marker.

\$\endgroup\$
4
\$\begingroup\$

C, 248 228 210 198 193 191 bytes

#define y(x)cos(.346*t*exp(x/17.))
d=1846,t;main(c){for(;t++<d*80;putchar(c=((t<d*64?y(("TTQRTTQRTHJLMOQRQQMOQQEFHJHFHEFHFFJHFFECECACEFHJFFJHJJLMHJLMOQRT"[t/d]-72)):y(12)+y(9)+y(5))+3)*42));}

This produces a sequence of 8 bit unsigned samples intended to be played at 8000 samples per second. If you have an older UNIX/Linux setup, you can redirect the output to /dev/audio. On some newer Linux distros, you may have to pipe the output to the ALSA command line player aplay

\$\endgroup\$
  • \$\begingroup\$ so awesome. Great solution! \$\endgroup\$ – Abel Tom Feb 5 '17 at 7:56
1
\$\begingroup\$

SmileBASIC, 115 bytes

BGMPLAY"@305T65L16[A8F+G]2A{r}F#8>F#GABAGAF#GAG8BAG8F#EF#EDEF#GABG8BAB8<C#D{r}AA1:1[R1]4F#1:2[R1]4D1{r=>AB<C#DEF#G}

Using a nice instrument was worth 4 extra bytes :)

\$\endgroup\$
  • \$\begingroup\$ What's instrument 305? \$\endgroup\$ – Pavel Feb 5 '17 at 7:08
  • \$\begingroup\$ SmileBASIC has all the general MIDI instruments, but also has a bunch of "secret" undocumented instruments. Here's an example of a much more complicated version of Pachelbel's Canon played using that instrument: dropbox.com/s/wfhr90tdkkji6qy/305.mp3?dl=0 \$\endgroup\$ – 12Me21 Feb 5 '17 at 15:47
0
\$\begingroup\$

JavaScript (ES6) using WAV.js, 325 bytes

w=new WAV();w.addProgression(btoa`9‘¹9‘¹8€¹‘9‘¹‘y‘9‘y¸€x¸x€8¸€8¸888¸€x¸€8€xù€xù‘y9`.replace(/[CF]./g,'$&#').split(/(?=[A-G])/g).map((f=t=>n=>({note:n,time:t}))(15/65)));['D5','F5#','A5'].map(n=>w.addNote(f(48/13)(n),.3,[],1,1));new Audio(URL.createObjectURL(w.toBlob())).play()
<script src="https://cdn.rawgit.com/patrickroberts/3b065ab94ce5094baacf45ed23e2a16e/raw/9c367e292fbee8341e1019d0d5953a2234449882/wav.babel.js"></script>

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.