11
\$\begingroup\$

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.

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

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

\$\endgroup\$
  • \$\begingroup\$ Well, the spec says explicitly to take the input from a file... \$\endgroup\$ – LegionMammal978 Apr 8 '15 at 21:50
  • \$\begingroup\$ @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 :) \$\endgroup\$ – Sanchises Apr 8 '15 at 22:11
4
\$\begingroup\$

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

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!

\$\endgroup\$
1
\$\begingroup\$

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)
\$\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.