# The Sound of Words

People have written many things allowing one thing to be visualized as another. Now I propose being able to translate letters to music! Its your job to write a program that takes a text file and outputs a sound file with each letter converted to a specific note from C3-C8.

Disclaimer - I don't actually expect the music to sound any good but I hope to be surprised.

## Specifications

• You take the name of a file as a string and the BPM (beats per minute) as an int
• You make uppercase A to be the note C3
• And go up a half-step for every character after in this order: ABCDEFGHIJKLMNOPQRSTUVWXYZ ()-,;.'"abcdefghijklmnopqrstuvwxyz
• Yes, not every character will be used since that spans too many octaves.
• You translate every character in the file in this way
• Put the notes together at the given BPM as quarter notes
• Either save as a sound file (I'm guessing midi will be the easiest, but anything is ok) or play it
• Any sane input format is fine
• No standard loopholes
• This is code-golf, so shortest code in bytes wins!

## Bonuses

• Allow transposing of output - 50 bytes
• Allow multiple tracks to be superimposed over each other by taking multiple files - 75 bytes
• Join consecutive letters into one longer note - 50 bytes

Edit: Wow, we have negative scores. I'll be announcing the winner on Saturday.

• When will you be deciding the winner? Apr 8 '15 at 12:54
• Can we assume the input will not contain any unlisted characters? Or, what should we do when we encountered unlisted characters? Apr 8 '15 at 14:09
• @apsillers In the Snap ! answer, he said that he'd allow undefined behavior. Apr 8 '15 at 14:40
• I uploaded a sample; you were quite correct,it really doesn't sound any good... Apr 8 '15 at 20:45

# MATLAB, 159-50-50-75 = -16

Sample input

Sample output

Generates pure sine sound waves, very funky (sawtooth is also possible, with an even better score, but that sounds a bit... too funky). Works as a function, so expects it a character array (['abc';'def']) with one row per 'track'. I think that is covered under 'any sane input format', but if the general consensus is that I need to read a file, I suppose I can change it. Input i is text tracks (of equal length), b beats per minute and t transpose (supply 0 for not transposed). It blends two sines into one by offsetting the sine input, so I got all three bonuses, giving me a negative score.

function v(i,b,t)
s=0;for r=1:size(i)
o=[];for k=i(r,:)
o=cat(2,o,sin(55*pi*2^((k-28+t)/12)*(numel(o)/2^13+(0:1/2^13:60/b))));end
s=s+o;end
sound(s/max(s))
end


# Version using input file: 211-175=36

Input argument i now represents the file name, other parameters unchanged. Might not work on newer releases because I'm getting a warning that textread may soon be deprecated. EDIT: textread apparently automatically splits up at whitespaces, so I fixed that. Also, I think I may have accidentally contacted some aliens with the weird sounds made while testing.

function v(i,b,t)
o=[];for k=i{r,:}
o=cat(2,o,sin(55*pi*2^((k-28+t)/12)*(numel(o)/2^13+(0:1/2^13:60/b))));end
s=s+o;end
sound(s/max(s))
end


Which version do you prefer? :)

• Well, the spec says explicitly to take the input from a file... Apr 8 '15 at 21:50
• @LegionMammal978 If you insist: .mat files are of a sane input format. Create a .mat file with a 'i' variable that has your text. Then, add load(i); at the start of line 2 of the top version. Score: -16+8=-8. I'll do this when your comment gets more upvotes than mine, or when the OP has an opinion :) Apr 8 '15 at 22:11

## Snap! - 401 - 75 = 326

Try it online here.

I'm using this method of counting bytes for the program.

I added playing multiple sounds at once.

The basic structure is the same as the original (see below), but with the addition of launch{}. launch{} starts a new thread with the code inside, allowing for concurrency.

The code as text is:

