34
\$\begingroup\$

This is Weekly Challenge #1. Theme: Audio Processing

Your task is to write a program, which writes an audio file to disc (in a format of your choice), which contains the Morse code for 2015, i.e.

..--- ----- .---- .....

You are free to choose any sort of sound for the segments, like a single-frequency sine wave, a chord, noise, some instrument (e.g. using MIDI files), as long as it's audible. However, there are some constraints on the timing:

  • Short segments need to be at least 0.2 seconds in length.
  • Long segments need to be at least 3 times as long as short segments.
  • Breaks between segments within a digit should be the same length as short segments.
  • Breaks between digits should be the same length as long segments.
  • Each segment and break may deviate up to 10% from the average length of that type of segment/break.
  • The entire audio file may not be longer than 30 seconds.

The breaks don't need to be completely silent, but the Morse segments should be audibly louder than the breaks.

Note that you have to write an audio file. You cannot just play the sound, e.g. using system beeps. You're allowed to use any sort of library to handle the file format and audio generation, but you must not use built-in features for Morse encoding.

This is code golf, so the shortest answer (in bytes) wins.

Please consider linking to an upload of the resulting audio file (on SoundCloud or similar), so people can check out the result without having to run your code. If you upload to SoundCloud, please make sure to enable downloads in the Permissions tab of the track.

If your output uses a rather uncommon file format, please add some information about how to play it back and/or convert it to a more common format and upload it.

Example track

This is a manually generated example track which conforms with the spec and uses noise for the Morse segments (microphone background noise, to be precise). Here is a link to SoundCloud if the embedded player doesn't work for you.

Bounty Details

I will award the bounty to the shortest submission in an audio programming language, i.e. a language that is designed to synthesise sound. That list is not complete, so feel free to use another audio programming language, if you know one. If you're not sure whether some language you want to use classifies as an audio programming language, please let me know in the comments or in chat, and we can discuss that.

Note that your submission still has to comply with all the rules - in particular, it has to write a file, which might not be possible in all audio programming languages. E.g., as far as I can tell, gibber can only play the sound and not save it to a file.

\$\endgroup\$
10
  • 1
    \$\begingroup\$ Additional Challenge: Make it sound really nice. \$\endgroup\$
    – Kaz Wolfe
    Commented Jan 2, 2015 at 20:47
  • 7
    \$\begingroup\$ @Mew How nice can Morse possibly sound? \$\endgroup\$ Commented Jan 2, 2015 at 21:19
  • 1
    \$\begingroup\$ Doing this in Brainf**k would make this awesome on so many bonus levels. \$\endgroup\$
    – Mast
    Commented Jan 4, 2015 at 17:02
  • \$\begingroup\$ @Mast Probably, but unfortunately, BF can't write to a file. ;) (I'll make sure to be more lenient with that next time.) \$\endgroup\$ Commented Jan 4, 2015 at 17:03
  • \$\begingroup\$ Are music notation formats, that only contain the score and no audio (.mid, .abc seen below) considered acceptable as an "audio file"? What about "tracker" formats, that contain both samples and a score, but no rendered audio track (.mod, .xm)? \$\endgroup\$
    – Tobia
    Commented Jan 6, 2015 at 15:42

14 Answers 14

13
\$\begingroup\$

x86 machine code (.COM file): 121 120 113 109 bytes

Hexdump:

00000000  b4 3e bb 01 00 cd 21 b4  3c 31 c9 ba 3e 01 cd 21  |.>....!.<1..>..!|
00000010  4a b4 09 cd 21 be 56 01  8a 0c 46 e8 0c 00 b1 32  |J...!.V...F....2|
00000020  e8 07 00 81 fe 6d 01 75  ef c3 88 cb c0 e3 07 c1  |.....m.u........|
00000030  e1 04 30 d2 b4 02 00 da  cd 21 e2 fa c3 2e 73 6e  |..0......!....sn|
00000040  64 00 00 00 18 ff ff ff  ff 00 00 00 02 00 00 10  |d...............|
00000050  00 00 00 00 01 24 33 33  99 99 99 66 99 99 99 99  |.....$33...f....|
00000060  99 66 33 99 99 99 99 66  33 33 33 33 33           |.f3....f33333|
0000006d

Can be easily run under DosBox; the output is a .SND file named SND. Here is a FLAC version of its output (and here the .COM file).

