只是一个音符-乐器合成[关闭]


11

声明

任务是使用(您选择的)通用编程语言的功能来合成(您选择的)某种乐器的声音(弹奏一个音符)。

有两个目标:

  • 产生的声音的质量。它应尽可能类似于真实乐器;
  • 极简主义。建议将代码保持在1500字节以下(如果仅产生基本声音,则应减少)。

只需要提供生成功能,不计入样板。

不幸的是,无法为声音保真度计算分数,因此没有严格的规则。

规则:

  • 不依赖样本库,不需要专门的音乐生成工具;
  • 禁止从网络下载或尝试使用麦克风或声卡的MIDI或类似外部的东西;
  • 代码大小的度量单位是字节。可以在当前目录中创建文件。可能存在预先存在的文件(系数表等),但其内容将添加到乐谱中,并且必须按名称将其打开。
  • 样板代码(不计分)接收有符号整数的数组(列表),仅处理输出它们。
  • 输出格式是带符号的小尾数16位字,每秒44100个样本,并带有可选的WAV标头。没有尝试输出压缩音频而不是普通的波形;
  • 请选择用于合成的其他乐器(或该乐器的其他质量与代码大小类别);但最初不要告诉您要模拟什么-让其他用户猜测评论;
  • 不鼓励使用电子仪器;
  • 鼓是一种乐器。人的声音是一种乐器。

样板

这是某些语言的样板。您也可以为您的语言编写类似的样板。注释掉的“ g”功能仅用于演示(1秒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

这是仿照钢琴声音的非高尔夫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
要成为合适的代码高尔夫挑战者,您需要定义一个客观的获胜标准。(鉴于此挑战的性质,我认为这必须是“人气竞赛”,即最多的投票数。)
面包盒

该示例似乎不起作用。输出完全失真,并且其中有很多损坏。在MinGW中使用“ gcc -o piano.exe piano.c”进行编译,并通过“ piano.exe> piano.wav”执行。即使使用简单的440 Hz音调g功能也具有相同的结果。顺便说一句,您可以使用M_PI代替庞大的数字。它在math.h中定义。
Mike C

@Mike C,不带注释的C样板输出的开始q应如下所示:pastebin.com/ZCB1v7QQ。您的房东是大端吗?
六。

不,我正在使用MinGW,所以我是x86。我将在我的Linux机器上尝试一下。我不明白为什么我有问题。奇怪。
Mike C

确实$><<7.chr在Ruby中计?:P为9个字符!或$><<?\a7个字符
门把手

Answers:


2

爪哇

我的样板播放声音。我可以打g()更多的高尔夫球,但是目前是273个字符,远低于1500个字符。我最初是为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合成


要在没有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。,我会花一些时间让其他人在我发布它之前说出他们认为我要瞄准的乐器。
彼得·泰勒

由于人们有几天的猜测时间,所以我要撒些豆子。预期的乐器是一个军鼓。
彼得·泰勒

您可以链接到实际记录的样本进行比较吗?
六。

2

C

这是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*ii*i*i*i赋予其他音质,尽管每个音质都更接近rand()。另一方面,诸如i*327584or 的值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强串。对我来说听起来像是一根钢丝。
彼得·泰勒

@PeterTaylor我的想法正好。另一方面,底部变种对我来说听起来就像大键琴的肠线(或尼龙线)。它只需要随后返回的羽毛笔的撞击声即可完成幻觉。
面包箱

删除空格和缩短后arraylengthvoidsigned在第二个代码我得到的分数:113个字节。非常好尝试。而且声音还不错。
六。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.