set[c v]to[ABCDEFGHIJKLMNOPQRSTUVWXYZ ()-,;.'"abcdefghijklmnopqrstuvwxyz
set[l v]to(list>
end
delete(last v)of(l
for each(i)of(l
launch{
repeat(length of(i))(#
play note(i(c)(letter(#)of(i)))for(0.25)beats

(i(h)(n))
report(call(JavaScript function ([h][n]) {[return h.indexOf(n)+48]})with inputs(h)(n


Original code, 308.

Lucky Snap! has MIDI playing built in. ;)

Unfortunately, it doesn't have an indexOf function, so i have to make an external JavaScript call, which is pretty expensive.

The repeat () (#) block comes from the iteration library.

The code can be written out as text like this, which is how i get 308 bytes:

set[c v]to[ABCDEFGHIJKLMNOPQRSTUVWXYZ ()-,;.'"abcdefghijklmnopqrstuvwxyz

(i(h)(n))
report(call(JavaScript function ([h][n]) {[return h.indexOf(n)+48]})with inputs(h)(n

• Does SNAP have a collection/showcase you can add these to like Scratch does? Linking directly to a runnable version would be handy IMO. Apr 7 '15 at 13:15
• Yes. I added a link to my project. Good idea. :) Apr 7 '15 at 15:05
• Does it ignore -1 from indexOf? Right now it looks like if its not in the string, it does midi number 47. Apr 7 '15 at 15:27
• @Maltysen The question never says what do to with input outside of the proper range. Apr 7 '15 at 15:28
• True. I meant for it to be ignored but since I guess its my fault for not being specific enough, I'll allow it. Apr 7 '15 at 15:30

# Mathematica, 219 - 50 - 75 - 50 = 44

c=CharacterRange;d=Import;EmitSound[Function[b,Sound[Split@Characters@d@b/.a:{__String}:>SoundNote[StringPosition[c["A","Z"]<>" ()-,;.'\""<>c["a","z"],a[[1]]][[1,1]]+#3-12,60Length@a/#2],{0,60StringLength@d@b/#2}]]/@#]&


Takes the list of input files, BPM, and number of half-steps to transpose by as input and plays the sound (from a piano, any other instrument would take more bytes.) Doesn't sound that bad!

# JavaScript (ES6) 377 - 50 - 50 - 75 = 202

First, here's a runnable snippet that uses <input> fields instead of file-reads:

<b>BMP:</b> <input id="bpm" size=3 placeholder="BMP" value="120"> <b>Transpose:</b> <input size=3 id="transpose" placeholder="Transpose" value="0"><br/><br/><div id="tracks" style="float:left;padding-right:5px;"><input placeholder="Track" class="track"></div><button id="add">Add Additional Track</button><div style="clear:both; padding-top:5px;"></div><button id="play"><b>Play</b></button><script>f=function(s,b,z){C=new (window.AudioContext||window.webkitAudioContext);b=6e4/b;s.map(function(p){var o=C.createOscillator(t=setTimeout);o.connect(C.destination);o.start();p.split("").map(function(c,i){t(function(){o.frequency.value=440*Math.pow(2, ("ABCDEFGHIJKLMNOPQRSTUVWXYZ ()-,;.'\"abcdefghijklmnopqrstuvwxyz".indexOf(c)-21+z)/12)},b*i)});t(function(){o.stop()},p.length*b)})};document.getElementById("play").onclick=function(){f([].map.call(document.getElementsByClassName("track"),function(e){return e.value;}),+document.getElementById("bpm").value,+document.getElementById("transpose").value);};document.getElementById("add").onclick=function(){var i=document.createElement("input");i.placeholder="Track";i.className="track";document.getElementById("tracks").appendChild(document.createElement("br"));document.getElementById("tracks").appendChild(i);};</script>

And now, the actual entry:

f=(n,b,z)=>{C=new AudioContext;b=6e4/b;s=n.map(m=>(x=new XMLHttpRequest,x.open("GET",m,0),x.send(),x.responseText));s.map(p=>{var o=C.createOscillator(t=setTimeout);o.connect(C.destination);o.start();[...p].map((c,i)=>t(_=>o.frequency.value=440*Math.pow(2,("ABCDEFGHIJKLMNOPQRSTUVWXYZ ()-,;.'\"abcdefghijklmnopqrstuvwxyz".indexOf(c)-21+z)/12),b*i)),t(_=>o.stop(),p.length*b)})}


The three arguments are an array of filepath strings to play concurrently, notes per minute, and number of half-steps to transpose all inputs.

f=(n,b,z)=>{
C=new AudioContext;
b=6e4/b;

// fill s with the contents of each file
s = n.map(m=>(x=new XMLHttpRequest,x.open("GET",m,0),x.send(),x.responseText));

// play each track
s.map(p=>{
var o=C.createOscillator(t=setTimeout);
o.connect(C.destination);
o.start();

// queue up each note with setTimeout
[...p].map((c,i)=>
t(_=>
o.frequency.value=440*
Math.pow(2,
("ABCDEFGHIJKLMNOPQRSTUVWXYZ ()-,;.'\"abcdefghijklmnopqrstuvwxyz".indexOf(c)-21+z)/12
),
b*i)
);
// queue up termination of those track
t(_=>o.stop(),p.length*b)})
}

f(["file:///home/users/apsillers/notes.txt",
"file:///home/users/apsillers/notes2.txt"],
240, 5)