Форум программистов, компьютерный форум, киберфорум
diadiavova
Войти
Регистрация
Восстановить пароль
Рейтинг: 5.00. Голосов: 2.

MIDI музыка на C#. Часть 1-я.

Запись от diadiavova размещена 02.10.2016 в 17:59
Обновил(-а) diadiavova 06.06.2018 в 19:32
Метки c#, midi, music, winmm

  1. Воспроизводим ноту
  2. Сыграем мелодию
  3. Воспользуемся старыми наработками
Разбираясь в вопросах темы "Мелодии на бипере", решил не останавливаться на достигнутом и пошел дальше. Наиболее естественным способом что-то сыграть на компьютере является использование MIDI. Естественно, для разработки профессионального музыкального софта пришлось бы изучать все спецификации и разбираться во множестве технических моментов. Но для того, чтобы написать что-то небольшое и несложное, достаточно иметь базовые представления о том, как это все работает. Вот в этом мы сейчас и будем разбираться.

Воспроизводим ноту

В деталях этот вопрос описан в статье Making Music with MIDI and C#, так что пересказывать подробно здесь все не буду, опишу тезисно только то, что понадобится для дальнейшего развития темы.

Для реализации задуманного нам понадобится три WinAPI функции
C#
1
using System.Runtime.InteropServices;
C#
1
2
3
4
5
6
7
8
9
10
        [DllImport("winmm.dll")]
        private static extern int midiOutOpen(ref int handle,
   int deviceID, MidiCallBack proc, int instance, int flags);
 
        [DllImport("winmm.dll")]
        protected static extern int midiOutShortMsg(int handle,
           int message);
 
        [DllImport("winmm.dll")]
        protected static extern int midiOutClose(int handle);
С помощью первой мы будем открывать MIDI-устройство и получать его дескриптор (hadle), с помощью последнего - закрывать, а функция midiOutShortMsg - это как раз и есть основная функция с помощью которой и будет воспроизводиться музыка.
Простейший код, который позволит нам воспроизвести ноту до первой октавы будет выглядеть так
C#
1
2
3
int handle = 0; // Переменная для хранения дескриптора устройства
res = midiOutOpen(ref handle, 0, null, 0, 0); // Открываем устройство  и получаем его дескриптор
res = midiOutShortMsg(handle, 0x007F3C90); // Воспроизводим ноту
Функция midiOutShortMsg может выполнять огромное количество всевозможных операций и что именно будет делать эта функция - зависит от значения второго аргумента. В общем и целом она посылает устройству с дескриптором, передаваемым первым аргументом сообщение - второй аргумент.

Теперь немного о сообщении. Хоть оно и имеет тип int, тем не менее оно состоит из трех частей, по одному байту каждая.
  1. Первый байт (читаем справа-налево, то есть в сообщении 0x007F3C90 это будут шестнадцатиричные цифры 90) - это так называемый статус-байт, он содержит команду и номер канала. В данном случае команда - 9 означает взятие ноты NoteOn. Она будет воспроизведена на первом канале. Если надо воспроизвести ноту на втором канале мы будем использовать команду 91 и так далее, всего шестнадцать каналов.
  2. Второй байт( в данном случае 3C) - собственно нота, которую мы хотим воспроизвести. Все ноты пронумерованы от 0 до 127. Таблица нот есть в статье, упоминавшейся вначале.
  3. Ну и третий байт - сила с которой взята нота. Обычно этот параметр влияет на громкость, но если речь идет, например, о фортепианной ноте, то это будет сила удара и т.д. (в конечном итоге все равно будет громче). Эта величина также может принимать значения от 0 до 127.

Первый байт сообщения это всегда команда, а вот следующие два - параметры команды и они могут быть, а может их и не быть, или, например может быть только один параметр. Зависит от команды. Например команда, устанавливающая инструмент для канала имеет только один параметр - собственно номер инструмента.

Сыграем мелодию

