단 하나의 음표-악기 합성 [닫힘]


11

성명서

이 작업은 일부 범용 프로그래밍 언어 (선택한 기능)를 사용하여 선택한 악기의 사운드 (한 음표 연주)를 합성하는 것입니다.

두 가지 목표가 있습니다.

  • 결과 사운드의 품질. 실제 악기와 최대한 비슷해야합니다.
  • 최소한. 코드를 1500 바이트 이하로 유지하는 것이 좋습니다 (기본 사운드 생성 만있는 경우는 적음).

생성 기능 만 제공하면되며, 상용구는 점수에 포함되지 않습니다.

불행히도 사운드 충실도에 대해서는 점수를 계산할 수 없으므로 엄격한 규칙을 적용 할 수 없습니다.

규칙 :

  • 샘플 라이브러리, 전문 음악 생성에 의존하지 않습니다.
  • 네트워크에서 다운로드하거나 마이크 또는 오디오 카드의 MIDI 또는 이와 유사한 외부 장치를 사용하려고 시도하지 않습니다.
  • 코드 크기 측정 단위는 바이트입니다. 파일은 현재 디렉토리에 생성 될 수 있습니다. 기존 파일 (계수 테이블 등)이 존재할 수 있지만 해당 내용은 점수에 추가 + 이름으로 열어야합니다.
  • 상용구 코드 (점수로 계산되지 않음)는 부호있는 정수의 배열 (목록)을 수신하고 출력 만 처리합니다.
  • 출력 형식은 옵션 WAV 헤더를 사용하여 리틀 엔디안 16 비트 워드 (초당 44100 샘플)로 서명됩니다. 일반 wav 대신 압축 된 오디오를 출력하려고하지 않습니다.
  • 합성을 위해 다른 기기를 선택하십시오 (또는 기기의 다른 품질 대 코드 크기 범주). 그러나 처음에 당신이 무엇을 시뮬레이션하고 있는지 말하지 말고 다른 사용자가 주석으로 추측하도록하십시오.
  • 전자 기기는 권장하지 않습니다.
  • 드럼은 악기입니다. 인간의 목소리는 악기입니다.

보일러 플레이트

다음은 일부 언어의 상용구입니다. 귀하의 언어와 비슷한 보일러 플레이트를 작성할 수 있습니다. "g"기능은 데모 용입니다 (1 초 440Hz 사인 톤).

씨:

//#!/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;
}

파이썬 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);

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

하스켈 :

#!/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

피아노 사운드를 모델링 한 ungolfed C 버전은 다음과 같습니다.

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

약 1330 바이트를 기록하며 열악한 / 약용 품질을 제공합니다.


2
적절한 코드 골프 도전이 되려면 객관적인 승리 기준을 정의해야합니다. (이 도전의 본질을 감안할 때, 나는 그것이 "인기 공모전", 즉 대부분의
공감대

예제가 작동하지 않는 것 같습니다. 출력이 완전히 왜곡되고 많은 분리가 발생합니다. "gcc -o piano.exe piano.c"로 MinGW에서 컴파일되고 "piano.exe> ​​piano.wav"로 실행되었습니다. 간단한 440Hz 톤 g 기능을 사용해도 결과는 같습니다. BTW, 거대한 숫자 대신 M_PI를 사용할 수 있습니다. math.h에 정의되어 있습니다.
Mike C

@Mike C, 주석 처리되지 않은 C 상용구의 출력 시작은 pastebin.com/ZCB1v7QQq 와 같습니다 . 호스트는 엔디안입니까?
Vi.

아니요, MinGW를 사용하고 있으므로 x86입니다. 나는 리눅스 박스 중 하나에서 시도 할 것이다. 그래도 왜 문제가 있는지 이해할 수 없습니다. 이상한.
Mike C

수행 $><<7.chr루비의 수에? : 9 자 P! 또는 $><<?\a7 개의 문자
Doorknob

답변:


2

자바

내 상용구에서 소리가납니다. g()좀 더 골프를 칠 수 는 있지만 현재 1500 자 이하인 273 자입니다. 나는 원래 4kB 게임을 위해 16kHz를 위해 이것을 썼고 44.1kHz 재생에서 올바른 음질을 얻기 위해 상수를 약간 조정해야했습니다. 합리적으로 만족합니다.

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

추가 자료 : Karplus-Strong synthesis .


PulseAudio없이 시작하려면 다음을 사용하십시오.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.

당신이 타악기를 원한다고 가정했지만 정확히 어느 것을 확신하지 못합니다. 너무 "전자적"인 것 같습니다.
Vi.

@Vi., 나는 다른 사람들이 내가 발표하기 전에 내가 어떤 악기를 목표로하고 있다고 생각하는지 말할 때까지 잠시만 두겠습니다.
피터 테일러

사람들은 추측하기 며칠을 보냈으므로 콩을 쏟을 것입니다. 의도 된 도구는 올가미입니다.
Peter Taylor

실제 기록 된 샘플에 대한 링크를 제공 할 수 있습니까?
Vi.

2

g()상용구가없는 기능 은 다음과 같습니다 .

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

흥미로운 실험은 임의의 값의 시작 순서를 초기화하는 첫 번째 루프를 사용하는 것입니다. 호출을 rand()i*i바꾸면 사운드의 특성이 그럴듯하게 변경됩니다 (즉, 합성이 동일한 악기 제품군의 다른 멤버를 모방하는 것처럼 들립니다). i*i*i그리고 i*i*i*i각자가 가까이 같은 소리를 얻을 수 있지만, 다른 사운드 품질을 제공합니다 rand(). 같은 값 i*327584이나 i*571, 다른 한편으로는, 매우 다른 (덜 뭔가 현실의 모방처럼) 소리가 난다.


동일한 기능의 또 다른 사소한 변형은 다른 악기에 더 가까워 지거나 적어도 내 귀에 가깝습니다.

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

추가 편집 : 이 코드는 1500 골프 문자를 넘어서 표시되지 않았기 때문에 코드 골프 질문으로 취급하지 않았지만 주석에 표시되었으므로 위의 골프 버전이 있습니다 ( 96 자) :

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

(글로벌 변수를 사용하도록 함수 인터페이스를 변경할 수 있다면 80 자 미만으로 줄 수 있습니다.)


Karplus-Strong 문자열. 나에게 강철 끈처럼 들린다.
피터 테일러

@PeterTaylor 내 생각은 정확히. 반면에 바닥 변형은 하프시 코드의 내장 (나일론) 끈과 정확히 같습니다. 환상을 완성하기 위해 돌아 오는 퀼의 썽크가 필요합니다.
breadbox

공백 및 단축을 제거한 후 array, length, voidsigned113 바이트 : 두 번째 코드에서 나는 점수를 얻었다. 아주 좋은 시도입니다. 그리고 소리는 다소 좋습니다.
Vi.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.