11
8
Statement
The task is to synthesize sound (one note played) of some musical instrument (of your choice) using function in some general purpose programming language (of your choice).
There are two goals:
- Quality of the resulting sound. It should resemble the real instrument as fine as possible;
- Minimality. Keeping the code under 1500 bytes is adviced (less if there's only basic sound generation).
Only generation function need to be provided, boilerplate is not counted for the score.
Unfortunately no score can be calculated for the sound fidelity, so there can't be strict rules.
Rules:
- No dependance on sample libraries, specialized music generation things;
- No downloading from network or trying to use microphone or audio card's MIDI or something too external like this;
- The code size measure unit is bytes. File can get created in current directory. Pre-existing files (coefficient tables, etc) may exist, but their content is added to the score + they must by opened by name.
- The boilerplate code (not counted to score) receives array (list) of signed integers and only deals with outputting them.
- Output format is signed little endian 16-bit words, 44100 samples per seconds, with optional WAV header. No trying to output compressed audio instead of plain wav;
- Please choose different instruments for synthesizing (or other quality vs code size category for the instrument); but don't initially tell what are you simulating - let other users to guess in comments;
- Electronic instruments are discouraged;
- Drum is an instrument. Human voice is an instrument.
Boilerplates
Here are boilerplates for some languages. You can write similar boiler plate for your language as well. Commented out "g" function is just for a demo (1 second 440 Hz sine tone).
C:
//#!/usr/bin/tcc -run
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
/*
void g(signed short *array, int* length) {
*length = 44100;
int i;
for(i=0; i<44100; ++i) array[i]=10000*sin(i*2.0*3.14159265358979323*440.0/44100.0);
}
*/
// define your g here
signed short array[44100*100];
int main(int argc, char* argv[]) {
int size=0;
memset(array,0,sizeof array);
// i(array); // you may uncomment and implement some initialization
g(array, &size);
fwrite("RIFFH\x00\x00\x00WAVEfmt\x20\x12\x00\x00\x00\x01\x00\x01\x00\x44\xac\x00\x00\x88X\x01\x00\x02\x00\x10\x00\x00\x00LIST\x1a\x00\x00\x00INFOISFT\x0e\x00\x00\x00GolfNote\0\0\0\0\0\0data\x00\xff\xff\xff", 1, 80, stdout);
fwrite(array, 1, size*sizeof(signed short), stdout);
return 0;
}
Python 2:
#!/usr/bin/env python
import os
import re
import sys
import math
import struct
import array
#def g():
# return [int(10000*math.sin(1.0*i*2*3.141592654*440.0/44100.0)) for i in xrange(0,44100)]
# define your g here
sys.stdout.write("RIFFH\x00\x00\x00WAVEfmt\x20\x12\x00\x00\x00\x01\x00\x01\x00\x44\xac\x00\x00\x88X\x01\x00\x02\x00\x10\x00\x00\x00LIST\x1a\x00\x00\x00INFOISFT\x0e\x00\x00\x00GolfNotePy\0\0\0\0data\x00\xff\xff\xff");
array.array("h", g()).tofile(sys.stdout);
Perl 5:
#!/usr/bin/perl
#sub g() {
# return (map 10000*sin($_*3.14159265358979*2*440.0/44100.0), 0..(44100-1))
#}
# define you g here
my @a = g();
print "RIFFH\x00\x00\x00WAVEfmt\x20\x12\x00\x00\x00\x01\x00\x01\x00\x44\xac\x00\x00\x88X\x01\x00\x02\x00\x10\x00\x00\x00LIST\x1a\x00\x00\x00INFOISFT\x0e\x00\x00\x00GolfNotePl\0\0\0\0data\x00\xff\xff\xff";
print join("",map(pack("s", $_), @a));
Haskell:
#!/usr/bin/runhaskell
import qualified Data.Serialize.Put as P
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as C8
import Data.Word
import Control.Monad
-- g :: [Word16]
-- g = map (\t->floor $ 10000 * sin(t*2*3.14159265358979*440/44100)) [0..44100-1]
-- insert your g here
main = do
B.putStr $ C8.pack $ "RIFFH\x00\x00\x00WAVEfmt\x20\x12\x00\x00\x00\x01\x00\x01\x00\x44\xac\x00\x00\x88X\x01\x00\x02\x00\x10\x00\x00\x00LIST\x1a\x00\x00\0INFOISFT\x0e\x00\x00\x00GolfNote\0\0\0\0\0\0data\x00\xff\xff\xff"
B.putStr $ P.runPut $ sequence_ $ map P.putWord16le g
Example
Here's ungolfed C version modeled after piano sound:
void g(signed short *array, int* length) {
*length = 44100*5;
int i;
double overtones[]={4, 1, 0.5, 0.25, 0.125};
double freq[] = {393, 416, 376, 355, 339, 451, 555};
double freq_k[] = {40, 0.8, 1, 0.8, 0.7, 0.4, 0.25};
double corrector = 1/44100.0*2*3.14159265358979323;
double volumes_begin[] ={0, 0.025, 0.05, 0.4};
double volumes_end [] ={0.025, 0.05, 0.4, 5};
double volumes_kbegin[]={0, 1.8, 1, 0.4};
double volumes_kend [] ={1.8, 1, 0.4, 0};
for(i=0; i<44100*5; ++i) {
int j;
double volume = 0;
for(j=0; j<sizeof volumes_begin/sizeof(*volumes_begin); ++j) {
double t = i/44100.0;
if(t>=volumes_begin[j] && t<volumes_end[j]) {
volume += volumes_kbegin[j]*(volumes_end[j]-t )/(volumes_end[j]-volumes_begin[j]);
volume += volumes_kend[j] *(t-volumes_begin[j])/(volumes_end[j]-volumes_begin[j]);
}
}
int u;
for(u=0; u<sizeof freq/sizeof(*freq); ++u) {
for(j=0; j<sizeof overtones/sizeof(*overtones); ++j) {
double f = freq[u]*(j+1);
array[i] += freq_k[u]*volume*10000.0/(f)/1*overtones[j]*sin(1.0*i*corrector*f);
}
}
}
}
It scores approximately 1330 bytes and provides poor/mediocre quality.
2To be an appropriate codegolf challenge, you are required to define an objective winning criterion. (Given the nature of this challenge, I think it'll have to be "popularity contest", i.e. most number of upvotes.) – breadbox – 2013-10-25T01:15:55.090
The example doesn't seem to work. The output is completely distorted and has many break-ups in it. Compiled in MinGW with "gcc -o piano.exe piano.c" and executed with "piano.exe >piano.wav". Even using the simple 440 Hz tone g function has the same result. BTW, you can use M_PI in place of your huge numbers. It's defined in math.h. – Mike C – 2013-10-25T04:22:17.230
@Mike C, The beginning of output of C boilerplate with uncommented
– Vi. – 2013-10-25T09:21:26.463q
should look like this: http://pastebin.com/ZCB1v7QQ . Is your host big-endian?No, I'm using MinGW so I'm x86. I'll try it on one of my Linux boxes. I don't understand why I'm having a problem though. Strange. – Mike C – 2013-10-25T17:25:51.887
does
$><<7.chr
in Ruby count? :P for 9 characters! or$><<?\a
for 7 chars – Doorknob – 2013-10-26T16:35:42.460Don't understand the question. Typed that in
ruby1.9.1
started in console, got no response... (I'm not a Ruby programmer). Score counting is simple: 1. Remove everything expect of the function that does the actual math and it's dependencies, 2. Save this to file, 3. See the file size. – Vi. – 2013-10-27T00:56:47.017@Vi. Maybe it only works in Windows... ASCII 7 is the 'bell' character, which is supposed to make a "beep" noise :P – Doorknob – 2013-10-27T02:41:42.627
@Doorknob, Even if it works, it's not by the rules:
No dependance on sample libraries, specialized music generation things
, such as system beep sound, which can be considered as a sample and OS as musical library in this context. – Vi. – 2013-10-27T02:43:50.670