Казалось бы, вот теперь выдавай себе команды на взятие клавиш и получай мелодию, как это было на бипере. Единственное, что непонятно, так это как долго будет звучать взятая нота, прежде чем будет взята следующая. Но на самом деле, если мы захотим проиграть последовательно ноты до, ми и соль первой октавы (60, 64 и 67 соответственно) и для этого выполним вот такой код
C#
1
2
3
4
5
int handle = 0; 
res = midiOutOpen(ref handle, 0, null, 0, 0);
midiOutShortMsg(handle, 127 << 16 | 60 << 8 | 0x00000090);
midiOutShortMsg(handle, 127 << 16 | 64 << 8 | 0x00000090);
midiOutShortMsg(handle, 127 << 16 | 67 << 8 | 0x00000090);
То эти ноты воспроизведутся одновременно и мы услышим аккорд до-мажор. Продолжительность же звучания будет сколь угодно долгой. Хотя на некоторых инструментах звучание скоро прекратится, но это связано с тем, что они имитируют инструменты с угасающими колебаниями. Например гитарный или фортепианный звуки со временем угасают из-за того, что струны, которые их воспроизводят просто перестают колебаться. А вот если выбрать инструмент типа органа, то взятая нота будет звучать пока ее эту вакханалию не прекратишь тем или иным способом.

Для того, чтобы понять как именно работает MIDI-устройство, можно представить себе синтезатор, на котором есть куча клавиш, всевозможных функциональных кнопок, переключателей, ручек, слайдеров. Также к нему может быть подключена педаль и прочие прибамбасы. Для того, чтобы зазвучала музыка, человеку приходится нажимать клавиши, что-то переключать, жать на педаль и так далее. Кроме того, все это делается во времени, и каждое действие должно быть выполнено в определенный момент. Так вот MIDI-устройство - это то же самое, только вместо всех этих нажатий, отпусканий поворотов и т. д. просто отправляются сообщения. Нажал клавишу - отправилось сообщение о нажатии клавиши, отпустил клавишу - сообщение об отпускании клавиши, сменил тембр... дальше ясно.

Таким образом, если мы хотим чтобы ноты из примера сверху звучали по полсекунды каждая и сменяли друг друга, то нам надо отправлять сообщения не только о нажатии клавиш, но об их отпускании, причем выдерживая интервалы между этим операциями, чтобы дать нотам позвучать.

Команда NoteOff задается как 8x, в отличие от 9x для NoteOn. Здесь также вместо x нужно указать канал на котором надо прекратить звучание ноты, то есть для первого канала это будет 80. И таким образом код, проигрывающий три ноты подряд будет выглядеть так.
C#
1
2
3
4
5
6
7
8
9
10
11
midiOutShortMsg(handle, 127 << 16 | 60 << 8 | 0x00000090);
Thread.Sleep(500);
midiOutShortMsg(handle,  60 << 8 | 0x00000080);
midiOutShortMsg(handle, 127 << 16 | 64 << 8 | 0x00000090);
Thread.Sleep(500);
midiOutShortMsg(handle,  64 << 8 | 0x00000080);
midiOutShortMsg(handle, 127 << 16 | 67 << 8 | 0x00000090);
Thread.Sleep(500);
midiOutShortMsg(handle,  67 << 8 | 0x00000080);
Thread.Sleep(500);
midiOutClose(handle);
В конце я закрыл устройство. Это необходимо делать, поскольку, если оно остается открытым, то открыть его снова нельзя и надо будет либо использовать ранее полученный дескриптор, либо устройство будет закрыто только вместе приложением, открывшим его. В предыдущих примерах я этого не делал из-за того, что, закрыв устройство сразу после открытия, мы бы ничего не услышали. Здесь же выдержаны паузы и устройство закрывается своевременно.

Воспользуемся старыми наработками

Коль скоро мы выяснили, как исполнять мелодии, было бы неплохо иметь возможность воспроизвести все, что было сделано в предыдущей статье, где речь шла о мелодиях для бипера. В конце концов мелодии подобраны, известны частоты звуков и длительности звуков и пауз, что еще нужно-то?

Создадим небольшой плеер для воспроизведения мелодий.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System.Runtime.InteropServices;
using System.Threading;
 
namespace midi
{
    class MelodyPlayer
    {
 
