11
\$\begingroup\$

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.

\$\endgroup\$

closed as off-topic by cat, Mego, Denker, Blue, Rɪᴋᴇʀ Apr 19 '16 at 14:28

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions without an objective primary winning criterion are off-topic, as they make it impossible to indisputably decide which entry should win." – cat, Mego, Denker, Blue, Rɪᴋᴇʀ
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 2
    \$\begingroup\$ To 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.) \$\endgroup\$ – breadbox Oct 25 '13 at 1:15
  • \$\begingroup\$ 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. \$\endgroup\$ – Mike C Oct 25 '13 at 4:22
  • \$\begingroup\$ @Mike C, The beginning of output of C boilerplate with uncommented q should look like this: pastebin.com/ZCB1v7QQ . Is your host big-endian? \$\endgroup\$ – Vi. Oct 25 '13 at 9:21
  • \$\begingroup\$ 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. \$\endgroup\$ – Mike C Oct 25 '13 at 17:25
  • \$\begingroup\$ does $><<7.chr in Ruby count? :P for 9 characters! or $><<?\a for 7 chars \$\endgroup\$ – Doorknob Oct 26 '13 at 16:35
2
\$\begingroup\$

Java

My boilerplate plays the sound. I could golf g() a bit more, but it's currently at 273 chars which is well under 1500. I originally wrote this for 16kHz for a 4kB game and had to tweak the constants a bit to get the right tonal qualities at 44.1kHz playback, but I'm reasonably happy with it.

import java.io.*;
import javax.sound.sampled.*;

public class codegolf13003 {
    byte[]g(){byte[]d=new byte[88000];int r=1,R=1103515247,b[]=new int[650],i,o,s,y;for(i=0;i<b.length;r*=R)b[i++]=0x4000+((r>>16)&0x3fff);for(i=o=0;i<d.length;o=s){s=(o+1)%b.length;y=(b[o]+b[s])/2*((r&0x10000)<1?-1:1);r*=R;d[i++]=(byte)(b[o]=y);d[i++]=(byte)(y>>8);}return d;}

    public static void main(String[] args) throws Exception {
        byte[] data = new codegolf13003().g();
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 1, 2, 44100, false/*LE*/);
        AudioInputStream stream = new AudioInputStream(bais, format, data.length / 2);
        new Previewer().preview(stream);
    }

    static class Previewer implements LineListener {
        Clip clip;

        public void preview(AudioInputStream ais) throws Exception {
            AudioFormat audioFormat = ais.getFormat();
            DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);

            clip = (Clip)AudioSystem.getLine(info);
            clip.addLineListener(this);

            clip.open(ais);
            clip.start();
            while (true) Thread.sleep(50); // Avoid early exit of program
        }

        public void update(LineEvent le) {
            LineEvent.Type type = le.getType();
            if (type == LineEvent.Type.CLOSE) {
                System.exit(0);
            }
            else if (type == LineEvent.Type.STOP) {
                clip.close();
            }
        }
    }
}

Further reading: Karplus-Strong synthesis.

\$\endgroup\$
  • \$\begingroup\$ To start without PulseAudio I use this: java -Djavax.sound.sampled.Clip=com.sun.media.sound.DirectAudioDeviceProvider -Djavax.sound.sampled.Port=com.sun.media.sound.PortMixerProvider -Djavax.sound.sampled.SourceDataLine=com.sun.media.sound.DirectAudioDeviceProvider -Djavax.sound.sampled.TargetDataLine=com.sun.media.sound.DirectAudioDeviceProvider codegolf13003 \$\endgroup\$ – Vi. Oct 25 '13 at 9:33
  • \$\begingroup\$ Assuming you wanted some percussions, but unsure which one exactly. It sounds a bit too "electronical". \$\endgroup\$ – Vi. Oct 25 '13 at 9:35
  • \$\begingroup\$ @Vi., I'll leave it a while for other people to say which instrument they think I'm aiming for before I unveil it. \$\endgroup\$ – Peter Taylor Oct 26 '13 at 7:17
  • \$\begingroup\$ Since people have had a few days to guess, I'm going to spill the beans. The intended instrument is a snare. \$\endgroup\$ – Peter Taylor Nov 1 '13 at 9:00
  • \$\begingroup\$ Can you give a link to actual recorded sample to compare? \$\endgroup\$ – Vi. Nov 1 '13 at 17:02
2
\$\begingroup\$

C

Here's the g() function, without the boilerplate.

void g(signed short *array, int* length)
{
    short r[337];
    int c, i;

    *length = 44100 * 6;
    for (i = 0 ; i < 337 ; ++i)
        r[i] = rand();
    *array = *r;
    for (i = c = 1 ; i < *length ; ++i) {
        array[i] = r[c];
        r[c] = (r[c] + array[i - 1]) * 32555 / 65536;
        c = (c + 1) % 337;
    }
}

An interesting experiment is to play with the first loop that initializes a beginning sequence of random values. Replacing the call to rand() with i*i changes the character of the sound in a plausible way (that is, it sounds like the synthesis is imitating a different member of the same instrument family). i*i*i and i*i*i*i give other sound qualities, though each one gets closer to sounding like rand(). A value like i*327584 or i*571, on the other hand, sounds quite different (and less like an imitation of something real).


Another minor variation of the same function comes even closer to another instrument, or at least it does to my ear.

void g(signed short *array, int* length)
{
    int i;

    *length = 44100 * 6;
    for (i = 0 ; i < 337 ; ++i)
        array[i] = rand();
    for ( ; i < *length ; ++i)
        array[i] = (array[i - 337] + array[i - 1]) * 32555 / 65536;
}

Edited to Add: I hadn't been treating this as a code golf question, since it's not flagged as such (beyond the 1500-char limit), but since it's been brought up in the comments, here's a golfed version of the above (96 characters):

g(short*a,int*n){int i=0;for(*n=1<<18;i<*n;++i)
a[i]=i>336?32555*(a[i-337]+a[i-1])/65536:rand();}

(I could get it down below 80 characters if I could change the function interface to use global variables.)

\$\endgroup\$
  • \$\begingroup\$ Karplus-Strong string. Sounds like a steel string to me. \$\endgroup\$ – Peter Taylor Oct 26 '13 at 7:17
  • \$\begingroup\$ @PeterTaylor my thought exactly. The bottom variant, on the other hand, sounds to me exactly like the gut (or nylon) string of a harpsichord. It just needs the thunk of the returning quill afterwards to complete the illusion. \$\endgroup\$ – breadbox Oct 26 '13 at 16:22
  • \$\begingroup\$ After removing the whitespace and shortening array, length, void and signed in the second code I got the score: 113 bytes. Very nice try. And the sound is rather good. \$\endgroup\$ – Vi. Oct 27 '13 at 1:05

Not the answer you're looking for? Browse other questions tagged or ask your own question.