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 ZeichenAntworten:
Java
Mein Boilerplate spielt den Ton. Ich könnte
g()
ein bisschen mehr Golf spielen , aber es liegt derzeit bei 273 Zeichen, was weit unter 1500 liegt. Ich habe dies ursprünglich für 16 kHz für ein 4-kB-Spiel geschrieben und musste die Konstanten ein wenig anpassen, um die richtigen Tonqualitäten bei der Wiedergabe von 44,1 kHz zu erzielen, aber ich bin ziemlich zufrieden damit.Weiterführende Literatur: Karplus-starke Synthese .
quelle
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
C.
Hier ist die
g()
Funktion ohne Boilerplate.Ein interessantes Experiment besteht darin, mit der ersten Schleife zu spielen, die eine Anfangssequenz von Zufallswerten initialisiert. Das Ersetzen des Aufrufs
rand()
durchi*i
ändert den Charakter des Klangs auf plausible Weise (das heißt, es klingt so, als würde die Synthese ein anderes Mitglied derselben Instrumentenfamilie imitieren).i*i*i
undi*i*i*i
geben Sie andere Klangqualitäten, obwohl jeder dem Klang näher kommtrand()
. Ein Wert wiei*327584
oderi*571
klingt dagegen ganz anders (und weniger wie eine Nachahmung von etwas Realem).Eine weitere geringfügige Variation derselben Funktion kommt einem anderen Instrument noch näher, oder zumindest meinem Ohr.
Bearbeitet, um hinzuzufügen: Ich habe dies nicht als Code-Golf-Frage behandelt, da es nicht als solches gekennzeichnet ist (über das Limit von 1500 Zeichen hinaus), aber da es in den Kommentaren erwähnt wurde, ist hier eine Golf-Version des oben genannten ( 96 Zeichen):
(Ich könnte es unter 80 Zeichen bringen, wenn ich die Funktionsoberfläche ändern könnte, um globale Variablen zu verwenden.)
quelle
array
,length
,void
undsigned
in dem zweiten Code bekomme ich die Punktzahl: 113 Bytes. Sehr schöner Versuch. Und der Sound ist ziemlich gut.