Commented assembly:

    org 100h

start:
    ; close stdout
    mov ah,3eh
    mov bx,1
    int 21h
    ; open snd
    mov ah,3ch
    xor cx,cx
    mov dx,filename
    int 21h
    ; write the header
    ; we used the `snd` part of the header as file name, back off one byte
    dec dx
    mov ah,9h
    int 21h
    mov si,data
.l:
    ; data read cycle
    ; read the current byte in cl (zero-extending to 16-bit)
    ; notice that ch is already zero (at the first iteration it's 0 from the
    ; int 21h/3ch, then we are coming from gen, which leaves cx to zero)
    mov cl,[si]
    ; move to next byte
    inc si
    ; generate the tone
    call gen
    ; generate the pause
    mov cl,50
    call gen
    ; repeat until we reach the end of data
    cmp si,eof
    jne .l
    ; quit
    ret

gen:
    ; generate a sawtooth wave at sampling frequency/2 Hz
    ; receives length (in samples>>4) in cx, with lowest bit indicating if
    ; it has to write a wave or a pause
    mov bl,cl
    ; shift the rightmost bit all the way to the left; this kills the
    ; unrelated data and puts a 128 in bl (if cx & 1 != 0)
    shl bl,7
    ; rescale the samples number
    shl cx,4
    ; zero the starting signal
    xor dl,dl
    ; prepare the stuff for int 21h
    mov ah,2h
.l:
    ; increment the signal
    add dl,bl
    ; write it
    int 21h
    ; decrement iteration count and loop
    loop .l
    ret

    ; .SND file header (4096 samples, mono, PCM)
header:
    db "."
    ; we also use "snd" as the file name
filename:
    db "snd",0,0,0,24,0xff,0xff,0xff,0xff,0,0,0,2,0,0,0x10,0,0,0,0,1
    ; terminator for int 21h/ah=9h
    db '$'
data:
    ; generated by gendata.py
    incbin "data.dat"
eof:

The data.dat included above is an easy-to-use representation of the morse string (lower bit: sound on/sound off, upper 7 bits: sound length in samples >> 4) generated by a Python script:

#!/usr/bin/env python2
import sys

# source string
s = "..--- ----- .---- ....."
# samples
sr = 4096
conv =  {
            '.': 1 | (((sr/5) >> 4) & ~1),    # dot:   1/5 second, dI/dt=1
            '-': 1 | (((sr/5*3) >> 4) & ~1),  # line:  3/5 second, dI/dt=1
            ' ':     ((sr/5*2) >> 4) & ~1     # space: 2/5 second (+1/5 from the always-present pause), dI/dt=0 (silent)
        }
sys.stdout.write(''.join(chr(conv[a]) for a in s))
\$\endgroup\$
3
  • \$\begingroup\$ You don't necessarily need a file extension, if that can save you four bytes. \$\endgroup\$ Commented Jan 3, 2015 at 11:32
  • 4
    \$\begingroup\$ @MartinBüttner: actually, it allows me to save 3 bytes. The a of a.snd is put just before the SND header, which starts with .snd followed by a zero byte, so I get the .snd part for free and I recycle its zero-terminator. Also, the fact that the header starts one byte after the file name allows me to use a inc dx to move to the header (1 byte) instead of a mov dx, header (3 bytes). OTOH, if I were allowed to call it just .snd alone I could save two bytes, but I'm not sure that real DOS would allow that (the treatment of the extension under DOS was quite peculiar). \$\endgroup\$ Commented Jan 3, 2015 at 14:42
  • \$\begingroup\$ I did some tests with calling the file .SND: I got .SND on DosBox, SND~1 on FreeDOS, and I expect something else on "real" DOS; thus, it's definitely "undefined behavior" area. In the end, I settled with calling the file SND (1 byte less due to the removed a, keeping the cost of the inc dx - which becomes dec dx). \$\endgroup\$ Commented Jan 5, 2015 at 3:35
8
\$\begingroup\$

Mathematica - 130

