Sadece bir not - müzik aleti sentezleme [kapalı]


11

Beyan

Görev, bazı genel amaçlı programlama dillerinde (seçtiğiniz) fonksiyonu kullanarak bazı müzik aletlerinin sesini (seçtiğiniz bir nota) sentezlemektir.

İki hedef var:

  • Ortaya çıkan ses kalitesi. Gerçek enstrümana mümkün olduğunca iyi benzemelidir;
  • Minimality. Kodun 1500 baytın altında tutulması önerilir (yalnızca temel ses üretimi varsa daha az).

Sadece üretim fonksiyonunun sağlanması gerekir, kazan için plaka sayılmaz.

Ne yazık ki, ses sadakati için hiçbir puan hesaplanamaz, bu nedenle katı kurallar olamaz.

Kurallar:

  • Örnek kütüphanelere bağımlılık yok, özel müzik üretimi şeyler;
  • Ağdan indirme veya mikrofon veya ses kartının MIDI'sini veya bunun gibi çok harici bir şeyi kullanmaya çalışmak yok;
  • Kod boyutu ölçüm birimi bayttır. Geçerli dizinde dosya oluşturulabilir. Önceden var olan dosyalar (katsayı tabloları, vb.) Bulunabilir, ancak içerikleri skora eklenir + adıyla açmaları gerekir.
  • Ortak kod (puan sayılmaz) imzalı tamsayıların dizisini (listesini) alır ve yalnızca bunların çıktısını alır.
  • Çıktı biçimi, isteğe bağlı WAV üstbilgisi ile küçük endian 16 bit sözcükler, saniyede 44100 örnek imzalanır. Düz wav yerine sıkıştırılmış ses çıkarmaya çalışmak yok;
  • Lütfen sentezlemek için farklı enstrümanlar seçin (veya enstrüman için başka bir kalite ve kod boyutu kategorisi); ancak başlangıçta neyi simüle ettiğinizi söylemeyin - diğer kullanıcıların yorumlarda tahmin yapmasına izin verin;
  • Elektronik aletler önerilmez;
  • Davul bir araçtır. İnsan sesi bir araçtır.

BoilerPlates

İşte bazı diller için ısıtıcı plakalar. Diliniz için de benzer bir kazan plakası yazabilirsiniz. Yorumlanan "g" işlevi sadece bir demo içindir (1 saniye 440 Hz sinüs tonu).

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

Misal

İşte piyano sesinden sonra modellenmemiş ungolfed C versiyonu:

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

Yaklaşık 1330 bayt alır ve kötü / vasat kalite sağlar.


2
Uygun bir codegolf mücadelesi olmak için, nesnel bir kazanma kriteri tanımlamanız gerekir. (Bu meydan doğası göz önüne alındığında, bunun yani "popülerlik yarışması", upvotes çoğu sayı gerekecek düşünüyorum.)
ekmek kutusu

Örnek işe yaramıyor gibi görünüyor. Çıktı tamamen bozuk ve içinde birçok arıza var. MinGW'de "gcc -o piano.exe piano.c" ile derlendi ve "piano.exe> ​​piano.wav" ile yürütüldü. Basit 440 Hz ton g işlevinin kullanılması bile aynı sonuca sahiptir. BTW, büyük sayılarınız yerine M_PI kullanabilirsiniz. Math.h.'de tanımlanmıştır.
Mike C

@Mike C, C kazan plakasının yorumlanmamış çıkışının başlangıcı qşöyle görünmelidir: pastebin.com/ZCB1v7QQ . Ev sahibi big-endian mı?
Vi.

Hayır, MinGW kullanıyorum, bu yüzden x86. Linux kutularımdan birinde deneyeceğim. Yine de neden sorun yaşadığımı anlamıyorum. Garip.
Mike C

yapar $><<7.chrRuby sayısında? : 9 karakter için P! veya $><<?\a7 karakter için
Kapı Tokmağı

Yanıtlar:


2

Java

Isıtıcım sesi çalıyor. g()Biraz daha golf yapabilirdim , ama şu anda 1500'ün altında olan 273 karakterde. Bir 4kB oyunu için 16kHz için yazdım ve 44.1kHz oynatımda doğru ton niteliklerini elde etmek için sabitleri biraz değiştirmek zorunda kaldım, ama ben bundan oldukça memnunum.

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

İlave değerler: Karplus-Güçlü sentez .


PulseAudio olmadan başlamak için şunu kullanın: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.

Bazı perküsyonlar istediğinizi varsayarsak, hangisinin tam olarak olduğundan emin değilsiniz. Biraz fazla "elektronik" geliyor.
Vi.

@Vi., Diğer insanların açıklamasını yapmadan önce hangi enstrümanı hedeflediklerini düşündüklerini söylemeleri için bir süre bırakacağım.
Peter Taylor

İnsanlar tahmin etmek için birkaç gün geçirdiğinden, fasulyeleri dökeceğim. Amaçlanan alet bir tuzaktır.
Peter Taylor

Karşılaştırmak için gerçek kaydedilmiş örneğe bir bağlantı verebilir misiniz?
Vi.

2

C

İşte g()kazan plakası olmadan fonksiyon.

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

İlginç bir deney, rastgele değerlerin başlangıç ​​dizisini başlatan ilk döngü ile oynamaktır. Çağrısına değiştirilmesi rand()ile i*imantıklı bir şekilde sesin karakterini değiştirir (sentez aynı enstrüman ailesinin farklı bir üyesini taklit edilir gibi olduğunu, bu sesler). i*i*ive i*i*i*iher biri gibi sese yakınlaşsa da, diğer ses özelliklerini de verin rand(). Öte yandan i*327584veya gibi bir değer i*571oldukça farklı (ve gerçek bir şeyin taklidi gibi).


Aynı işlevin bir başka küçük varyasyonu başka bir enstrümana daha da yaklaşır veya en azından kulağımdan gelir.

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

Eklemek için düzenlendi: Bunu bir kod golf sorusu olarak görmüyordum, çünkü bu gibi işaretlenmedi (1500 karakter sınırının ötesinde), ancak yorumlarda gündeme getirildiğinden, yukarıdakilerin golf edilmiş bir versiyonu ( 96 karakter):

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

(Genel değişkenleri kullanmak için işlev arayüzünü değiştirebilirsem 80 karakterin altına inebilirim.)


Karplus-Güçlü tel. Bana çelik tel gibi geliyor.
Peter Taylor

@PeterTaylor düşüncemi tam olarak. Alt varyant, diğer taraftan, bana tam olarak bir klavsenin bağırsak (veya naylon) ipi gibi geliyor. Sadece illüzyonu tamamlamak için geri dönen tüyün gövdesine ihtiyacı var.
ekmek kutusu

Boşluğu kaldırdıktan ve kısalttıktan arraysonra length, voidve signedikinci kodda skor aldım: 113 bayt. Çok güzel bir deneyin. Ve ses oldukça iyi.
Vi.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.