Play an RTTTL song

User quartata posted this challenge, but he neglected the fact that, for whatever reason, he's not able to play MIDI files on his computer.

Let's help him out by writing a full program that reads a song in RTTTL format from the standard input, prints the song name to the standard output and plays it (at the right speed and pitch).

Format details

RTTTL is a fairly dumb and underspecified ringtone format. It consists of a name, some default values, and a series of notes (only one note at a time) in a simple text format.

Example: fifth: d=4,o=5,b=63: 8P, 8G5, 8G5, 8G5, 2D#5

The name is a string terminated by a colon. Here the name is "fifth". Your program must accept names with at least 15 characters.

Next, the defaults section (also terminated by a colon) lists some default values for duration (d), octave (o) and the beats per minute (b) for the song. They are comma-separated and use a "key=value" syntax. There may be any number of spaces around each "key=value" part. You may assume that default values d, o and b are all present, in this order. The duration and octave will be explained below; the bpm refers to the number of beats (corresponding to quarter notes) that should be played in a minute, and you must support any integer value between 20 and 900 (inclusive).

Then the actual song is listed as a comma-separated series of notes using a "DPO" syntax, where D is the duration, P is the pitch (note) and O is the octave. There may be any number of spaces and newlines around each "DPO" part.

The duration is a power of 2 between 1 and 32 (inclusive), representing a fraction of a whole note. So for example a value of 4 (quarter note) is twice as long as a value of 8 (eighth note). The duration can be missing, in which case the default duration will be used. The duration may also be modified by the presence of a dot (.), specifically the dot makes the note last 50% longer. Since not everybody agrees about where the dot is supposed to be, you must accept a dot after the pitch or after the octave (i.e. both "DP.O" and "DPO." should work).

The pitch is one of A,B,C,D,E,F,G,A#,C#,D#,F#,G#,P where A-G# are the standard musical notes (note: no flats, use the corresponding sharp note) and P is a pause. The pitch is the only part of the note that is required, and is case-insensitive.

And finally, the octave is a number normally from 4 to 8, but you must support any number from 1 to 8 inclusive. For example, C4 is the standard middle C with a frequency of about 261.63Hz. The octave can be missing, in which case the default octave will be used. You can assume that pauses don't have an octave specified (as it has no meaning).

As mentioned in the other challenge, you can use this site to convert RTTTL songs to MIDI format for testing (but note that it may not follow the exact same specification).

Requirements:

Your program must play each note at the right speed and pitch. It can use any kind of sound (sine/triangle/square wave, piano sound, bell sound, whatever; also it can be a standard beep, wave sound or MIDI sound etc) as long as it is audible and the pitch is recognizable.

Each note must be played continuously for the specified duration or no more than a 64th note shorter than that, except if you're using something like an ADSR envelope, in which case the release phase may continue through the next pause or over the next note.

If two consecutive notes have the same pitch, they must be clearly distinguished, either through a short break (using no more than the length of a 64th note, as part of the first note's duration) or by using a non-uniform sound (such as the ADSR envelope mentioned above), or at least through a phase change if it's clear enough. Two consecutive pauses should be treated the same as a single pause with the total duration.

The program must be runnable in Linux using freely available software. It should read the song from the standard input, and print the song name to the standard output.

If the input does not match the specification above, the behavior is unspecified. Your program may ignore errors, or print a message, or play something wrong, hang or crash, it should just not do any damage.

Standard loopholes are not allowed.

Scoring

Code golf, shortest program (measured in UTF-8 bytes) wins.

• Do we have a restricted OS (i.e. no Linux-specific languages if running Windows)? – Addison Crump Oct 19 '15 at 19:16
• I believe aditsu is trying to be nice to me (:3) and make it only languages that run under Linux. I do have a really crummy old Mac laptop so feel free to make an AppleScript one – a spaghetto Oct 19 '15 at 19:17
• You knew exactly what I was going for. ;) – Addison Crump Oct 19 '15 at 19:17
• I don't have linux and so even if I knew how to write something on linux I couldn't test it. If I write an exeecutable that uses Windows API can it be run in Wine or is that not allowed? I think limiting it to linux is really limiting - kind of like those questions that limit answers to a particular programming language... – Jerry Jeremiah Oct 19 '15 at 19:39
• Working on a Perl solution just so that I can imitate the greats (Dennis, Martin) and answer a challenge about me – a spaghetto Oct 19 '15 at 20:15

