Erklärung
Die Aufgabe besteht darin, den Klang (eine gespielte Note) eines Musikinstruments (Ihrer Wahl) mithilfe einer Funktion in einer allgemeinen Programmiersprache (Ihrer Wahl) zu synthetisieren.
Es gibt zwei Ziele:
- Qualität des resultierenden Klangs. Es sollte dem echten Instrument so gut wie möglich ähneln.
- Minimalität. Es wird empfohlen, den Code unter 1500 Byte zu halten (weniger, wenn nur eine grundlegende Klangerzeugung vorhanden ist).
Es muss nur eine Generierungsfunktion bereitgestellt werden, die Boilerplate wird nicht für die Punktzahl gezählt.
Leider kann für die Wiedergabetreue keine Punktzahl berechnet werden, so dass es keine strengen Regeln geben kann.
Regeln:
- Keine Abhängigkeit von Sample-Bibliotheken, speziellen Dingen der Musikgenerierung;
- Kein Herunterladen aus dem Netzwerk oder Versuch, das MIDI eines Mikrofons oder einer Audiokarte oder etwas zu Externes wie dieses zu verwenden;
- Die Maßeinheit für die Codegröße ist Bytes. Die Datei kann im aktuellen Verzeichnis erstellt werden. Bereits vorhandene Dateien (Koeffiziententabellen usw.) sind möglicherweise vorhanden, ihr Inhalt wird jedoch zur Punktzahl hinzugefügt. Sie müssen nach Namen geöffnet werden.
- Der Boilerplate-Code (nicht zur Punktzahl gezählt) empfängt ein Array (eine Liste) von vorzeichenbehafteten Ganzzahlen und behandelt nur deren Ausgabe.
- Das Ausgabeformat besteht aus kleinen 16-Bit-Endian-Wörtern mit 44100 Abtastwerten pro Sekunde und optionalem WAV-Header. Kein Versuch, komprimiertes Audio anstelle von einfachem WAV auszugeben;
- Bitte wählen Sie verschiedene Instrumente für die Synthese aus (oder eine andere Qualitäts- oder Codegrößenkategorie für das Instrument). Sagen Sie jedoch zunächst nicht, was Sie simulieren. Lassen Sie andere Benutzer in Kommentaren raten.
- Von elektronischen Instrumenten wird abgeraten.
- Trommel ist ein Instrument. Die menschliche Stimme ist ein Instrument.
Boilerplates
Hier sind Boilerplates für einige Sprachen. Sie können eine ähnliche Kesselplatte auch für Ihre Sprache schreiben. Die auskommentierte "g" -Funktion ist nur für eine Demo gedacht (1 Sekunde 440 Hz Sinus).
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
Beispiel
Hier ist eine ungolfed C-Version nach dem Vorbild des Pianosounds:
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);
}
}
}
}
Es erreicht ungefähr 1330 Bytes und bietet eine schlechte / mittelmäßige Qualität.
q
sollte folgendermaßen aussehen: pastebin.com/ZCB1v7QQ . Ist Ihr Gastgeber Big-Endian?
$><<7.chr
in Ruby? : P für 9 Zeichen! oder $><<?\a
für 7 Zeichen