แค่โน้ตเดียว - เครื่องดนตรีสังเคราะห์ [ปิด]


11

คำให้การ

งานคือการสังเคราะห์เสียง (เล่นโน้ตหนึ่ง) ของเครื่องดนตรีบางอย่าง (ที่คุณเลือก) โดยใช้ฟังก์ชั่นในภาษาการเขียนโปรแกรมวัตถุประสงค์ทั่วไปบางอย่าง (ที่คุณเลือก)

มีสองเป้าหมาย:

  • คุณภาพของเสียงที่ได้ มันควรจะคล้ายกับเครื่องดนตรีจริงอย่างดีที่สุด
  • minimality แนะนำให้ใช้โค้ดที่มีขนาดต่ำกว่า 1500 ไบต์ (น้อยกว่าหากมีการสร้างเสียงแบบพื้นฐานเท่านั้น)

จำเป็นต้องจัดหาฟังก์ชั่นการสร้างเท่านั้นไม่มีการนับคะแนนสำหรับสำเร็จรูป

น่าเสียดายที่ไม่มีการคำนวณคะแนนสำหรับความเที่ยงตรงของเสียงดังนั้นจึงไม่มีกฎที่เข้มงวด

กฎ:

  • ไม่มีการพึ่งพาห้องสมุดตัวอย่างสิ่งสร้างเพลงเฉพาะ
  • ไม่มีการดาวน์โหลดจากเครือข่ายหรือพยายามใช้ไมโครโฟนหรือ MIDI ของการ์ดเสียงหรือบางสิ่งภายนอกเกินไปเช่นนี้
  • หน่วยวัดขนาดรหัสคือไบต์ ไฟล์สามารถสร้างได้ในไดเรกทอรีปัจจุบัน ไฟล์ที่มีอยู่แล้ว (ตารางค่าสัมประสิทธิ์ ฯลฯ ) อาจมีอยู่ แต่เนื้อหาของไฟล์เหล่านั้นจะถูกเพิ่มไปยังคะแนน + โดยจะต้องเปิดด้วยชื่อ
  • รหัสสำเร็จรูป (ไม่นับคะแนน) จะได้รับอาร์เรย์ (รายการ) ของจำนวนเต็มที่ลงนามและเกี่ยวข้องกับการส่งออกเท่านั้น
  • รูปแบบเอาต์พุตมีการลงนามคำ 16 บิตแบบ endian น้อยตัวอย่าง 44100 ตัวอย่างต่อวินาทีพร้อมส่วนหัว WAV เสริม ไม่พยายามเอาท์พุทเสียงที่ถูกบีบอัดแทนที่จะเป็น wav ธรรมดา;
  • โปรดเลือกเครื่องมือที่แตกต่างกันสำหรับการสังเคราะห์ (หรือหมวดหมู่ขนาดอื่น ๆ เทียบกับขนาดรหัสสำหรับเครื่องมือ) แต่อย่าบอกในตอนแรกว่าคุณกำลังจำลองอะไร - ให้ผู้ใช้คนอื่นเดาความเห็น
  • เครื่องมืออิเล็กทรอนิกส์ไม่สนับสนุน;
  • กลองเป็นเครื่องดนตรี เสียงของมนุษย์เป็นเครื่องมือ

Boilerplates

นี่คือเทมเพลตสำหรับบางภาษา คุณสามารถเขียนแผ่นบอยเลอร์ที่คล้ายกันสำหรับภาษาของคุณได้เช่นกัน ฟังก์ชั่น "g" แสดงความคิดเห็นเป็นเพียงการสาธิต (1 วินาที 440 Hz sine tone)

ค:

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

มันให้คะแนนประมาณ 1,330 ไบต์และให้คุณภาพต่ำ / ปานกลาง


2
ในการเป็นผู้ท้าทาย codegolf ที่เหมาะสมคุณจะต้องกำหนดเกณฑ์การชนะอย่างมีวัตถุประสงค์ (ด้วยลักษณะของความท้าทายนี้ฉันคิดว่ามันจะต้องเป็น "การประกวดความนิยม" เช่นจำนวน upvotes มากที่สุด)
breadbox

ตัวอย่างไม่ทำงาน เอาต์พุตมีการผิดเพี้ยนอย่างสมบูรณ์และมีการหยุดพักหลายครั้ง รวบรวมใน MinGW ด้วย "gcc -o piano.exe piano.c" และดำเนินการด้วย "piano.exe> ​​piano.wav" แม้แต่การใช้งานฟังก์ชั่น g 440 tone เฮิร์ตซ์ก็ง่ายเหมือนกัน BTW คุณสามารถใช้ M_PI แทนตัวเลขจำนวนมากของคุณ มันกำหนดไว้ในวิชาคณิตศาสตร์
Mike C

@ ไมค์ C, จุดเริ่มต้นของการส่งออกของ C สำเร็จรูปกับ uncommented qควรมีลักษณะเช่นนี้pastebin.com/ZCB1v7QQ เจ้าบ้านใหญ่ของคุณหรือไม่
วิ

ไม่ฉันใช้ MinGW ดังนั้นฉันจึง x86 ฉันจะลองในกล่อง Linux ของฉัน ฉันไม่เข้าใจว่าทำไมฉันถึงมีปัญหา แปลก.
Mike C

ไม่$><<7.chrนับทับทิม? : P สำหรับ 9 ตัวอักษร! หรือ$><<?\a7 ตัวอักษร
Doorknob

คำตอบ:


2

ชวา

สำเร็จรูปของฉันเล่นเสียง ฉันเล่นกอล์ฟได้g()มากกว่านี้เล็กน้อย แต่ปัจจุบันอยู่ที่ 273 chars ซึ่งต่ำกว่า 1500 ฉันเคยเขียนสิ่งนี้ไว้ที่ 16kHz สำหรับเกม 4kB และต้องปรับค่าคงที่เล็กน้อยเพื่อให้ได้คุณภาพเสียงที่ถูกต้องที่การเล่น 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 แข็งแกร่ง


ในการเริ่มต้นโดยไม่ต้อง 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. ฉันจะปล่อยให้คนอื่นพูดว่าเครื่องมือที่พวกเขาคิดว่าฉันตั้งเป้าหมายไว้ก่อนที่จะเปิดเผย
Peter Taylor

เนื่องจากผู้คนมีเวลาสองสามวันในการเดาฉันจะทำถั่วหก เครื่องมือที่ตั้งใจไว้คือบ่วง
Peter Taylor

คุณสามารถให้ลิงค์ไปยังตัวอย่างจริงที่บันทึกไว้เพื่อเปรียบเทียบได้หรือไม่
วิ

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

แก้ไขเพื่อเพิ่ม: ฉันไม่ได้รับการปฏิบัติเช่นนี้เป็นคำถามรหัสกอล์ฟเนื่องจากมันไม่ได้ถูกตั้งค่าสถานะเช่นนี้ (เกินขีด จำกัด 1,500-char) แต่เนื่องจากถูกนำขึ้นมาในความคิดเห็นนี่เป็นเวอร์ชั่นด้านบนของ golfed ( 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 ฟังดูเหมือนสายเหล็กสำหรับฉัน
Peter Taylor

@ PeterTaylor ความคิดของฉันอย่างแน่นอน ตัวแปรด้านล่างในทางกลับกันฟังฉันเหมือนกับสตริงไส้ (หรือไนลอน) ของฮาร์ปซิคอร์ด มันแค่ต้องการปากกาขนนกที่กลับมาหลังจากนั้นจึงทำให้ภาพลวงตาสมบูรณ์
breadbox

หลังจากลบช่องว่างและการตัดทอนarray, length, voidและsignedในรหัสที่สองผมได้คะแนน: 113 ไบต์ ลองดีมาก และเสียงค่อนข้างดี
วิ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.