Juste une note - instrument de musique synthétisant [fermé]

11

Déclaration

La tâche consiste à synthétiser le son (une note jouée) d'un instrument de musique (de votre choix) en utilisant la fonction dans un langage de programmation à usage général (de votre choix).

Il y a deux objectifs:

  • Qualité du son résultant. Il devrait ressembler le plus possible au véritable instrument;
  • Minimalité. Il est conseillé de garder le code sous 1 500 octets (moins s'il n'y a qu'une génération de son de base).

Seule la fonction de génération doit être fournie, le passe-partout n'est pas pris en compte pour le score.

Malheureusement, aucun score ne peut être calculé pour la fidélité du son, il ne peut donc pas y avoir de règles strictes.

Règles:

  • Aucune dépendance à l'égard des bibliothèques d'échantillons, des choses spécialisées de génération de musique;
  • Aucun téléchargement depuis le réseau ou tentative d'utilisation du microphone ou de la carte audio MIDI ou quelque chose de trop externe comme celui-ci;
  • L'unité de mesure de la taille du code est en octets. Le fichier peut être créé dans le répertoire courant. Des fichiers préexistants (tableaux de coefficients, etc.) peuvent exister, mais leur contenu est ajouté au score + ils doivent être ouverts par leur nom.
  • Le code passe-partout (non compté pour marquer) reçoit un tableau (liste) d'entiers signés et ne traite que leur sortie.
  • Le format de sortie est un petit mot endian signé de 16 bits, 44100 échantillons par seconde, avec en-tête WAV en option. Aucune tentative de sortie audio compressé au lieu de wav simple;
  • Veuillez choisir différents instruments pour la synthèse (ou une autre catégorie de qualité vs taille de code pour l'instrument); mais ne dites pas d'abord ce que vous simulez - laissez les autres utilisateurs deviner dans les commentaires;
  • Les instruments électroniques sont découragés;
  • Le tambour est un instrument. La voix humaine est un instrument.

Plaques de chaudière

Voici des passe-partout pour certaines langues. Vous pouvez également écrire une plaque de chaudière similaire pour votre langue. La fonction "g" commentée est juste pour une démo (1 seconde tonalité sinusoïdale 440 Hz).

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

Exemple

Voici la version C non golfée modelée sur le son du piano:

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);
            }
        }
    }
}

Il marque environ 1330 octets et offre une qualité médiocre / médiocre.

Vi.
la source
2
Pour être un défi de golf de code approprié, vous devez définir un critère de gain objectif. (Compte tenu de la nature de ce défi, je pense que ce sera un "concours de popularité", c'est-à-dire le plus grand nombre de votes positifs.)
boîte à pain
L'exemple ne semble pas fonctionner. La sortie est complètement déformée et contient de nombreuses ruptures. Compilé dans MinGW avec "gcc -o piano.exe piano.c" et exécuté avec "piano.exe> ​​piano.wav". Même en utilisant la simple fonction g de 440 Hz, le résultat est le même. BTW, vous pouvez utiliser M_PI à la place de vos énormes nombres. Il est défini dans math.h.
Mike C
@Mike C, Le début de la sortie du passe-partout C avec non commenté qdevrait ressembler à ceci: pastebin.com/ZCB1v7QQ . Votre hôte est-il big-endian?
Vi.
Non, j'utilise MinGW donc je suis x86. Je vais l'essayer sur une de mes box Linux. Je ne comprends pas pourquoi j'ai un problème. Étrange.
Mike C
est-ce que $><<7.chrRuby compte? : P pour 9 caractères! ou $><<?\apour 7 caractères
Poignée de porte

Réponses:

2

Java

Mon passe-partout joue le son. Je pourrais jouer g()un peu plus au golf , mais il est actuellement à 273 caractères, ce qui est bien en deçà de 1500. J'ai initialement écrit ceci pour 16 kHz pour un jeu de 4 Ko et j'ai dû ajuster un peu les constantes pour obtenir les bonnes qualités tonales à 44,1 kHz, mais je suis raisonnablement satisfait avec ça.

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();
            }
        }
    }
}

Pour en savoir plus: synthèse Karplus-Strong .

Peter Taylor
la source
Pour commencer sans PulseAudio j'utilise ceci: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
Vi.
En supposant que vous vouliez des percussions, mais vous ne savez pas laquelle exactement. Cela semble un peu trop "électronique".
Vi.
@Vi., Je vais laisser un peu de temps aux autres pour dire quel instrument ils pensent que je vise avant de le dévoiler.
Peter Taylor
Puisque les gens ont eu quelques jours pour deviner, je vais renverser les haricots. L'instrument prévu est une caisse claire.
Peter Taylor
Pouvez-vous donner un lien vers l'échantillon enregistré réel à comparer?
Vi.
2

C

Voici la g()fonction, sans le passe-partout.

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;
    }
}

Une expérience intéressante consiste à jouer avec la première boucle qui initialise une séquence de début de valeurs aléatoires. Remplacer l'appel à rand()par i*ichange le caractère du son de manière plausible (c'est-à-dire que la synthèse imite un membre différent de la même famille d'instruments). i*i*iet i*i*i*idonner d'autres qualités sonores, bien que chacune se rapproche du son rand(). Une valeur comme i*327584ou i*571, d'autre part, sonne tout à fait différent (et moins comme une imitation de quelque chose de réel).


Une autre variation mineure de la même fonction se rapproche encore plus d'un autre instrument, ou du moins de mon oreille.

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;
}

Modifié pour ajouter: je n'avais pas traité cela comme une question de code de golf, car il n'est pas signalé comme tel (au-delà de la limite de 1500 caractères), mais puisqu'il a été mentionné dans les commentaires, voici une version golfée de ce qui précède ( 96 caractères):

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();}

(Je pourrais le faire descendre en dessous de 80 caractères si je pouvais changer l'interface de fonction pour utiliser des variables globales.)

boite à pain
la source
Corde Karplus-Strong. Cela ressemble à une corde d'acier pour moi.
Peter Taylor
@PeterTaylor ma pensée exactement. La variante du bas, en revanche, me ressemble exactement à la corde en boyau (ou en nylon) d'un clavecin. Il a juste besoin du thunk de la plume qui revient pour compléter l'illusion.
boîte à pain
Après avoir retiré les espaces et le raccourcissement array, length, voidet signeddans le second code je suis arrivé le score: 113 octets. Très bon essai. Et le son est plutôt bon.
Vi.