        [DllImport("winmm.dll")]
        private static extern int midiOutOpen(ref int handle,
   int deviceID, MidiCallBack proc, int instance, int flags);
 
        [DllImport("winmm.dll")]
        protected static extern int midiOutShortMsg(int handle,
           int message);
 
        [DllImport("winmm.dll")]
        protected static extern int midiOutClose(int handle);
 
        private delegate void MidiCallBack(int handle, int msg,
   int instance, int param1, int param2);
 
 
        int handle = 0;
        public void Open()
        {
            var res = midiOutOpen(ref handle, 0, null, 0, 0);
        }
 
        public void Close()
        {
            var res = midiOutClose(handle);
        }
 
        public void SetInstrument(int id)
        {
            var res = midiOutShortMsg(handle, id << 8 | 0x000000C0);
        }
 
        public void PlayNote(int note, int duration)
        {
            var res = midiOutShortMsg(handle, 127 << 16 | note << 8 | 0x00000090);
            Thread.Sleep(duration);
            res = midiOutShortMsg(handle, 127 << 16 | note << 8 | 0x00000080);
        }
    }
}
Здесь у нас есть метод Open, открывающий устройство 0. Метод Close, закрывающий открытое устройство. Метод SetInstrument устанавливает интсрумент для первого канала. Здесь мы используем еще одну команду Cx. Опять-таки вместо x используется номер канала, а второй байт представляет из себя номер инструмента, из списка, приведенного здесь.
GM 1 Sound Set
Для того, чтобы было удобнее пользоваться этим списком, лучше оформить его в виде перечисления. Я воспользовался расширением CustomButtons для Firefox и создал кнопку со следующим кодом
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var headerRow = Array.prototype.filter.call( content.document.querySelectorAll("tr"), function(tr){return tr.textContent.search("Instrument Name") > -1;})[0];
var rows = headerRow.parentElement.querySelectorAll("tr");
var s = "public enum MusicalInstruments\r\n{\r\n";
for(var i = 3; i < rows.length; i++ )
{
  var row = rows[i];
  var tds = row.querySelectorAll("td");
  var name = tds[1].textContent.replace(/[\s\(\)\-\+]+/g, "_").replace(/_$/, "");
  var value = tds[0].textContent.substr(0, tds[0].textContent.length - 1);
  s += "    " + name + " = " + value + ",\r\n";
}
s += "}";
 
