Solo una nota: strumento musicale che sintetizza [chiuso]


11

dichiarazione

Il compito è sintetizzare il suono (una nota suonata) di alcuni strumenti musicali (a vostra scelta) usando la funzione in un linguaggio di programmazione generico (a vostra scelta).

Ci sono due obiettivi:

  • Qualità del suono risultante. Dovrebbe assomigliare il più possibile allo strumento reale;
  • Minimalità. Mantenere il codice sotto i 1500 byte è consigliato (meno se c'è solo la generazione del suono di base).

È necessario fornire solo la funzione di generazione, la caldaia non viene conteggiata per il punteggio.

Sfortunatamente nessun punteggio può essere calcolato per la fedeltà del suono, quindi non ci possono essere regole rigide.

Regole:

  • Nessuna dipendenza da biblioteche campione, cose specializzate nella generazione di musica;
  • Nessun download dalla rete o tentativo di utilizzare il MIDI del microfono o della scheda audio o qualcosa di troppo esterno come questo;
  • L'unità di misura della dimensione del codice è di byte. Il file può essere creato nella directory corrente. Possono esistere file preesistenti (tabelle dei coefficienti, ecc.), Ma il loro contenuto viene aggiunto alla partitura + devono essere aperti per nome.
  • Il codice boilerplate (non conteggiato per il punteggio) riceve un array (elenco) di numeri interi con segno e si occupa solo di emetterli.
  • Il formato di output è composto da parole little endian a 16 bit, 44100 campioni al secondo, con intestazione WAV opzionale. Nessun tentativo di emettere audio compresso invece di wav semplice;
  • Scegliere strumenti diversi per la sintesi (o altra categoria di qualità rispetto alla dimensione del codice per lo strumento); ma inizialmente non dire cosa stai simulando: lascia che altri utenti indovinino nei commenti;
  • Gli strumenti elettronici sono scoraggiati;
  • Il tamburo è uno strumento. La voce umana è uno strumento.

boilerplates

Ecco le piastre di cottura per alcune lingue. Puoi anche scrivere una piastra di caldaia simile per la tua lingua. La funzione "g" commentata è solo per una demo (tono sinusoidale di 1 secondo 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

Esempio

Ecco la versione C non modificata modellata sul suono del 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);
            }
        }
    }
}

Ha un punteggio di circa 1330 byte e offre una qualità scadente / mediocre.


2
Per essere una sfida codegolf appropriata, è necessario definire un criterio vincente oggettivo. (Data la natura di questa sfida, penso che dovrà essere un "concorso di popolarità", vale a dire il maggior numero di voti.)
breadbox

L'esempio non sembra funzionare. L'output è completamente distorto e contiene molte interruzioni. Compilato in MinGW con "gcc -o piano.exe piano.c" ed eseguito con "piano.exe> ​​piano.wav". Anche usando la semplice funzione tone g da 440 Hz ha lo stesso risultato. A proposito, puoi usare M_PI al posto dei tuoi enormi numeri. È definito in math.h.
Mike C,

@Mike C, L'inizio dell'output della caldaia con C non commentato qdovrebbe essere simile al seguente: pastebin.com/ZCB1v7QQ . Il tuo host è big-endian?
Vi.

No, sto usando MinGW quindi sono x86. Lo proverò su una delle mie scatole Linux. Non capisco perché sto avendo un problema però. Strano.
Mike C,

conta $><<7.chrin Ruby? : P per 9 caratteri! o $><<?\aper 7 caratteri
Maniglia della porta

Risposte:


2

Giava

La mia piastra della caldaia riproduce il suono. Potrei giocare g()a golf un po 'di più, ma attualmente è a 273 caratteri che è ben al di sotto dei 1500. Inizialmente ho scritto questo per 16kHz per un gioco da 4kB e ho dovuto modificare un po' le costanti per ottenere le giuste qualità tonali alla riproduzione a 44.1kHz, ma io ne sono ragionevolmente felice.

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

Ulteriori letture: sintesi Karplus-Strong .


Per iniziare senza PulseAudio lo uso come segue: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.

Supponendo che volessi delle percussioni, ma non sei sicuro di quale esattamente. Sembra un po 'troppo "elettronico".
Vi.

@Vi., Lascerò un po 'di tempo agli altri per dire a quale strumento pensano che stia puntando prima di svelarlo.
Peter Taylor,

Dato che la gente ha avuto qualche giorno per indovinare, ho intenzione di rovesciare i fagioli. Lo strumento previsto è un rullante.
Peter Taylor,

Potete fornire un collegamento al campione registrato effettivo da confrontare?
Vi.

2

C

Ecco la g()funzione, senza la caldaia.

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

Un esperimento interessante è suonare con il primo loop che inizializza una sequenza iniziale di valori casuali. Sostituire la chiamata rand()con i*icambia il carattere del suono in modo plausibile (cioè, sembra che la sintesi stia imitando un membro diverso della stessa famiglia di strumenti). i*i*ie i*i*i*idare altre qualità sonore, sebbene ognuna si avvicini di più al suono rand(). Un valore simile i*327584o i*571, d'altra parte, sembra abbastanza diverso (e meno come un'imitazione di qualcosa di reale).


Un'altra variazione minore della stessa funzione si avvicina ancora di più a un altro strumento, o almeno lo fa al mio orecchio.

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

Modificato per aggiungere: non lo stavo trattando come una domanda di golf del codice, dal momento che non è contrassegnato come tale (oltre il limite di 1500 caratteri), ma poiché è stato riportato nei commenti, ecco una versione golf di quanto sopra ( 96 caratteri):

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

(Potrei farlo scendere sotto gli 80 caratteri se potessi cambiare l'interfaccia di funzione per usare le variabili globali.)


Corda Karplus-Strong. Mi sembra una corda d'acciaio.
Peter Taylor,

@PeterTaylor il mio pensiero esattamente. La variante inferiore, invece, mi suona esattamente come la corda di budello (o nylon) di un clavicembalo. Ha solo bisogno del tonfo della penna di ritorno in seguito per completare l'illusione.
breadbox,

Dopo aver rimosso la spazi e accorciamento array, length, voide signednella seconda il codice ho ottenuto il punteggio: 113 byte. Bel tentativo. E il suono è piuttosto buono.
Vi.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.