Только одна нота - синтезирующий музыкальный инструмент [закрыто]


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

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
Чтобы быть подходящим испытанием для Codegolf, вы должны определить объективный критерий выигрыша. (Учитывая природу этого вызова, я думаю, что это будет «конкурс популярности», то есть большинство голосов.)
хлебница»

Пример, похоже, не работает. Вывод полностью искажен и имеет много разрывов. Скомпилировано в MinGW с помощью «gcc -o piano.exe piano.c» и выполнено с помощью «piano.exe> ​​piano.wav». Даже при использовании простой функции тона g 440 Гц результат тот же. Кстати, вы можете использовать M_PI вместо ваших огромных чисел. Это определено в математике.
Майк C

@Mike C, начало вывода шаблона C с комментариями qдолжно выглядеть следующим образом: pastebin.com/ZCB1v7QQ . Является ли ваш хост big-endian?
Ви.

Нет, я использую MinGW, поэтому я x86. Я попробую это на одном из моих Linux-боксов. Я не понимаю, почему у меня проблемы, хотя. Странный.
Mike C

делает $><<7.chrв счете Руби? : P для 9 символов! или $><<?\aза 7 символов
дверная ручка

Ответы:


2

Ява

Мой шаблон играет звук. Я мог бы играть 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.