gClipboard.write(s);
Далее, открыл страницу и выполнил код кнопки. В результате в буфер обмена скопировался почти готовый код перечисления(там только запятую лишнюю в конце убрать надо).

Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    public enum MusicalInstruments
    {
        Acoustic_Grand_Piano = 1,
        Bright_Acoustic_Piano = 2,
        Electric_Grand_Piano = 3,
        Honky_tonk_Piano = 4,
        Electric_Piano_1 = 5,
        Electric_Piano_2 = 6,
        Harpsichord = 7,
        Clavi = 8,
        Celesta = 9,
        Glockenspiel = 10,
        Music_Box = 11,
        Vibraphone = 12,
        Marimba = 13,
        Xylophone = 14,
        Tubular_Bells = 15,
        Dulcimer = 16,
        Drawbar_Organ = 17,
        Percussive_Organ = 18,
        Rock_Organ = 19,
        Church_Organ = 20,
        Reed_Organ = 21,
        Accordion = 22,
        Harmonica = 23,
        Tango_Accordion = 24,
        Acoustic_Guitar_nylon = 25,
        Acoustic_Guitar_steel = 26,
        Electric_Guitar_jazz = 27,
        Electric_Guitar_clean = 28,
        Electric_Guitar_muted = 29,
        Overdriven_Guitar = 30,
        Distortion_Guitar = 31,
        Guitar_harmonics = 32,
        Acoustic_Bass = 33,
        Electric_Bass_finger = 34,
        Electric_Bass_pick = 35,
        Fretless_Bass = 36,
        Slap_Bass_1 = 37,
        Slap_Bass_2 = 38,
        Synth_Bass_1 = 39,
        Synth_Bass_2 = 40,
        Violin = 41,
        Viola = 42,
        Cello = 43,
        Contrabass = 44,
        Tremolo_Strings = 45,
        Pizzicato_Strings = 46,
        Orchestral_Harp = 47,
        Timpani = 48,
        String_Ensemble_1 = 49,
        String_Ensemble_2 = 50,
        SynthStrings_1 = 51,
        SynthStrings_2 = 52,
        Choir_Aahs = 53,
        Voice_Oohs = 54,
        Synth_Voice = 55,
        Orchestra_Hit = 56,
        Trumpet = 57,
        Trombone = 58,
        Tuba = 59,
        Muted_Trumpet = 60,
        French_Horn = 61,
        Brass_Section = 62,
        SynthBrass_1 = 63,
        SynthBrass_2 = 64,
        Soprano_Sax = 65,
        Alto_Sax = 66,
        Tenor_Sax = 67,
        Baritone_Sax = 68,
        Oboe = 69,
        English_Horn = 70,
        Bassoon = 71,
        Clarinet = 72,
        Piccolo = 73,
        Flute = 74,
        Recorder = 75,
        Pan_Flute = 76,
        Blown_Bottle = 77,
        Shakuhachi = 78,
        Whistle = 79,
        Ocarina = 80,
        Lead_1_square = 81,
        Lead_2_sawtooth = 82,
        Lead_3_calliope = 83,
        Lead_4_chiff = 84,
        Lead_5_charang = 85,
        Lead_6_voice = 86,
        Lead_7_fifths = 87,
        Lead_8_bass_lead = 88,
        Pad_1_new_age = 89,
        Pad_2_warm = 90,
        Pad_3_polysynth = 91,
        Pad_4_choir = 92,
        Pad_5_bowed = 93,
        Pad_6_metallic = 94,
        Pad_7_halo = 95,
        Pad_8_sweep = 96,
        FX_1_rain = 97,
        FX_2_soundtrack = 98,
        FX_3_crystal = 99,
        FX_4_atmosphere = 100,
        FX_5_brightness = 101,
        FX_6_goblins = 102,
        FX_7_echoes = 103,
        FX_8_sci_fi = 104,
        Sitar = 105,
        Banjo = 106,
        Shamisen = 107,
        Koto = 108,
        Kalimba = 109,
        Bag_pipe = 110,
        Fiddle = 111,
        Shanai = 112,
        Tinkle_Bell = 113,
        Agogo = 114,
        Steel_Drums = 115,
        Woodblock = 116,
        Taiko_Drum = 117,
        Melodic_Tom = 118,
        Synth_Drum = 119,
        Reverse_Cymbal = 120,
        Guitar_Fret_Noise = 121,
        Breath_Noise = 122,
        Seashore = 123,
        Bird_Tweet = 124,
        Telephone_Ring = 125,
        Helicopter = 126,
        Applause = 127,
        Gunshot = 128
    }

Имея такое перечисление можно перегрузить метод SetInstrument вот таким методом.
C#
1
2
3
4
       public void SetInstrument(MusicalInstruments id)
        {
            midiOutShortMsg(handle, (int)id << 8 | 0x000000C0);
        }
Пользоваться им намного удобнее.

Ну и наконец метод PlayNote я сделал похожим на метод Console.Beep, за исключением того, что первый аргумент - это не частота, а номер ноты. Таким образом примеры из предыдущей статьи сначала придется обработать, вычислив номер ноты по ее частоте. Вычисление это несложное, метод выглядит так
C#
1
2
3
4
        int NoteNumberFromFriquency(double friquency)
        {
            return (int)Math.Round(69.0 + 12.0 * (Math.Log(friquency / 440.0) / Math.Log(2.0)));
        }
