The Sound of Words

11

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.

Maltysen

Posted 2015-04-07T06:26:05.087

Reputation: 25 023

When will you be deciding the winner? – LegionMammal978 – 2015-04-08T12:54:30.303

Can we assume the input will not contain any unlisted characters? Or, what should we do when we encountered unlisted characters? – apsillers – 2015-04-08T14:09:22.203

@apsillers In the Snap ! answer, he said that he'd allow undefined behavior. – LegionMammal978 – 2015-04-08T14:40:49.720

I uploaded a sample; you were quite correct,it really doesn't sound any good... – Sanchises – 2015-04-08T20:45:15.323

Answers

6

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)
i=textread(i,'%s','whitespace','','delimiter','\n');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

Which version do you prefer? :)

Sanchises

Posted 2015-04-07T06:26:05.087

Reputation: 8 530

Well, the spec says explicitly to take the input from a file... – LegionMammal978 – 2015-04-08T21:50:14.577

@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 :) – Sanchises – 2015-04-08T22:11:45.273

4

Snap! - 401 - 75 = 326

Try it online here.

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

enter image description here

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>
ask[BPM]and wait
set tempo to(answer)bpm
repeat until<(answer)=[
ask[notes]and wait
add(answer)to(l
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.

enter image description here

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
ask[BPM]and wait
set tempo to(answer)bpm
ask[notes]and wait
repeat(length of(answer))(#)
play note(i(c)(letter(#)of(answer)))for(0.25)beats

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

Scimonster

Posted 2015-04-07T06:26:05.087

Reputation: 2 905

Does SNAP have a collection/showcase you can add these to like Scratch does? Linking directly to a runnable version would be handy IMO. – Geobits – 2015-04-07T13:15:21.237

Yes. I added a link to my project. Good idea. :) – Scimonster – 2015-04-07T15:05:42.670

Does it ignore -1 from indexOf? Right now it looks like if its not in the string, it does midi number 47. – Maltysen – 2015-04-07T15:27:51.307

@Maltysen The question never says what do to with input outside of the proper range. – Scimonster – 2015-04-07T15:28:50.590

True. I meant for it to be ignored but since I guess its my fault for not being specific enough, I'll allow it. – Maltysen – 2015-04-07T15:30:12.260

I believe you should not do BPM*025; BPM is simply the number of quarter notes per minute, not the number of measures/bars per minute

– Sanchises – 2015-04-08T15:00:06.613

4

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!

LegionMammal978

Posted 2015-04-07T06:26:05.087

Reputation: 15 731

1

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.

With whitespace and comments:

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)

apsillers

Posted 2015-04-07T06:26:05.087

Reputation: 3 632