Java, 813

import javax.sound.sampled.*;class R{public static void main(String[]a)throws Exception{Integer k=0;a=new
java.util.Scanner(System.in).useDelimiter("\\A").next().split(":");System.out.println(a[0]);int[]X=new
int[3],N={9,11,0,2,4,5,7};while(k<3)X[k]=k.valueOf(a[1].split(",")[k++].split("=")[1].trim());SourceDataLine
l=AudioSystem.getSourceDataLine(new AudioFormat(48000,8,1,1>0,1<0));l.open();l.start();for(String
t:a[2].toLowerCase().split(",")){a[k=0]=a[1]=a[2]="";for(char
c:t.trim().toCharArray())if(c!=46)a[k+=(c<48|c>57?1:0)^k]+=c;int
D=32/(a[k=0]==""?X[0]:k.valueOf(a[0]))*(t.contains(".")?3:2),m=a[1].charAt(0)-97,P=m>6?0:N[m]+12*(a[2]==""?X[1]:k.valueOf(a[2]))+(t.contains("#")?1:0);int
n=180000*D/X[2];for(;k<n;++k)l.write(new byte[]{(byte)(P>0&k<n-n/D?k*Math.pow(2,P/12.)/22.93:0)},0,1);}l.drain();}}


I'm still working on it.
It's a bit sensitive to CPU speed and busyness when starting.

C++, 15186 bytes

There's a link at the bottom where you can hear a sample

I present one of the least practical ways to play music on your Linux machine:

#include <SFML/Audio.hpp>
#include <cmath>
#include <iostream>
#include <fstream>
#include <string>



I unfortunately I cannot include both the golfed and ungolfed code (space constraints), and the code could be golfed further.

A large part of the reason that the file is so long is that it has to create every pitch (12 notes * 5 octaves) individually using sine waves.

Compiling I compiled using the dev cmd prompt for visual studio, but it is very similar with g++ on Linux.

cl music.cpp /I SFML\SFML-2.3.1\include /link SFML\SFML-2.3.1\lib\sfml-system.lib SFML\SFML-2.3.1\lib\sfml-audio.lib


You just need to link things properly.

credit to SFML and this post for the idea.

I think the timings are correct, let me know if they are not.

Give it a listen

Here (Link to DropBox) is a screen recording of it playing a quick rendition of the Morrowind Theme that I whipped up. Note that in the video it doesn't ask for input because the file already exists.

MorrowindTheme: d=4,o=4,b=100: 8a, 8b, 2c, 8c, 8d, 2e, 8e, 8g, 2d, 16e, 16d, 8c, 8b, a, p,  8a, 8b, 2c, 8c, 8d, 2e, 8e, 8g, 2a5, g, 8b5, a5, 8a5, 8b5, c5, b5, a5, g, f, e, 2d, c, 8e, 2d, 8c, 8b, 1a

• meta.codegolf.stackexchange.com/questions/7159/… – user46167 Oct 21 '15 at 23:48
• added accurate byte count – Liam Oct 22 '15 at 0:02
• Still, you are supposed to make some effort to golf your code. And have you heard of input redirection? – aditsu quit because SE is EVIL Oct 23 '15 at 8:34
• First, I have heard of input redirection, but have never used it, so I opted to do something I was more familiar with. Is there a resource that indicates that code should be golfed? All I could find is this(meta.codegolf.stackexchange.com/questions/714/… ). To be perfectly honest, I don't want to spend hours golfing this. If it isn't acceptable, I will remove it. – Liam Oct 23 '15 at 9:08
• Actually I found this (meta.codegolf.stackexchange.com/questions/25/…) – Liam Oct 23 '15 at 9:55