Play some of Pachelbel's Canon

29

2

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>

Mego

Posted 2016-12-25T17:00:29.270

Reputation: 32 998

Would that snippet be competitive as it downloads it from elsewhere – Blue – 2016-12-25T17:09:23.197

4@muddyfish No, because fetching the output from an external source is a standard loophole. – Mego – 2016-12-25T17:10:04.733

Would outputting a midi file instead of audio be acceptable? – James – 2016-12-25T17:31:19.777

@DJMcMayhem Yes, MIDI files are acceptable. – Mego – 2016-12-25T17:32:23.970

I assume the final (only) chord is required in full? That effectively prohibits non-polyphonic languages / API's – Level River St – 2016-12-25T19:39:55.540

@LevelRiverSt Yes. That exact excerpt is to be played. – Mego – 2016-12-25T19:52:49.837

Is it okay to output two sixteenth notes for an eighth note (and sixteen sixteenth notes for a whole note)? – JungHwan Min – 2016-12-26T03:25:00.113

@JungHwanMin If it's indistinguishable from an actual eighth note (meaning the first doesn't taper noticeably before the second begins), sure. – Mego – 2016-12-26T03:26:22.000

Would you consider the tempo 64.8649 close enough to 65? – JungHwan Min – 2016-12-26T03:36:59.987

@JungHwanMin Yeah that's fine. A 0.1401 bpm difference would take several more measures to become audible than are present in this except. – Mego – 2016-12-26T03:38:21.077

Let us continue this discussion in chat.

– Mego – 2016-12-26T03:40:00.363

Is outputting an ABC-notation version OK?

– FlipTack – 2016-12-30T12:21:00.370

@FlipTack If you can find a codec for it (or, rather, a translation program that translates ABC notation to some common audio format) that existed prior to this challenge, sure. – Mego – 2016-12-31T01:00:00.510

Just pointing out that this melody was originally in 32th notes. – ericw31415 – 2017-12-21T02:10:23.467

@DLosc: QBasic deserves an answer here also! – sergiol – 2018-06-12T00:13:13.953

Answers

9

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.

user81655

Posted 2016-12-25T17:00:29.270

Reputation: 10 181

880*2**(-~-n%20/12) should save you a few bytes. – Neil – 2016-12-26T12:42:30.470

@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). – user81655 – 2016-12-26T12:46:54.920

Saw this after I posted my answer, lol – Patrick Roberts – 2017-02-05T11:27:09.887

You can save another byte by replacing c=new AudioContext; with with(new AudioContext) and removing the two occurrences of c. in the program. – Patrick Roberts – 2017-02-05T12:18:42.957

Thanks @PatrickRoberts. I used a previous answer of mine as a template so you saved me a byte on that as well!

– user81655 – 2017-02-08T00:14:22.767

Do you need to use exponentiation or can you replace *2** with <<? – kamoroso94 – 2018-02-08T16:38:05.383

1@kamoroso94 No. The exponent can be a fraction, so if I used << it would cast it to an integer. – user81655 – 2018-02-09T00:24:33.257

8

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.

JungHwan Min

Posted 2016-12-25T17:00:29.270

Reputation: 13 290

14Really, Mathematica doesn't have a built-in for Pachelbel's Canon? – Stewie Griffin – 2016-12-25T23:06:58.490

2@StewieGriffin Only the whole thing probably, and the cost of clipping it would be too high. – Mego – 2016-12-26T00:09:46.910

@StewieGriffin Surprisingly, it doesn't. – JungHwan Min – 2016-12-26T01:53:00.743

6

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..`./.

Mego

Posted 2016-12-25T17:00:29.270

Reputation: 32 998

6

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.

Level River St

Posted 2016-12-25T17:00:29.270

Reputation: 22 049

Shouldn't this be labeled non-competing, then? – JungHwan Min – 2016-12-26T02:41:07.217

@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. – Level River St – 2016-12-26T02:43:34.823

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. – Mego – 2016-12-26T03:16:18.660

@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. – Level River St – 2016-12-26T04:06:48.447

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) – JungHwan Min – 2016-12-26T04:06:57.597

65.215 is close enough, if that's as close as you can get. – Mego – 2016-12-26T04:07:24.630

Brings back memories to my youth where I had an amstrad with basic on it. I could transform my keyboard into a music machine by using the SOUND command! – Carra – 2016-12-30T11:57:54.663

@Carra I had forgotten that the Amstrad BASIC was so similar to BBC BASIC https://en.wikipedia.org/wiki/Locomotive_BASIC. The reason for the particular scope of SOUND commands on the BBC was that there was a third party sound chip built into it and that set the particular scope of available functionality. I had the Electron, a cut down version of the BBC, which lacked the chip and produced similar (for compataibility) but reduced functionality (i.e only one volume setting) by other means.

– Level River St – 2016-12-30T16:49:42.257

4

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.

James Holderness

Posted 2016-12-25T17:00:29.270

Reputation: 8 298

4

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

ceilingcat

Posted 2016-12-25T17:00:29.270

Reputation: 5 503

so awesome. Great solution! – Abel Tom – 2017-02-05T07:56:32.133

1

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 :)

12Me21

Posted 2016-12-25T17:00:29.270

Reputation: 6 110

What's instrument 305? – Pavel – 2017-02-05T07:08:21.967

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: https://www.dropbox.com/s/wfhr90tdkkji6qy/305.mp3?dl=0

– 12Me21 – 2017-02-05T15:47:56.023

0

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>

Patrick Roberts

Posted 2016-12-25T17:00:29.270

Reputation: 2 475