r = Riffle;
s = SoundNote;
Export["m.mid", 
 Sound@
   r[Flatten@
     r[
       s[0,.4(Boole@#+.5)]&/@Array[#>4&,5,5-#]&/@{2,0,1,5},
       (b=None~s~#&)@.6
     ],[email protected]
   ]
]

Play online

\$\endgroup\$
4
  • \$\begingroup\$ Oh you can also use infix notation for Export, like "m.mid"~Export~Sound@.... \$\endgroup\$ Commented Jan 2, 2015 at 15:52
  • \$\begingroup\$ (b=None~s~#&)@.6 should be (b=None~s~#&)@.4 Also, you can save 3 chars using r = Riffle; s = SoundNote; Export["m.mid", Sound@r[r[Table[s[0, If[{1, 2, 11}~MemberQ~k || k > 15, .2, .6]], {k, 20}], None~s~.2], None~s~.4, 11]] \$\endgroup\$
    – DavidC
    Commented Jan 2, 2015 at 23:15
  • \$\begingroup\$ Martin, But there was already a .2 in each break. .4 + .2 \$\endgroup\$
    – DavidC
    Commented Jan 4, 2015 at 14:17
  • \$\begingroup\$ @DavidCarraher Ah, you're right. \$\endgroup\$ Commented Jan 4, 2015 at 18:43
7
\$\begingroup\$

Perl 5: 94 122 140

SND files have simpler headers, no need to print in binary. This versions produce 8khz mono SND file named 'a':

open A,'>a';print
A".snd",pack"N*",24,-1,2,8e3,1,map{(--$|x3)x(894261072>>$_&1?1600:400)}0..39

The result file.

Old solution. Produces a 1khz 8-bit mono WAV file named 'a':

open
A,'>a';print
A pack"A4lA8lssllssA4ls*",RIFF,17040,WAVEfmt,16,1,1,(1e3)x2,1,8,data,17004,map{($_)x(894261072>>$v++&1?400:100)}(255,0)x20

The result file.

To get to 122 characters I had to paste the header in binary instead of packing it which makes the code hard to copy here. The escaped version is:

open
A,'>a';print
A"RIFF\x90B\0\0WAVEfmt \0\0\0\0\0\xe8\0\0\xe8\0\0\0\0datalB\0\0",pack"s*",map{($_)x(894261072>>$v++&1?400:100)}(255,0)x20

Base64 encoding of the actual 122 bytes solution:

b3BlbgpBLCc+YSc7cHJpbnQKQSJSSUZGkEIAAFdBVkVmbXQgEAAAAAEAAQDoAwAA6AMAAAEACABk
YXRhbEIAACIscGFjayJzKiIsbWFweygkXyl4KDg5NDI2MTA3Mj4+JHYrKyYxPzQwMDoxMDApfSgy
NTUsMCl4MjA=
\$\endgroup\$
1
  • \$\begingroup\$ U could use .au extension, maybe. Well done! \$\endgroup\$ Commented Jan 8, 2015 at 22:02
7
\$\begingroup\$

AWK: 172 170 bytes

...and without using any wave library! (*)

BEGIN{for(;++i<204;){d=d"\177\177\n";D=D"\n\n"}t=d d d D;d=d D;T=D D D;u=t t t;print".snd\0\0\0\30\0\0\221\306\0\0\0\2\0\0\20\0\0\0\0\1"d d u T u t t T d u t T d d d d d}

This outputs a Sun au audio file on stdout which can be played by vlc (among others). While the au fileformat don't have any sample rate limitation, VLC refuses to play any file with a sampling rate inferior to 4096 Hz, so I used this frequency

EDIT: Link to resulting audio file on DropBox


(*) Shouldn't there be a bonus for this? ;o)

\$\endgroup\$
3
  • \$\begingroup\$ You don't need the space in the d=d "\177... concatenation. That saves a byte. But when I play the resulting audio file, it sounds like it's missing the last dit of the 5. \$\endgroup\$
    – Mark Reed
    Commented Jan 7, 2015 at 20:48
  • \$\begingroup\$ Thanks. I saved a second byte with another concatenation using the same trick. I just checked the audio file with vlc 2.1.1 and it sounds complete. What player did you use? \$\endgroup\$
    – LeFauve
    Commented Jan 8, 2015 at 0:33
  • \$\begingroup\$ I was using QuickTime Player on OS X. I opened it in VLC and it sounds fine, so never mind. Apple's fault, not yours. \$\endgroup\$
    – Mark Reed
    Commented Jan 8, 2015 at 1:31
7
\$\begingroup\$

Python, 155

Uses the python built-in wave module.

import wave
n=wave.open(*"nw")
k=17837
n.setparams((2,2,k,0,"NONE",0))
h=k*1314709609
while h:[n.writeframes(`i%9`)for i in[0]*(2-h%2)*k+range(h%4*k)];h/=4

Writes to a file called n.

Thanks Sp3000 for suggestion on using list comprehension for loop (this helped remove a bit of indentation).

Listen to it:

https://soundcloud.com/bitpwner/morse-the-new-year-2015

Here is a link to SoundCloud if the embedded player doesn't work for you.

Commented code:

import wave
n=wave.open("n.wav","w")         # Open a wav file for writing
k=44100                            
n.setparams((2,2,k,0,"NONE","")) # Sets the minimal params for the wav file
w=n.writeframes
h=23450475295733                 # Contains base-4 morse: '.'=1, '-'=3, ' '=0
while h:
    for i in range(h%2*k):w(h%4*chr(i%99)) # Writes saw-tooth signal using i%99
    w((2-h%2)*k*" ")                       # Writes the pauses
    h/=4
\$\endgroup\$
7
  • \$\begingroup\$ Since w is a side-effect I think you can list comp to save two bytes: while h:[w(h%4*chr(i%99))for i in range(h%2*k)];w((2-h%2)*k*" ");h/=4 \$\endgroup\$
    – Sp3000
    Commented Jan 2, 2015 at 12:53
  • \$\begingroup\$ @Sp3000 oo... didn't think of that =D. thx! \$\endgroup\$
    – Vectorized
    Commented Jan 2, 2015 at 12:56
  • \$\begingroup\$ This might be a stupid question, but if h is divided by 4 for each iteration, how does the while loop stop? \$\endgroup\$ Commented Jan 2, 2015 at 18:05
  • \$\begingroup\$ @Derek朕會功夫 For python, when h becomes 0, it evaluates to False, terminating the loop. The while loop is a golfing trick to extract the values in the sequence "11333033333013333011111" one by one. \$\endgroup\$
    – Vectorized
    Commented Jan 2, 2015 at 18:09
  • \$\begingroup\$ @bitpwner I get it that 0 evaluates to false, but isn't it that for all positive numbers, if you divide it by another positive number, there's no way you can get a 0 out of it? \$\endgroup\$ Commented Jan 2, 2015 at 18:12
6
\$\begingroup\$

C#, 556 552 536 535 516 506 503 491 483 bytes

Uses the library Wav.Net.

using System;using System.Linq;namespace WavDotNet.Core{using S=Samples<float>;class P{static void Main(){var w=new WavFileWrite<float>("a",9999);var b=new Tools.Generators.Sawtooth(9999);Func<long,float,S>g=(t,a)=>b.Generate32Bit(new TimeSpan(t),99,a);var l=2500000;S x=g(l,1),y=g(l*3,1),z=g(l*3,0),_=g(l,0),v=new S(new[]{x,_,x,_,y,_,y,_,y,z,y,_,y,_,y,_,y,_,y,z,x,_,y,_,y,_,y,_,y,z,x,_,x,_,x,_,x,_,x}.SelectMany(c=>c).ToList());w.AudioData.Add(new Channel<float>(v,0));w.Flush();}}}

Outputs to a file named a.

Result hosted on Dropbox

Ungolfed code:

using System;
using System.Linq;
namespace WavDotNet.Core
{
    using FloatSamples = Samples<float>;
    class P
    {
        static void Main()
        {
            var file = new WavFileWrite<float>("output.wav", 9999);
            var sawtoothGen = new Tools.Generators.Sawtooth(9999);
            Func<long, float, FloatSamples> generate = (t, amplitude) => sawtoothGen.Generate32Bit(new TimeSpan(t), 99, amplitude);
            var length = 2500000;
            FloatSamples shortBeep = generate(length, 1),
            longBeep = generate(length * 3, 1),
            shortPause = generate(length * 3, 0),
            longPause = generate(length, 0),
            allSamples = new FloatSamples(new[] { shortBeep, longPause, shortBeep, longPause, longBeep, longPause, longBeep, longPause, longBeep, shortPause,
                longBeep, longPause, longBeep, longPause, longBeep, longPause, longBeep, longPause, longBeep, shortPause, 
                shortBeep, longPause, longBeep, longPause, longBeep, longPause, longBeep, longPause, longBeep, shortPause, 
                shortBeep, longPause, shortBeep, longPause, shortBeep, longPause, shortBeep, longPause, shortBeep }
                .SelectMany(c => c).ToList());
            file.AudioData.Add(new Channel<float>(allSamples, 0)); // 0 == ChannelPositions.Mono
            file.Flush();
        }
    }
}
\$\endgroup\$
0
5
\$\begingroup\$

Python 3 2, 191 188 174 171 (no libraries)

Wav files are incredibly simple. Wanted to try without libraries. For some reason my files seem to crash Windows Media Player. Quicktime works bugs out halfway into the file. Conversion to a larger sample rate using Audition fixes this.

Update: Implemented some optimizations from the Perl answer. Now outputs with just the name n and in 1000Hz sampling. Edited info above accordingly.

w,s=200*" ",50*"~~  "
a,b,c=s+w,3*s+w,2*w
open(*"nw").write("RIFF\xD46\0\0WAVEfmt \20\0\0\0\1\0\1\0\xE8\3\0\0\xE8\3\0\0\1\0\10\0data\xB06\0\0"+a*2+b*3+c+b*5+c+a+b*4+c+a*5)

Old version

w,s=1600*" ",200*"~~~~    "
a,b,c=s+w,3*s+w,2*w
open("n.wav","wb").write("RIFF\244\265\1\0WAVEfmt \20\0\0\0\1\0\1\0@\x1f\0\0@\x1f\0\0\1\0\10\0data\200\265\1\0"+a*2+b*3+c+b*5+c+a+b*4+c+a*5)
\$\endgroup\$
4
\$\begingroup\$

AWK BASH: 66 86 67 74 bytes

As requested by Martin Büttner, I added a tempo since after checking the ABC Notation standard, it seems there is no defined default value for this (thanks nutki for pointing this).
I also write into a disk file (a) instead of STDOUT since the question wanted explicitly "a file on disc".

a=C3z;echo "X:1
K:A
Q:99
CzCz$a$a$a3$a$a$a$a${a}3Cz$a$a$a${a}3CzCzCzCzC">a

I put a tempo of 99 which causes the audio file to last 22 seconds; It's slower than my previous version, but at least now it is supposed to be the same length on every ABC player, and it fit under 30 seconds.

It looks ... very much like the previous version as you can see: Last (I hope :o) ) version of 2015's score

Here is the new midi file.

First BASH version (tempo is missing)

Why didn't I think to this first... :o)

That's 22 less bytes than with AWK, for the same result

a=C3z;echo "X:1
K:A
CzCz$a$a$a3$a$a$a$a${a}3Cz$a$a$a${a}3CzCzCzCzC"

Like the previous version in AWK, it write on stdout a valid "ABC" notation file (thanks Tobia for finding out that the "L" statement was optional)

It looks like this: last version of "2015" partition

And it sounds exactly like the previous version.

Previous version in AWK (86 Bytes)

Here is a new version; a little longer, but with a more accurate timing. I let the first version bellow for comparison/reference:

BEGIN{a="C3z";print"X:1\nK:A\nL:1/8\nCzCz"a a a"3"a a a a a"3Cz"a a a a"3CzCzCzCzCz";}

This is still a valid "abc" file, which looks like this: score of 2015

Here is the new midi file (I accelerated the tempo to stay under the 30 seconds limit).

First version in AWK (66 Bytes):

This is a lot less interesting than my previous answer, but it is a lot shorter, so:

BEGIN{print"X:1\nK:A\nL:1/4\nCCC2C2C2zC2C2C2C2C2zCC2C2C2C2zCCCCC"}

This outputs a valid "abc" file, which can be read into (among others) EasyABC. It will look like this: Score of "2015" in morse

and it will sound like this (midi file). +

\$\endgroup\$
11
  • \$\begingroup\$ You should post it as ABC without the AWK wrapping and claim the bounty! \$\endgroup\$
    – Tobia
    Commented Jan 5, 2015 at 23:55
  • \$\begingroup\$ Tobia, ABC is a file format, not a programming language. And so far with 86 bytes this is the shortest answer... The question is now "is the resulting sound close enough from requirements for the answer to be valid?" \$\endgroup\$
    – LeFauve
    Commented Jan 6, 2015 at 7:34
  • \$\begingroup\$ I would have called it a file format too, but the wiki page the OP linked lists it as a Sound programming language. I've sumbitted my own ABC file as an entry, alongside a more proper Csound program. Let's see what they think of it. \$\endgroup\$
    – Tobia
    Commented Jan 6, 2015 at 9:49
  • \$\begingroup\$ That's the problem with wikis... sometime people editing them make mistakes :o) . Lot's of different things can be called "programming languages" but I think ABC is not one of them. Anyways, thanks for finding out the "L" was optional. It saved me a couple of bytes ;o) \$\endgroup\$
    – LeFauve
    Commented Jan 6, 2015 at 14:39
  • \$\begingroup\$ "Long segments need to be at least 3 times as long as short segments." The last music line shown doesn't meet the requirements. \$\endgroup\$
    – mbomb007
    Commented Jan 8, 2015 at 20:31
4
\$\begingroup\$

C# ~ 485 bytes

Using the Wav.Net library.

using WavDotNet.Core;namespace System.Collections.Generic{using f=Single;class T{static void Main(){var i=new WavFileWrite<f>("x",8000);Func<long,Samples<f>>g=d=>new WavDotNet.Tools.Generators.Sawtooth(8000).Generate32Bit(new TimeSpan(d),600,1);var s=new List<f>();var k="..--- ----- .---- .....";foreach(var c in k){s.AddRange(c=='.'?g(2000000):g(6000000));s.AddRange(new f[1600]);if(c==' '){s.AddRange(new f[3200]);}}i.AudioData.Add(new Channel<f>(new Samples<f>(s),0));i.Flush();}}}

And here's the output.

Readable version,

using WavDotNet.Core;

namespace System.Collections.Generic
{
    using f = Single;

    class T
    {
        static void Main()
        {
            var i = new WavFileWrite<f>("x", 8000);
            Func<long, Samples<f>> g = d => new WavDotNet.Tools.Generators.Sawtooth(8000).Generate32Bit(new TimeSpan(d), 600, 1);
            var s = new List<f>();
            var k = "..--- ----- .---- .....";

            foreach (var c in k)
            {
                s.AddRange(c == '.' ? g(2000000) : g(6000000));
                s.AddRange(new f[1600]);

                if (c == ' ')
                {
                    s.AddRange(new f[3200]);
                }
            }

            i.AudioData.Add(new Channel<f>(new Samples<f>(s), 0));
            i.Flush();
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ You can save some bytes by wrapping your class inside the namespace System.Collections.Generic (that actually works). There's also some unnecessary whitespace you can remove. \$\endgroup\$
    – ProgramFOX
    Commented Jan 2, 2015 at 14:58
4
\$\begingroup\$

C# 382 333bytes

Doesn't use any non-standard libraries, writes out an 8bits-per-sample 44100samples-per-second wav, with what I hope is a valid header (seems to play/load happily in WMP/.NET/Audacity).

The header is base64 encoded, and the morse is encoded as signal on/off which is stored in a single long (64bits) because the last 5 bits are the same as the first.

The result can be found here

Golfed code:

using System.IO;class P{static void Main(){using(var w=new FileStream("w.wav",FileMode.Create)){int i=0,d=9980;w.Write(System.Convert.FromBase64String("UklGRhCCCgBXQVZFZm10IBAAAAABAAEARKwAAESsAAABAAgAZGF0YeyBCgA="),0,44);for(var k=5899114207271221109L;i++<d*69;w.WriteByte((byte)(System.Math.Sin((k>>(i/d)%64&1)*i*0.1)*127+127)));}}}

With comments:

using System.IO;

class P
{
    static void Main()
    {
        using(var w=new FileStream("w.wav",FileMode.Create))
        {
            int i=0,d=9980; // d is samples per tone

            // write wav header
            w.Write(System.Convert.FromBase64String("UklGRhCCCgBXQVZFZm10IBAAAAABAAEARKwAAESsAAABAAgAZGF0YeyBCgA="),0,44);

            for(var k=5899114207271221109L; // 0101000111011101110111010001110111011101110111000111011101110101 as long
                i++<d*69; // 69 is number of bits
                w.WriteByte((byte)(
                    System.Math.Sin(
                        (k>>(i/d)%64&1) // read bit (0 or 1)
                        *i*0.1) // mul by ticker (sin(0) = 0)
                    *127+127)) // make sensible
            );
        }
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ The challenge doesn't require the name of the file to end with .wav, so you can save 4 bytes there. \$\endgroup\$
    – ProgramFOX
    Commented Jan 3, 2015 at 13:39
  • \$\begingroup\$ Nice way of fitting the 69 bits PWM code into a 64bit constant. I was trying something like that but probably I could not shorten my code using your method. \$\endgroup\$
    – nutki
    Commented Jan 3, 2015 at 13:46
2
\$\begingroup\$

SuperCollider, 625 605 bytes

Audio programming language submission!

Output is written to a file b in AIFF format. Windows Media Player fails to open it, but it works fine in VLC media player. The generated file a is an OSC file.

c=0;d=0;f={c=c+d;d=0.2;[c,[\s_new,\w,1001,0,0,\freq,800]]};g={c=c+d;d=0.2;[c,[\n_free,1001]]};h={c=c+d;d=0.6;[c,[\s_new,\w,1001,0,0,\freq,800]]};i={c=c+d;d=0.6;[c,[\n_free,1001]]};x=[f.value,g.value,f.value,g.value,h.value,g.value,h.value,g.value,h.value,i.value,h.value,g.value,h.value,g.value,h.value,g.value,h.value,g.value,h.value,i.value,f.value,g.value,h.value,g.value,h.value,g.value,h.value,g.value,h.value,i.value,f.value,g.value,f.value,g.value,f.value,g.value,f.value,g.value,f.value,i.value];Score.recordNRT(x,"morse.osc","Morse2015.aiff");SynthDef("w",{arg freq=440;Out.ar(0,SinOsc.ar(freq,0,0.2))}).writeDefFile;

I created a few SuperCollider functions: f generates a short beep, g a short break, h a long beep and i a long break. SuperCollider needs the starting positions for each sine wave and not a length, so I had to create functions that generate a wave with the correct starting position and I have to call the functions each time I need a sine wave. (I could not store a wave with a specific length in a variable to reuse). The \w definition is created at the end of the code block.

On my Windows computer, it did not save the audio file in the same directory as my code, but in this directory:

C:\Users\MyName\AppData\Local\VirtualStore\Program Files (x86)\SuperCollider-3.6.6

Result hosted on Dropbox

Code with indentation:

c = 0;
d = 0;
f = { c=c+d;d=0.2;[ c, [ \s_new, \w, 1001, 0, 0,  \freq, 800 ] ] };
g = { c=c+d;d=0.2; [ c, [\n_free, 1001]] };
h = { c=c+d;d=0.6; [ c, [ \s_new, \w, 1001, 0, 0, \freq, 800]]};
i = { c=c+d;d=0.6;[ c, [\n_free, 1001]] };

x = [ f.value, g.value, f.value, g.value, h.value, g.value, h.value, g.value, h.value, i.value,
      h.value, g.value, h.value, g.value, h.value, g.value, h.value, g.value, h.value, i.value,
      f.value, g.value, h.value, g.value, h.value, g.value, h.value, g.value, h.value, i.value,
    f.value, g.value, f.value, g.value, f.value, g.value, f.value, g.value, f.value, i.value
];

Score.recordNRT(x, "morse.osc", "Morse2015.aiff");

SynthDef("w",{ arg freq = 440;
    Out.ar(0,
         SinOsc.ar(freq, 0, 0.2)
    )
}).writeDefFile;
\$\endgroup\$
0
2
+200
\$\begingroup\$

ChucK - 1195 217 201 147 145 144

ChucK is an audio programming language. bitpwner helped me get this down from 201 bytes to 147 bytes.

SinOsc s;WvOut w=>blackhole;"y"=>w.wavFilename;int j;for(1016835=>int i;i>0;2/=>i){s=>w;j++;(i%2?200:600)::ms=>now;s=<w;(j%5?200:600)::ms=>now;}

Here is a direct link to SoundCloud if the embedded player doesn't work for you.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Managed to cut it down to 164 with a similar trick used in my answer: WvOut w=>blackhole;"x"=>w.wavFilename;SinOsc s=>w;0=>int j;for(1016835=>int i;i>0;2/=>i){j++;300=>s.freq;(600-i%2*400)::ms=>now;s=<w;(j%5>0?200:600)::ms=>now;s=>w;} \$\endgroup\$
    – Vectorized
    Commented Jan 5, 2015 at 20:08
  • \$\begingroup\$ @bitpwner You're using j to avoid the array? \$\endgroup\$
    – KSFT
    Commented Jan 5, 2015 at 20:10
  • \$\begingroup\$ The magic number 1016835 in binary is 11111000010000000011. j is there to simply to keep track of the pauses between each digit of 2015 (each digit has 5 sounds). \$\endgroup\$
    – Vectorized
    Commented Jan 5, 2015 at 20:12
  • \$\begingroup\$ @bitpwner Ah, I didn't even notice that. That's a really cool idea! \$\endgroup\$
    – KSFT
    Commented Jan 5, 2015 at 20:12
  • \$\begingroup\$ You can edit it into your answer to have a higher chance of bounty. imo, the improvement is not a lot to justify a new answer sinc you already did the bulk of the work figuring out Chuck ;) \$\endgroup\$
    – Vectorized
    Commented Jan 5, 2015 at 20:15
2
\$\begingroup\$

Csound, 140 + 40 = 180

Audio programming language.

This is the Orchestra file:

instr 1
a1 oscil 2^15,990,1
out a1
endin

and this is the Score file:

f1 0 512 10 1
t0 300
i1 0 1
i1 2
i1 40
i1 60
i1 62
i1 64
i1 66
i1 68
i1 4 3
i1 8
i1 12
i1 18
i1 22
i1 26
i1 30
i1 34
i1 42
i1 46
i1 50
i1 54

The sizes are computed assuming no extra whitespace, single line terminator (UNIX) and no terminator after the last line.

You invoke them using the csound command:

csound morse.org morse.sco

which will produce an output file in the current directory, by default named "test.aif"

https://soundcloud.com/whatfireflies/morse-code-golf-in-csound/s-qzsaq

I could have shaved two or three bytes by choosing an uglier waveform, but I like the sound of the traditional Morse sine wave.

PS: I'm a total newbie at Csound, any golfing tips are appreciated, especially concerning the score file!

\$\endgroup\$
1
  • \$\begingroup\$ Ah, I was looking forward to CSound. If no one had posted an answer in it, I probably would have tried to write one myself. :) \$\endgroup\$ Commented Jan 6, 2015 at 0:44
1
\$\begingroup\$

brainfuck, 649 bytes

++[>-[+>++[++<]>]>[>..........-..........+<-]-[+>++[++<]>]>[....................-]<<<<<-]+++[>+++[>-[+>++[++<]>]>[>..........-..........+<-]<<<-]-[+>++[++<]>]>[....................-]<<<-]-[+>++[++<]>]>[....................-]+++++[>-[+>++[++<]>]>[....................-]+++[>-[+>++[++<]>]>[>..........-..........+<-]<<<-]<<<-]+++[>-[+>++[++<]>]>[....................-]<<<-]-[+>++[++<]>]>[>..........-..........+<-]++++[>-[+>++[++<]>]>[....................-]+++[>-[+>++[++<]>]>[>..........-..........+<-]<<<-]<<<-]++[>-[+>++[++<]>]>[....................-]<<<-]+++++[>-[+>++[++<]>]>[....................-]-[+>++[++<]>]>[>..........-..........+<-]<<<<<-]

This generates a sequence of 8 bit unsigned samples that may be played at 8000 samples per second with a tool such as aplay on Linux. Credit to table of BF constants.

Try it online!

Somewhat less golfed

DIT DIT DAH DAH DAH
++[>
 -[+>++[++<]>]>[>..........-..........+<-]
 -[+>++[++<]>]>[....................-]
<<<<<-]
+++[>
 +++[>
  -[+>++[++<]>]>[>..........-..........+<-]
 <<<-]
 -[+>++[++<]>]>[....................-]
<<<-]
-[+>++[++<]>]>[....................-]
DAH DAH DAH DAH DAH
+++++[>
 -[+>++[++<]>]>[....................-]
 +++[>
  -[+>++[++<]>]>[>..........-..........+<-]
 <<<-]
<<<-]
+++[>
 -[+>++[++<]>]>[....................-]
<<<-]
DIT DAH DAH DAH DAH
-[+>++[++<]>]>[>..........-..........+<-]
++++[>
 -[+>++[++<]>]>[....................-]
 +++[>
  -[+>++[++<]>]>[>..........-..........+<-]
 <<<-]
<<<-]
++[>
 -[+>++[++<]>]>[....................-]
<<<-]
DIT DIT DIT DIT DIT
+++++[>
 -[+>++[++<]>]>[....................-]
 -[+>++[++<]>]>[>..........-..........+<-]
<<<<<-]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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