Hanya satu not - alat musik yang disintesis [ditutup]

11

Pernyataan

Tugasnya adalah untuk mensintesis suara (satu nada dimainkan) dari beberapa alat musik (pilihan Anda) menggunakan fungsi dalam beberapa bahasa pemrograman tujuan umum (pilihan Anda).

Ada dua tujuan:

  • Kualitas suara yang dihasilkan. Itu harus menyerupai instrumen nyata sehalus mungkin;
  • Minimalitas Menyimpan kode di bawah 1500 byte disarankan (kurang jika hanya ada generasi suara dasar).

Hanya fungsi pembangkitan yang perlu disediakan, boilerplate tidak dihitung untuk skor.

Sayangnya tidak ada skor yang dapat dihitung untuk kesetiaan suara, sehingga tidak ada aturan yang ketat.

Aturan:

  • Tidak ada ketergantungan pada perpustakaan sampel, hal-hal generasi musik khusus;
  • Tidak mengunduh dari jaringan atau mencoba menggunakan MIDI mikrofon atau kartu audio atau sesuatu yang terlalu eksternal seperti ini;
  • Unit ukuran ukuran kode adalah byte. File dapat dibuat di direktori saat ini. File yang sudah ada sebelumnya (tabel koefisien, dll) mungkin ada, tetapi konten mereka ditambahkan ke skor + mereka harus dibuka dengan nama.
  • Kode boilerplate (tidak dihitung untuk skor) menerima array (daftar) dari bilangan bulat yang ditandatangani dan hanya berurusan dengan mengeluarkannya.
  • Format output ditandatangani sedikit kata-kata endian 16-bit, 44100 sampel per detik, dengan header WAV opsional. Tidak mencoba untuk mengeluarkan audio terkompresi, bukan wav biasa;
  • Silakan pilih instrumen yang berbeda untuk disintesis (atau kategori ukuran kode vs kualitas lainnya untuk instrumen); tetapi pada awalnya jangan memberi tahu apa yang Anda simulasi - biarkan pengguna lain menebak dalam komentar;
  • Instrumen elektronik tidak disarankan;
  • Drum adalah instrumen. Suara manusia adalah instrumen.

Pelat boiler

Berikut adalah beberapa pelat untuk beberapa bahasa. Anda dapat menulis pelat ketel yang serupa untuk bahasa Anda juga. Mengomentari fungsi "g" hanya untuk demo (1 detik 440 Hz nada 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

Contoh

Inilah versi C ungolfed yang dimodelkan setelah suara 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);
            }
        }
    }
}

Skornya sekitar 1330 byte dan memberikan kualitas buruk / biasa-biasa saja.

Vi.
sumber
2
Untuk menjadi tantangan codegolf yang tepat, Anda harus menentukan kriteria kemenangan objektif. (Mengingat sifat tantangan ini, saya pikir itu harus menjadi "kontes popularitas", yaitu jumlah
upvote terbanyak
Contohnya sepertinya tidak berhasil. Output sepenuhnya terdistorsi dan memiliki banyak break-up di dalamnya. Dikompilasi dalam MinGW dengan "gcc -o piano.exe piano.c" dan dieksekusi dengan "piano.exe> ​​piano.wav". Bahkan menggunakan fungsi nada sederhana 440 Hz g memiliki hasil yang sama. BTW, Anda dapat menggunakan M_PI sebagai pengganti angka besar Anda. Ini didefinisikan dalam math.h.
Mike C
@ Mike C, Awal output C boilerplate dengan uncommented qakan terlihat seperti ini: pastebin.com/ZCB1v7QQ . Apakah tuan rumah Anda big-endian?
Vi.
Tidak, saya menggunakan MinGW jadi saya x86. Saya akan mencobanya di salah satu kotak Linux saya. Saya tidak mengerti mengapa saya mengalami masalah. Aneh.
Mike C
apakah $><<7.chrdalam Ruby dihitung? : P untuk 9 karakter! atau $><<?\auntuk 7 karakter
Gagang Pintu

Jawaban:

2

Jawa

Pelat ketel saya memainkan suara. Saya bisa bermain golf g()sedikit lebih, tetapi saat ini di 273 karakter yang jauh di bawah 1500. Saya awalnya menulis ini untuk 16kHz untuk permainan 4kB dan harus sedikit mengubah konstanta untuk mendapatkan kualitas tonal yang tepat pada pemutaran 44.1kHz, tapi saya Saya cukup senang dengannya.

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

Bacaan lebih lanjut: Sintesis Karplus-Strong .

Peter Taylor
sumber
Untuk memulai tanpa PulseAudio saya menggunakan ini: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.
Dengan asumsi Anda menginginkan beberapa perkusi, tetapi tidak yakin yang mana yang tepat. Kedengarannya agak terlalu "elektronik".
Vi.
@ Vi., Saya akan meninggalkan beberapa saat bagi orang lain untuk mengatakan instrumen mana yang mereka pikir saya bidik sebelum saya mengungkapnya.
Peter Taylor
Karena orang sudah punya beberapa hari untuk menebak, saya akan menumpahkan kacang. Instrumen yang dimaksud adalah jerat.
Peter Taylor
Bisakah Anda memberikan tautan ke sampel rekaman aktual untuk dibandingkan?
Vi.
2

C

Inilah g()fungsinya, tanpa boilerplate.

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

Eksperimen yang menarik adalah bermain dengan loop pertama yang menginisialisasi urutan awal dari nilai acak. Mengganti panggilan rand()dengan i*imengubah karakter suara dengan cara yang masuk akal (artinya, sintesisnya meniru anggota yang berbeda dari keluarga instrumen yang sama). i*i*idan i*i*i*imemberikan kualitas suara lainnya, meskipun masing-masing lebih dekat dengan terdengar seperti rand(). Nilai suka i*327584atau i*571, di sisi lain, terdengar sangat berbeda (dan kurang seperti tiruan dari sesuatu yang nyata).


Variasi kecil lain dari fungsi yang sama bahkan datang lebih dekat ke instrumen lain, atau setidaknya ada di telinga saya.

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

Diedit untuk Menambahkan: Saya belum memperlakukan ini sebagai pertanyaan kode golf, karena tidak ditandai seperti itu (di luar batas 1500-char), tetapi karena sudah dimunculkan dalam komentar, inilah versi golf dari yang di atas ( 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();}

(Saya dapat menurunkannya di bawah 80 karakter jika saya dapat mengubah antarmuka fungsi untuk menggunakan variabel global.)

kotak roti
sumber
Karplus-string yang kuat. Kedengarannya seperti tali baja bagi saya.
Peter Taylor
@ PeterTaylor pikiranku persis. Varian bawah, di sisi lain, terdengar bagiku persis seperti tali usus (atau nilon) harpsichord. Hanya perlu duri dari pena bulu yang kembali setelah itu untuk menyelesaikan ilusi.
kotak roti
Setelah menghapus spasi dan shortening array, length, voiddan signeddalam kode kedua saya mendapat skor: 113 bytes. Usaha yang sangat bagus. Dan suaranya agak bagus.
Vi.