Теперь для того, чтобы преобразовать строку с числами для бипера, в которой у нас частоты чередуются с длительностями мы создадим следующий метод
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        int[] BeepToMidi(string beep)
        {
            var result = new List<int>();
            var b = beep.Split(" \t\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < b.Length; i += 2)
            {
                if (b[i] == "0")
                {
                    result.Add(-1);
                }
                else
                {
                    result.Add(NoteNumberFromFriquency(double.Parse(b[i])));
                }
                result.Add(int.Parse(b[i + 1]));
            }
            return result.ToArray();
        }
Он получает строку, а возвращает массив чисел, которые мы можем использовать. Вот так можно проиграть "Имперский марш" из статьи о бипере(инструмент, разумеется, можно выбрать и другой).
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
        void PlayImperialWithNotes()
        {
            var song = "440 500 440 500 440 500 349 350 523 150 440 500 349 350 523 150 440 1000 659 500 659 500 659 500 698 350 523 150 415 500 349 350 523 150 440 1000 880 500 440 350 440 150 880 500 830 250 784 250 740 125 698 125 740 250 0 250 455 250 622 500 587 250 554 250 523 125 466 125 523 250 0 250 349 125 415 500 349 375 440 125 523 500 440 375 523 125 659 1000 880 500 440 350 440 150 880 500 830 250 784 250 740 125 698 125 740 250 0 250 455 250 622 500 587 250 554 250 523 125 466 125 523 250 0 250 349 250 415 500 349 375 523 125 440 500 349 375 261 125 440 1000 0 100";
            var songInt = BeepToMidi(song);
            var player = new MelodyPlayer();
                player.Open();
                player.SetInstrument(44);
                for (int i = 0; i < songInt.Length; i += 2)
                {
                    if (songInt[i] == -1)
                    {
                        Thread.Sleep(songInt[i + 1]);
                    }
                    else
                    {
                        player.PlayNote(songInt[i], songInt[i + 1]);
                    }
                }
                player.Close();
        }
Формат, придуманный для "снятия" мелодий с нотного листа, тоже можно использовать, поскольку можно сначала пропустить его через парсер, а уже результат использовать в методах, описанных выше.
>>
Размещено в Без категории
Показов 13793 Комментарии 12
Всего комментариев 12
Комментарии
  1. Старый комментарий
    Аватар для XLAT
    оставлю здесь инфу, которая мне была нужна, но здесь не нашёл:
    C#
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
            ///--------------------------------------------------------------------|
            /// MCI
            /// Проигрывание MIDI(*.mid) на платформе VS C# (Windows)
            /// (код взят из класса формы Form1)
            ///--------------------------------------------------------------------:
     
            [DllImport("winmm.dll")]
            private static extern long mciSendString(
                string       strCommand, 
                StringBuilder strReturn, 
                int       iReturnLength, 
                IntPtr     hwndCallback
            );
     
            ////////////////////////////////////////////////////////////////////////
            private static string Alias = "";
            public static void playMidi(String fileName, String alias)
            {
                if (Alias.Count() != 0) stopMidi(Alias);
     
                Alias = alias;
                mciSendString("open " + fileName + " type sequencer alias " + alias,
                    new StringBuilder(), 0, new IntPtr());
                mciSendString("play " + alias, new StringBuilder(), 0, new IntPtr());
            }
     
            public static void stopMidi(String alias)
            {
                if (Alias.Count() == 0) return;
     
                mciSendString("stop "  + alias, null, 0, new IntPtr());
                mciSendString("close " + alias, null, 0, new IntPtr());
                Alias = "";
            }
            ////////////////////////////////////////////////////////////////////////
    Запись от XLAT размещена 15.10.2020 в 10:50 XLAT вне форума
  2. Старый комментарий
    Аватар для diadiavova
    Цитата:
    Сообщение от XLAT Просмотреть комментарий
    оставлю здесь инфу, которая мне была нужна, но здесь не нашёл:
    Когда все это писалось, планов было громадьё, но как-то интерес к теме быстро иссяк, так что хорошо, что хоть это успел сделать))
    Запись от diadiavova размещена 16.10.2020 в 12:49 diadiavova вне форума
  3. Старый комментарий
    MIDI музыка в Windows PowerShell ?
    Запись от politoto размещена 17.10.2020 в 11:17 politoto вне форума
  4. Старый комментарий
    Аватар для diadiavova
    Цитата:
    Сообщение от politoto Просмотреть комментарий
    MIDI музыка в Windows PowerShell ?
    Вопрос о возможности использования? Напрямую, как мне кажется, вряд ли, в силу того, что тут нужно юзать винапи. Но ничто не мешает написать код на C# и использовать команду Add-Type. Ну и потом, опять-таки, со всем этим есть смысл заморачиваться, если пишешь какую-то музыкальную программу. А какой смысл в такой программе без гуя? Разве что гуй написать на каком-нибудь HTA, и из него уже задействовать пауэршелл. Ради развлечения можно, конечно, поизголяться, но практической пользы от этого я не вижу ))
    Запись от diadiavova размещена 17.10.2020 в 11:54 diadiavova вне форума
  5. Старый комментарий
    Музыкально информировать администратора о результатах выполнения скрипта.
    Запись от politoto размещена 17.10.2020 в 13:04 politoto вне форума
  6. Старый комментарий
    Аватар для diadiavova
    Не проще ли файл воспроизвести?
    Запись от diadiavova размещена 17.10.2020 в 14:52 diadiavova вне форума
  7. Старый комментарий
    Аватар для XLAT
    Цитата:
    Сообщение от diadiavova Просмотреть комментарий
    Не проще ли файл воспроизвести?
    казалось бы чаво проще?!

    у меня такой к вам вопрос:
    Каким простым способом можно воспроизвести одновременно сразу два(и/или больше) миди-файла *.mid,
    например один играет в фоне, а в эффектах что-то звякает???

    как я понял в MCI так нельзя, отсюда у меня выше в коде спаренные плеи со стопами.
    Запись от XLAT размещена 17.10.2020 в 17:50 XLAT вне форума
    Обновил(-а) XLAT 17.10.2020 в 17:52
  8. Старый комментарий
    Аватар для diadiavova
    Цитата:
    Сообщение от XLAT Просмотреть комментарий
    Каким простым способом можно воспроизвести одновременно сразу два(и/или больше) миди-файла *.mid,
    например один играет в фоне, а в эффектах что-то звякает???
    Думаю - никаким. Если есть два файла с совпадающим таймингом и прочими прибамбасами (что мне кажется достаточно маловероятным), то при попытке их одновременного воспроизведения они вряд ли совпадут по времени. Но тут надо понимать, что миди-файл фактически аудио-файлом не является. По сути это файл данных. То есть он не содержит данных о звуках, но содержит данные о нотах, а если быть более точным, то о сообщениях и времени когда какое нужно отправить. Поэтому решение задачи состоит в том, чтобы разобрать файлы, то есть извлечь эти данные, привести их к удобоваримому виду, объединить, после чего можно воспроизводить либо способом описанным в этой серии, либо сохранить в один миди-файл и воспроизводить его каким-нибудь стандартным способом. Другого варианта я не вижу, а этот вряд ли можно считать простым, поскольку еще надо разбираться в структуре файлов и т. д.
    Запись от diadiavova размещена 17.10.2020 в 18:35 diadiavova вне форума
  9. Старый комментарий
    Аватар для XLAT
    Цитата:
    Сообщение от diadiavova Просмотреть комментарий
    Думаю - никаким
    FMOD справляется вроде неплохо.

    Цитата:
    Сообщение от diadiavova Просмотреть комментарий
    Думаю - никаким
    вот по этой причине MCI хорош для мелкой озвучки под спец заточенных утилит.

    там и размер побочных ресов, какими являются сами файлы миди желателен незаметного объема.
    например, мелодия из к/ф "17 мгновений весны." всего то лишь 2 кбайта
    Запись от XLAT размещена 17.10.2020 в 19:07 XLAT вне форума
    Обновил(-а) XLAT 17.10.2020 в 19:09
  10. Старый комментарий
    Аватар для diadiavova
    Цитата:
    Сообщение от XLAT Просмотреть комментарий
    FMOD справляется вроде неплохо.
    Честно говоря, ничего по этому поводу сказать не могу. Я обработкой аудио никогда не занимался, причем ни с помощью готовых программ, ни, тем более, с помощью самописных. Просто мне идея создания каких-то эффектов путем одновременного запуска нескольких файлов кажется странной.
    И уж если речь идет о работе с миди-форматом, то тут открывается куча возможностей именно в том случае, если обработать данные. То есть можно добавить в файл дополнительный канал, в который можно впихнуть любые эффекты, при этом синхронизировав их и по времени и по другим параметрам(тональность, громкость, темп и прочее). Простым такое решение не будет, но, думаю, что и сверхсложным тоже.
    Запись от diadiavova размещена 18.10.2020 в 11:41 diadiavova вне форума
  11. Старый комментарий
    Аватар для XLAT
    Цитата:
    Сообщение от diadiavova Просмотреть комментарий
    Я обработкой аудио никогда не занимался
    ну, если использовать готовые "промышленные" редакторы и если хорошо представлять,
    что нужно на выходе, то я не вижу тут ничего сложного.

    Цитата:
    Сообщение от diadiavova Просмотреть комментарий
    Просто мне идея создания каких-то эффектов путем одновременного запуска нескольких файлов кажется странной.
    хм, вот странно, что может быть это странно:
    смотрите:
    допустим это не скрипт, а небольшая утилитка, нуждающаяся в простой озвучке,
    то естественным простым способом управления вывода разных звуков,
    это их проигрывания из готовых файлов от музыкально одаренных спецов(даже необязательно музыкантов).
    Как видите речь о удобстве управления этим аудио-хозяйством с точки зрения юзер-кодера и тут с этим всё просто.

    Цитата:
    Сообщение от diadiavova Просмотреть комментарий
    И уж если речь идет о работе с миди-форматом, то тут открывается куча возможностей именно в том случае, если обработать данные. То есть можно добавить в файл дополнительный канал, в который можно впихнуть любые эффекты, при этом синхронизировав их и по времени и по другим параметрам(тональность, громкость, темп и прочее).
    походу ваш способ это рядом должен всё время сидеть музыкально одаренный чел?

    ок,
    если хранить не файлах, то тогда в чем хранить?
    идиотский вопрос, неправда ли?


    Предлагаете придумать некий новый формат с толстым талмудом АПИ,
    чтобы франт-прогер мог тыкать ваши каналы в нужные места своей проги обвешавшись секундомерами?

    Запись от XLAT размещена 18.10.2020 в 16:38 XLAT вне форума
    Обновил(-а) XLAT 18.10.2020 в 16:40
  12. Старый комментарий
    Аватар для diadiavova
    Цитата:
    Сообщение от XLAT Просмотреть комментарий
    хм, вот странно, что может быть это странно:
    смотрите:
    допустим это не скрипт, а небольшая утилитка, нуждающаяся в простой озвучке,
    то естественным простым способом управления вывода разных звуков,
    это их проигрывания из готовых файлов от музыкально одаренных спецов(даже необязательно музыкантов).
    Как видите речь о удобстве управления этим аудио-хозяйством с точки зрения юзер-кодера и тут с этим всё просто.
    Первая проблема, которую я вижу - это несовпадение темпов, даже если удастся запустить одновременно, все будет съезжать.
    Второй проблемой может оказаться, например, несовпадение тональностей.
    Как это все будет решаться?
    Цитата:
    Сообщение от XLAT Просмотреть комментарий
    походу ваш способ это рядом должен всё время сидеть музыкально одаренный чел?
    А как можно браться за написание программы, не имея хотя бы базового представления о предметной области?
    Цитата:
    Сообщение от XLAT Просмотреть комментарий
    ок,
    если хранить не файлах, то тогда в чем хранить?
    идиотский вопрос, неправда ли?


    Предлагаете придумать некий новый формат с толстым талмудом АПИ,
    чтобы франт-прогер мог тыкать ваши каналы в нужные места своей проги обвешавшись секундомерами?

    Да я вроде нигде ничего подобного не говорил. Я говорил о том, что слияние миди-файлов нужно осуществлять на уровне данных, независимо от того, будет ли результат воспроизводиться путем непосредственного программного взаимодействия с миди-интерфейсом или путем сохранения результатов слияния в файл. О хранении речи вообще не было, хранить можно как результат слияния в том же миди-формате или исходные файлы хранить, это вообще не важно.
    Запись от diadiavova размещена 18.10.2020 в 23:11 diadiavova вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru