Лише одна нота - синтезує музичний інструмент [закрито]


11

Заява

Завдання полягає в синтезі звуку (одна нота) на якомусь музичному інструменті (на ваш вибір), використовуючи функцію в якійсь загальній мові програмування (на ваш вибір).

Є дві цілі:

  • Якість звуку, що виходить. Він повинен максимально нагадувати справжній інструмент;
  • Мінімальність. Зберігання коду під 1500 байтів рекомендується (менше, якщо є лише основне створення звуку).

Потрібно забезпечити лише функцію генерації, котловарна плита не враховується для оцінки.

На жаль, жодна оцінка не може бути обчислена для достовірності звуку, тому не може бути жорстких правил.

Правила:

  • Відсутність залежності від бібліотек зразків, спеціалізованих речей для створення музики;
  • Немає завантаження з мережі або намагання використовувати MIDI мікрофона або аудіокарти чи щось подібне зовнішнє;
  • Одиниця вимірювання розміру коду - байти. Файл можна створити в поточному каталозі. Попередньо існуючі файли (таблиці коефіцієнтів тощо) можуть існувати, але їх вміст додається до оцінки + вони повинні відкриватися по імені.
  • Код котла (не зараховується до балу) отримує масив (список) підписаних цілих чисел і займається лише їх виведенням.
  • Формат виводу підписаний 16-бітовими словами з невеликим ендіанітом, 44100 зразків на секунду, з додатковим заголовком WAV Немає спроб виводити стиснене аудіо замість простого wav;
  • Будь ласка, вибирайте різні інструменти для синтезу (або іншу категорію якості та розміру коду для інструменту); але спочатку не кажіть, що ви імітуєте - нехай інші користувачі здогадуються у коментарях;
  • Електронні інструменти не перешкоджають;
  • Барабан - це інструмент. Людський голос - це інструмент.

Котли

Ось котли для деяких мов. Ви можете написати подібну плиту для котла і для вашої мови. Коментована функція "g" призначена лише для демонстрації (1-секундний синусоїд 440 Гц).

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

Пітон 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
Щоб бути відповідним завданням кодогольфа, вам потрібно визначити об'єктивний критерій виграшу. (З огляду на характер цієї проблеми, я думаю , що це має бути «популярність конкурсу», тобто найбільшу кількість upvotes.)
Breadbox

Приклад, здається, не працює. Вихід повністю спотворений і в ньому багато розривів. Складено в MinGW з "gcc -o piano.exe piano.c" і виконується з "piano.exe> ​​piano.wav". Навіть використання простої функції 440 Гц тону g має той же результат. До речі, ви можете використовувати M_PI замість величезної кількості. Це визначено в математиці.
Майк C

@Mike C, початок виходу котла C із некомментованим qповинен виглядати так: pastebin.com/ZCB1v7QQ . Ваш господар великий ендіанець?
Ві.

Ні, я використовую MinGW, тому я x86. Я спробую це на одному з моїх скриньок Linux. Я не розумію, чому у мене проблеми. Дивно.
Майк C

робить $><<7.chrв рахунку Рубі? : P на 9 символів! або $><<?\aна 7 годин
Doorknob

Відповіді:


2

Java

Мій котел відтворює звук. Я міг g()би трохи більше пограти в гольф , але в даний час це 273 символів, що значно менше 1500. Спочатку я писав це на 16 кГц для гри в 4 кБ і мені довелося трохи налаштувати константи, щоб отримати правильні тональні якості при відтворенні 44,1 кГц, але я Я задоволений цим.

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

Подальше читання: синтез Карплуса-Сильного .


Для початку без 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

С

Ось 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 символів, якби я міг змінити інтерфейс функції для використання глобальних змінних.)


Карплус-Сильна струна. Мені звучить як сталева струна.
Пітер Тейлор

@PeterTaylor моя думка точно. Нижній варіант, з іншого боку, звучить мені точно так само, як кишкова (або нейлонова) струна клавесина. Для того, щоб завершити ілюзію, вона просто потребує грона, що повертається згодом.
хлібниця

Після видалення пробілів і укорочення array, length, voidі signedв другому коді я отримав рахунок: 113 байт. Дуже приємна спроба. І звук досить хороший.
Ві.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.