Скачал LMMS, потыкал. Прикольная халявная штука. Принцип аналогичен (Fruty Loops) FL Studio.В описании ЛММС так и говорится, что аналог Fruty Loops, но, это, наверное правильно, только для лиц непосвящённых в азы программирования и в принципы создания программ для Линукса. В Линуксе, обычное правило - одна программа для одной операции (для одного действия). В Линуксе всё обстоит несколько иначе, чем в платных программах для винды. Я увидел стёб сразу, но объяснить это трудно. Когда-то я знал винду, но понятия не имел, как там объединять программы. В Линуксе всё очевидно. Примитивную LMMS можно через коннектор соединить с другой программой, расширяющей возможности, ту в свою очередь можно соединить с третьей и так далее добившись того, что виндовый cakewalk sonar окажется примитивной игрушкой в руках композитора. Интерфейс ЛММС, действительно, удручает пользователя крякнутыми виндовыми прогами, но, нужно помнить, что эта прога лишь для написания треков, которые в последствии без проблем можно вывести для сведения звука в семидорожечную запись для домашнего кинотеатра, используя уже совсем другую программу.
https://youtu.be/PI43L1Gp9uw
Как и много раз до этого, упомяну ещё раз, что основная проблема не в крутом и навороченном ПО для Win, Mac или в доступном под Lin, а в другом...Я первую проблему вижу в следующем. У нас сайт про Блендер. У Блендера есть PythonAPI, позволяющий расширять возможности Блендера. Вот про это мало кто хочет говорить. Я говорю, что возможности Блендера можно расширить используя программы, написанные как и сам Блендер с открытым исходным кодом, используя PythonAPI. То бишь, я стараюсь не уходить от темы. Мне очень странно читать, что люди используют Блендер, но категорически отказываются от программного обеспечения, способствующего развитию этого самого Блендера. Если такое отвращение к свободно распространяемому программному обеспечению, то почему тогда используют сам Блендер? Майя всё равно ведь лучше, как тут жопой не крути. Я юзал эту прогу, и знаю, что Блендеру до неё очень далеко. Там назначишь кучу волос, и она считает, а Блендеру назначишь кучу волос, он херак и улетел в небытие. Скин применил в Блендере - жди беды, если тысячи эджей. Я ни разу не смог майку заставить зависнуть, что бы я там с ней не вытворял. 3D-max - легко повесить, прожорливая тварь. Блендер повесить как два пальца об асфальт.
https://youtu.be/2PVUlouy0tM
>> Если кто-то захочет синхронное поведение персов относительно музыки, то ЛММС он расцелует, расцелует либу Cairo, а ни Fruty Loops и Corel Draw...
... Леонид, было бы не плохо устроить целовальню на конкретном примере, сможешь показать как это делать? ...
... на текущий момент можно делать без парсинга - https://www.blender.org/manual/ru/editors/graph_editor/fcurves/editing.html#bake-sound-to-f-curves , то есть хотелось бы понять в чем преимущество разбора ...
<pattern steps="16" muted="0" type="1" name="TripleOscillator" pos="0" len="960">
<note pan="0" key="65" vol="100" pos="12" len="48"/>
<note pan="0" key="54" vol="100" pos="108" len="48"/>
<note pan="0" key="50" vol="100" pos="252" len="48"/>
<note pan="0" key="58" vol="100" pos="324" len="48"/>
<note pan="0" key="65" vol="100" pos="528" len="48"/>
<note pan="0" key="57" vol="100" pos="576" len="48"/>
<note pan="0" key="62" vol="100" pos="648" len="48"/>
<note pan="0" key="58" vol="100" pos="780" len="48"/>
<note pan="0" key="63" vol="100" pos="828" len="48"/>
<note pan="0" key="56" vol="100" pos="900" len="48"/>
</pattern>
>> Вот фрагмент файла ЛММС:Элементарно, Ватсон. Это файл проекта. Пишешь музыку, сохраняешь проект. Там есть два варианта сохранить как mmpz - сжатый, и mmp - обычный xml
... как получить такой код? ...
... на самом деле после того как получил mmp, открыл его в geany и пошел искать/читать описание формата ...Николай, спонтанность - это то, что отличает людей от нелюдей. Очень много идей у меня рождается спонтанно. Я - человек.
... у тебя есть готовый/полуготовый код парсера? ...
... в принципе вот xml прасер, то есть теперь нужно по смыслу пройтись ...
import xml.etree.ElementTree as ET
tree = ET.parse('hardstyle-01.mmp')
root = tree.getroot()
print(root.tag)
for child in root:
print(":",child.tag,child.attrib)
Если кто-то захочет синхронное поведение персов относительно музыки, то ЛММС он расцелует, расцелует либу Cairo, а ни Fruty Loops и Corel Draw...LMMS, ведь кушает .flp файлы FL Studio... так что можно относительно просто конвертировать в .xml
... у меня достаточно серьёзный настрой чтобы делать реверсинжиниринг "механического пианино" в blender ...
... у тебя будет код для этого или мне пользовать что есть? ...
FL Studio богаче в инструментарии, и там больше синтезаторов и эффектов... правда и цена у ней от $99 до $737 в зависимости от комплектации...но радует прогресс LMMS.Не буду утверждать, но, есть подозрение, что весь инструментарий у FL Studio есть благодаря румынскому программисту Полю Наска. Нет ничего сложного скоммуниздить у ZynAddSubFX несколько десятков звуков и объявить их достоянием республики пиндосов.
Я тестировал проприетарные миди-секвенсоры: четыре ноты бесплатно, пятая в подарок...Не спорю, потому и пишу, что LMMS порадовал.
Честное слово, я рад за тебя. У меня есть много чего сказать о краже, но ограничусь нелюбимым тобой текстом:ЦитироватьЯ тестировал проприетарные миди-секвенсоры: четыре ноты бесплатно, пятая в подарок...Не спорю, потому и пишу, что LMMS порадовал.
Кое-где существуют ещё народы и стада, но не у нас, братья мои; у нас есть государства.
Государство? Что это такое? Итак, слушайте меня, ибо теперь я скажу вам своё слово о смерти народов.
Государством называется самое холодное из всех холодных чудовищ. Холодно лжёт оно; и эта ложь ползёт из уст его: «Я, государство, есмь народ».
Это - ложь! Созидателями были те, кто создали народы и дали им веру и любовь; так служили они жизни.
Разрушители - это те, кто ставит ловушки для многих и называет их государством: они навесили им меч и навязали им сотни желаний.
Где ещё существует народ, не понимает он государства и ненавидит его, как дурной глаз и нарушение обычаев и прав.
Это знамение даю я вам: каждый народ говорит на своём языке о добре и зле - этого языка не понимает сосед. Свой язык обрёл он себе в обычаях и правах.
Но государство лжёт на всех языках о добре и зле: и что оно говорит, оно лжёт - и что есть у него, оно украло.
Всё в нём поддельно: крадеными зубами кусает оно, зубастое. Поддельна даже утроба его.
Смешение языков в добре и зле: это знамение даю я вам как знамение государства. Поистине, волю к смерти означает это знамение! Поистине, оно подмигивает проповедникам смерти!
Рождается слишком много людей: для лишних изобретено государство!
Смотрите, как оно их привлекает к себе, это многое множество! Как оно их душит, жуёт и пережевывает!
«На земле нет ничего больше меня: я упорядочивающий перст Божий» - так рычит чудовище. И не только длинноухие и близорукие опускаются на колени!
Ах, даже вам, великие души, нашёптывает оно свою мрачную ложь! Ах, оно угадывает богатые сердца, охотно себя расточающие!
Да, даже вас угадывает оно, вы, победители старого Бога! Вы устали в борьбе, и теперь ваша усталость служит новому кумиру!
Героев и честных людей хотел бы он уставить вокруг себя, новый кумир! Оно любит греться в солнечном сиянии чистой совести, - холодное чудовище!
Всё готов дать вам, если вы поклонитесь ему, новый кумир: так покупает он себе блеск вашей добродетели и взор ваших гордых очей.
Приманить хочет он вас, вы, многое множество! И вот изобретена была адская штука, конь смерти, бряцающий сбруей божеских почестей!
Да, изобретена была смерть для многих, но она прославляет самое себя как жизнь: поистине, сердечная услуга всем проповедникам смерти!
Государством зову я, где все вместе пьют яд, хорошие и дурные; государством, где все теряют самих себя, хорошие и дурные; государством, где медленное самоубийство всех - называется - «жизнь».
Посмотрите же на этих лишних людей! Они крадут произведения изобретателей и сокровища мудрецов: культурой называют они свою кражу - и всё обращается у них в болезнь и беду!
Посмотрите же на этих лишних людей! Они всегда больны, они выблёвывают свою желчь и называют это газетой. Они проглатывают друг друга и никогда не могут переварить себя.
Посмотрите же на этих лишних людей! Богатства приобретают они и делаются от этого беднее. Власти хотят они, и прежде всего рычага власти, много денег, - эти немощные!
Посмотрите, как лезут они, эти проворные обезьяны! Они лезут друг на друга и потому срываются в грязь и в пропасть.
Все они хотят достичь трона: безумие их в том - будто счастье восседало бы на троне! Часто грязь восседает на троне - а часто и трон на грязи.
По-моему, все они безумцы, карабкающиеся обезьяны и находящиеся в бреду. По-моему, дурным запахом несёт от их кумира, холодного чудовища; по-моему, дурным запахом несёт от всех этих служителей кумира.
Братья мои, разве хотите вы задохнуться в чаду их пастей и вожделений! Скорее разбейте окна и прыгайте вон!
Избегайте же дурного запаха! Сторонитесь идолопоклонства лишних людей!
Избегайте же дурного запаха! Сторонитесь дыма этих человеческих жертв!
Свободною стоит для великих душ и теперь ещё земля. Свободных много ещё мест для одиноких и для тех, кто одиночествует вдвоём, где веет благоухание тихих морей.
Ещё свободной стоит для великих душ свободная жизнь. Поистине, кто обладает малым, тот будет тем меньше обладаем: хвала малой бедности!
Там, где кончается государство, и начинается человек, не являющийся лишним: там начинается песнь необходимых, мелодия, единожды существующая и невозвратная.
Туда, где кончается государство, - туда смотрите, братья мои! Разве вы не видите радугу и мосты, ведущие к сверхчеловеку? -
Так говорил Заратустра.
... у меня достаточно серьёзный настрой чтобы делать реверсинжиниринг "механического пианино" в blender ...Жаль, что LMMS не умеет экспортировать в формат MIDI
... у тебя будет код для этого или мне пользовать что есть? ...
Я объяснял, что в линуксе программа может чего-то не уметь, но можно подключать другие программы. Если к ЛММС подключить Rosegarden, то партию можно будет сохранить и в миди файл:Цитировать... у меня достаточно серьёзный настрой чтобы делать реверсинжиниринг "механического пианино" в blender ...Жаль, что LMMS не умеет экспортировать в формат MIDI
... у тебя будет код для этого или мне пользовать что есть? ...
Нашёлся импортёр для 2.4x MIDI файлов http://jb.perin.pagesperso-orange.fr/midi_importer/doc/midi_importer.html
Халява: http://midifile.ru
Я объяснял, что в линуксе программа может чего-то не уметь, но можно подключать другие программы. Если к ЛММС подключить Rosegarden, то партию можно будет сохранить и в миди файл:Великолепно! Осталось дело за малым: написать вменяемое дополнение импорта MIDI в текущую версию Блендер с вменяемым интерфейсом, и желательно прослушиванием всего файла и каналов инструментов, которые задействованы... :)
Идёшь в меню File и выбираешь экспорт в миди:
... у меня есть предложение провести совместное исследование и оформить статью на тему "какделать: звук в анимацию" на вики нашего форума ...Вот для начала:
Нашёлся импортёр для 2.4x MIDI файлов http://jb.perin.pagesperso-orange.fr/midi_importer/doc/midi_importer.html
Халява: http://midifile.ru
ЦитироватьЯ объяснял, что в линуксе программа может чего-то не уметь, но можно подключать другие программы. Если к ЛММС подключить Rosegarden, то партию можно будет сохранить и в миди файл:Великолепно! Осталось дело за малым: написать вменяемое дополнение импорта MIDI в текущую версию Блендер с вменяемым интерфейсом, и желательно прослушиванием всего файла и каналов инструментов, которые задействованы... :)
Идёшь в меню File и выбираешь экспорт в миди:
Ага, и от этого персы запрыгают сами по себе...А они должны были сами начать скакать? :) Тот скрипт, для 2.4х просто импортирует партитуры инструментов из MIDI в отдельные IPO. А остальное - дело аниматора....
А кто сказал, что речь идёт о ТОМ СКРИПТЕ?ЦитироватьАга, и от этого персы запрыгают сами по себе...А они должны были сами начать скакать? :) Тот скрипт, для 2.4х просто импортирует партитуры инструментов из MIDI в отдельные IPO. А остальное - дело аниматора....
Речь идет об анимации персов синхронно с музыкой, если чо...если чо, то, то о чем ты пишешь, уже из раздела ИИ (частицы и поведение стаи)... а текущая версия Блендера даже MIDI не может конвертировать в кривые анимации.. :)
... есть предложения? ...Было предложение хернёй не страдать только с LMMS. И если всерьёз берётесь, то лучше сделайте дополнение как импортёр MIDI в кривые анимации... многие вам спасибо скажут... а может, и деньгой подсобят :)
с midi более менее понятно и на самом деле реализуемо без дополнительных дополнений. Все треки midi файла конвертируются в отдельные wav файлы одной строкой в консоле. Об этом спрашивал на ЛОРе и там дали годный код http://www.linux.org.ru/forum/multimedia/12722602 , который легко затачивается для мирных целей. Далее wav файлы запекаются в f-curve стандартными средствами blender, об этом мы уже говорили в этой теме.Тогда таким же образом инструменты по отдельности можно экспортировать в .ogg или .wav из LMMS (Экспортировать дорожки) или другого аудиоредактора... нахрена тогда писать дополнение вообще?
Вариант с парсером mmp меня заинтриговал и мне уже удалось получить доступ к трекам в python. Это весьма занятная тема.
... экспортер Jean-Baptiste PERIN парсит midi файл и делает on|off - анимацию, и твой совет по адаптации его к новой версии blender не остался не замеченным, попробуем его вытащить ...Рад, что был услышан... Успехов! :)
... экспортер Jean-Baptiste PERIN парсит midi файл и делает on|off - анимацию, и твой совет по адаптации его к новой версии blender не остался не замеченным, попробуем его вытащить ...Вот это можно приспособить к созданию ключей вершин на пианино. Но, по-моему нужен универсальный код для любого инструмента: анимировать какие-то параметры и прикручивать к нужному драйверами.
... суть - получить последовательность действий - нажатие клавиш и прочего ...
... поэтому собираем дальше полезную информацию ...
... спасибо ...
import bpy
scene = bpy.context.scene
instrument = None
parse = False
bpy.ops.object.select_all(action = 'DESELECT')
for line in open("lmms.mmp"):
split = line.replace('<',' ')
split = split.replace('>',' ')
split = split.replace('"',' ')
split = split.replace('=',' ')
split = split.split()
if split[0] == 'pattern':
instrument = split[8]
ob = scene.objects[instrument]
scene.objects.active = ob
ob.select = True
bpy.ops.object.mode_set(mode = 'EDIT')
parse = True
continue
if parse:
if split[0] == 'note':
pos = int(split[8])
scene.frame_set(pos)
key = split[4]
bpy.ops.object.vertex_group_set_active(key)
bpy.ops.object.vertex_group_select()
vol = float(split[6])
bpy.ops.transform.translate(value = (0,0,vol))
LEN = split[10]
import bpy
scene = bpy.context.scene
instrument = None
parse = False
layers = 20*[False]
layers[19] = True
for line in open("lmms.mmp"):
split = line.replace('<',' ')
split = split.replace('>',' ')
split = split.replace('"',' ')
split = split.replace('=',' ')
split = split.split()
if split[0] == 'pattern':
instrument = split[8]
parse = True
for i in range(108):
bpy.ops.mesh.primitive_plane_add(layers=layers)
ob = bpy.context.object
ob.name = instrument + '_' +'%s'%i
bpy.ops.object.select_all(action = 'DESELECT')
continue
if parse:
if split[0] == 'note':
key = split[4]
ob.name = instrument + '_' + key
ob = scene.objects[ob.name]
scene.objects.active = ob
ob.select = True
loc = ob.location
vol = float(split[6])
pos = int(split[8])
LEN = int(split[10])
if scene.frame_current > 1:
scene.frame_set(pos-1)
bpy.ops.anim.keyframe_insert_menu(type='Location')
scene.frame_set(pos)
ob.location = (0,0,vol)
bpy.ops.anim.keyframe_insert_menu(type='Location')
f = pos+LEN
scene.frame_set(f)
bpy.ops.anim.keyframe_insert_menu(type='Location')
scene.frame_set(f+1)
ob.location = loc
bpy.ops.anim.keyframe_insert_menu(type='Location')
if split[0] == '/pattern':
parse = False
import bpy
scene = bpy.context.scene
instrument = None
parse = False
layers = 20*[False]
layers[0] = True
for line in open("/home/leonid/lmms/projects/lmms.mmp"):
split = line.replace('<',' ')
split = split.replace('>',' ')
split = split.replace('"',' ')
split = split.replace('=',' ')
split = split.split()
if split[0] == 'pattern':
instrument = split[8]
parse = True
for i in range(108):
bpy.ops.mesh.primitive_plane_add(layers=layers)
ob = bpy.context.object
ob.name = instrument+'_'+'%s'%i
bpy.ops.object.select_all(action = 'DESELECT')
continue
if parse:
if split[0] == 'note':
key = split[4]
ob_name = instrument + '_' + key
ob = scene.objects[ob_name]
scene.objects.active = ob
ob.select = True
loc = ob.location[:]
vol = float(split[6])
pos = int(split[8])
LEN = int(split[10])
if scene.frame_current > 1:
scene.frame_set(pos-1)
bpy.ops.anim.keyframe_insert_menu(type='Location')
scene.frame_set(pos)
ob.location = (0,0,vol/100)
bpy.ops.anim.keyframe_insert_menu(type='Location')
f = pos+LEN
scene.frame_set(f)
bpy.ops.anim.keyframe_insert_menu(type='Location')
scene.frame_set(f+1)
ob.location = loc
bpy.ops.anim.keyframe_insert_menu(type='Location')
if split[0] == '/pattern':
parse = False
... пока не вижу надлежащего контейнера в который складывать ...А какой контейнер ты хочешь увидеть? Использовать информацию полученную из ммр можно использовать как только душе угодно. Кому-то захочется просто клавиши анимировать, кому-то арматуру. Мы не можем предугадать, поэтому драйверы - единственное универсальное решение. Наш импорт будет полезен тому, у кого уже есть идея и замоделена сцена. Юзер выбирает свой объект анимации и связывает нужный ему параметр анимации драйвером с анимированным параметром импортированного объекта... Это можно будет сделать либо питоном, либо вручную. Можно приделать кое-какие крутилки, чтобы можно было выбирать ни в аутлайнере, ни во вьюпорте, а в панели инструментов в каком-нибудь EnumProperty...
... надо подумать над юзабельностью ...
Господа-товарищи, Ланухумыч и Сангриныч. Можете вкратце описать для публики функциональные возможности по пунктам того дополнения, которое вы планируете? Очень интересно врубиться. Спасибо!Самый простой вариант: перс играет на ксилофоне и юзер желает, чтобы это было правдоподобно. Мы пытаемся облегчить юзеру исполнение поставленной задачи вплоть до полной автоматизации процесса. Извлечённые из файла данные могут позволить автоматом располагать предплечье над нужной плиткой и наносить удар по ней.
Ты шутишь? :) Нет? :oГоспода-товарищи, Ланухумыч и Сангриныч. Можете вкратце описать для публики функциональные возможности по пунктам того дополнения, которое вы планируете? Очень интересно врубиться. Спасибо!Самый простой вариант: перс играет на ксилофоне и юзер желает, чтобы это было правдоподобно. Мы пытаемся облегчить юзеру исполнение поставленной задачи вплоть до полной автоматизации процесса. Извлечённые из файла данные могут позволить автоматом располагать предплечье над нужной плиткой и наносить удар по ней.
Я не шучу. И, уже много сказал, вернее, написал код для реализации задумки. Читай код.Ты шутишь? :) Нет? :oГоспода-товарищи, Ланухумыч и Сангриныч. Можете вкратце описать для публики функциональные возможности по пунктам того дополнения, которое вы планируете? Очень интересно врубиться. Спасибо!Самый простой вариант: перс играет на ксилофоне и юзер желает, чтобы это было правдоподобно. Мы пытаемся облегчить юзеру исполнение поставленной задачи вплоть до полной автоматизации процесса. Извлечённые из файла данные могут позволить автоматом располагать предплечье над нужной плиткой и наносить удар по ней.
Тогда напомню, что "в каждой большой задаче сидит маленькая, пытающаяся вырваться наружу" (какой-то отечественный программёр так сказал)... можешь по функциям планируемое более детально разбить?
Я не шучу. И, уже много сказал, вернее, написал код для реализации задумки. Читай код.Да я вижу.... пытаюсь врубиться в суть. Возникли вопросы: Это будет универсальные риг (оснастка) и сетап персонажа (типа .BVH) или как будет реализована связь с драйверами сетапа? На уровне пользователя дополнения или вручную?
Тут тебе ни здесь, и, не хухры-мухры. :)
Последний исправленный код и есть импорт в анимационные кривые. Скриншот же я делал.ЦитироватьЯ не шучу. И, уже много сказал, вернее, написал код для реализации задумки. Читай код.Да я вижу.... пытаюсь врубиться в суть. Возникли вопросы: Это будет универсальные риг (оснастка) и сетап персонажа (типа .BVH) или как будет реализована связь с драйверами сетапа? На уровне пользователя дополнения или вручную?
Тут тебе ни здесь, и, не хухры-мухры. :)
http://3dyuriki.com/2009/06/21/setap-cetapshhik-slovar/
Предлагаю сразу не строить грандиозных планов, а замутить просто импорт инструментов в кривые, в том числе и MIDI... а дальше потихоньку разовьёте... Уже просто за импорт в анимационные кривые вам люди скажут большое спасибо.
... чтобы тема не ушла в песок и с учётом того что уже сделано и сказано, предлагаю через две недели более плотно обсудить "хотелки", мы же никуда не спешим ...Согласен.
Помогите решить проблему с экспортом в wav из lmms. Во вложении тестовый файл test27.mmp При его экспорте в wav о обратном импорте в lmms дорожки имеют разную длину? Почему так?Хм. Вообще, не вижу в своей версии 1.1.3, как добавить звуковую дорожку. Помнится, в прежних версиях видел. ???
Если после реимпорта у вас длины дорожек совпали, то выложите полученный из этого тестогово файла wav.
LanuHum, почитай тут https://lmms.io/wiki/index.php?title=Working_with_SamplesРазобрался не читая, но, спасибо. С дуру собрал последнюю версию с github. Действительно, "Экспортировать как петлю(убрать тишину в конце)"...
(gdb) run
Starting program: /usr/bin/lmms
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffe59f1700 (LWP 7744)]
[New Thread 0x7fffe51f0700 (LWP 7745)]
[New Thread 0x7fffe49ef700 (LWP 7746)]
Notice: could not set realtime priority.
[New Thread 0x7fffcca66700 (LWP 7747)]
VST sync support disabled in your configuration
[New Thread 0x7fffb4a51700 (LWP 7748)]
[New Thread 0x7fffabffe700 (LWP 7749)]
[New Thread 0x7fffab28d700 (LWP 7752)]
[New Thread 0x7fffaaa8c700 (LWP 7753)]
Thread 8 "QThread" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffab28d700 (LWP 7752)]
__memset_sse2 () at ../sysdeps/x86_64/multiarch/../memset.S:78
78 ../sysdeps/x86_64/multiarch/../memset.S: Нет такого файла или каталога.
(gdb) bt
#0 __memset_sse2 () at ../sysdeps/x86_64/multiarch/../memset.S:78
#1 0x0000000000556b15 in FxMixer::masterMix(float (*) [2]) ()
#2 0x0000000000594f72 in Mixer::renderNextBuffer() ()
#3 0x0000000000595214 in Mixer::fifoWriter::run() ()
#4 0x00007ffff6807e3c in ?? () from /usr/lib/x86_64-linux-gnu/libQtCore.so.4
#5 0x00007ffff7bc170a in start_thread (arg=0x7fffab28d700) at pthread_create.c:333
#6 0x00007ffff432282d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
(gdb) quit
Кстати очень жаль, что lmms, конкретно в ubuntu 16.04, очень падучая. Чуть не туда нажал крашится. Даже просто нормально прослушать встроенные треки не дает. Иногда вообще хрипит. Альса, пульса, сдл.Послушал несколько раз Root84-TrancyLoop.mmpz в транке с гитхаба. Для пущей уверенности установил оффициальную версию 1.1.3, ещё несколько раз послушал Root84-TrancyLoop.mmpz. Никаких проблем. Никаких проблем и с другими демками. В настройках звука SDL, в настройках миди ALSA. Дистрибутив ROSA Fresh 8.
Может есть какой рецепт чтоб стабильно работала? У вас она как, стабильна? Какая версия, дистибутив?
Вот бэктрейс. Просто слушал Root84-TrancyLoop.mmpz, ничего не трогал, через минуту крашнулось.
Ну, видно что "memset.S: Нет такого файла или каталога". Собрать транк с гитхаба? Может мейтейнет накосячил, просто пересобрать?
Хм, интересно. Значит убунтовцы как всегда...Да, там ,просто,косяк с nvidia-драйвером. Система обновляется, ставит ядро 4.4, и после начинает долго грузиться. Так грузится несколько секунд, а после обновления несколько минут. Меня это вымораживало, и, я систему снёс. У росы для моей GTX-440 припасён отдельный драйвер 361.42, а у Магеи его нет, и из-за этого сыр-бор. Про секунды я не вру, ибо у меня SSD - это, просто чудо для установки на него ОС.
Надо попробовать в лайф режиме Росы загрузиться. А почему ты с Мигеи ушел?
... может есть у кого "справка" о том как из файла формата mmp вытянуть время трека? в милли/микро/секундах-с ...Очень интересный вопрос. :)
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffb63a7700 (LWP 14077)]
0x000000000050d712 in AutomatableModel::controllerValue(int) const ()
(gdb) bt
#0 0x000000000050d712 in AutomatableModel::controllerValue(int) const ()
#1 0x0000000000566af8 in FxMixer::masterMix(float (*) [2]) ()
#2 0x000000000054666a in Mixer::renderNextBuffer() ()
#3 0x00000000005468f4 in Mixer::fifoWriter::run() ()
#4 0x00007ffff68bae3f in () at /usr/lib64/libQtCore.so.4
#5 0x00007ffff7bc5645 in start_thread () at /lib64/libpthread.so.0
#6 0x00007ffff43fadfd in clone () at /lib64/libc.so.6
void TimeDisplayWidget::updateTime()
{
Song* s = Engine::getSong();
switch( m_displayMode )
{
case MinutesSeconds:
m_majorLCD.setValue( s->getMilliseconds() / 60000 );
m_minorLCD.setValue( ( s->getMilliseconds() / 1000 ) % 60 );
m_milliSecondsLCD.setValue( s->getMilliseconds() % 1000 );
break;
case BarsTicks:
int tick;
tick = ( s->getMilliseconds() * s->getTempo() * (DefaultTicksPerTact / 4 ) ) / 60000 ;
m_majorLCD.setValue( (int)(tick / s->ticksPerTact() ) + 1);
m_minorLCD.setValue( ( tick % s->ticksPerTact() ) / ( s->ticksPerTact() / s->getTimeSigModel().getNumerator() ) +1 );
m_milliSecondsLCD.setValue( ( tick % s->ticksPerTact() ) %( s->ticksPerTact() / s->getTimeSigModel().getNumerator() ) );
break;
default: break;
}
}
... по своему вопросу про время нашел вот такой код, который связывает тики и время ...len = 5184 - это количество тиков?
... вечерком сброшу файлик со скриптом для дальнейшего обсуждения ...Заканчивается вечерок, однако... :)
... добавил во вложение предварительный вариант импорта mmp в кривые анимации ...
... это небольшое дополнение в раздел File->Import ...
... после вызова процедуры импорта создаётся пустой объект с pattern-секциями, у для которых добавлены анимированные свойства "custom properties" на основании параметров note(pos,len) ...
... ключи анимации расставляются с текущей позиции, кривая имеет линейные сегменты со смещением offset, которое задаётся через меню импорта ...
from bpy_extras.io_utils import ImportHelper
class LMMPPatternToScene(bpy.types.Operator, ImportHelper):
"""Load *.mmp"""
bl_idname = "import.lmmp_pattern_to_scene"
bl_label = "LMMP Pattern to Scene"
bl_description = "Imports notes from LMMP files into 3D Scenes"
bl_options = {'REGISTER','PRESET', 'UNDO'}
filename_ext = ".mmp"
filter_glob = StringProperty(
default="*.mmp",
options={'HIDDEN'},
)
Offset = FloatProperty(name="Offset Frame", description="Offset frame", min=0, default=1)
DefaultTicksPerTact = FloatProperty(name="Default Ticks Per Tact", description="Default ticks per tact", min=16, default=192)
def draw(self, context):
... сейчас понятно что можно загружать "звук", но хотелось бы еще научиться выгружать в "звук" ...Что ты под этим подразумеваешь, то же что и я, или нет? :)
... пояснение где смотреть ...Что дальше будем с этим делать?
Аппликату́ра (нем. Applikatur, от лат. applico — прикладываю, прижимаю) — порядок расположения и чередования пальцев при игре на музыкальном инструменте. Аппликатурой также называется указание пальцев в нотах с помощью цифр или, реже, иным способом. Аппликатура особенно важна для клавишных и струнных инструментов. Умение разрабатывать аппликатуру — важная составляющая мастерства исполнителя.
>> ещё аппликатуру можно бы было указывать...Я скажу так: тема может нас объединить. Ставилось - не ставилось, какая разница?
... несмотря на упоминание в этой теме подобного, указанной цели не ставилось ...
... тем не менее, если к этому функционалу есть интерес, давайте собирать информацию ...
... вот например
https://itunes.apple.com/ru/app/chord3d-guitar-ukulele-guitalele/id1004214587?mt=12
... есть у этой тематики технологические описания и готовые шаблоны? ...
... выкладывайте алгоритмы и методики ...
>> Ещё что?Я знаю, что у нас есть скрипт, но, как мне показалось, наш скрипт маловат. :)
... умеренный сепаратизм ...
... у нас "уже" есть скрипт загрузки нот в кривые без привязки к конкретному инструменту ...
... допустим, что даже есть анимированный инструмент со своим ригом ...
... теперь нужно проработать интерфейс стыковки одного с другим ...
... и в целом правила стыковки не должны зависеть от инструмента (ксилофон, саксофон, миелафон, ...) ...
class NoteSettingItem(bpy.types.PropertyGroup):
name = bpy.props.StringProperty()
volume = bpy.props.IntProperty()
position = bpy.props.IntProperty()
duration = bpy.props.IntProperty()
hands = bpy.props.EnumProperty(
name="Hands",
items=( ("left", "Left", ""),
("right", "Right", ""),
("both", "Both", "")),
default="right")
left_hand = bpy.props.EnumProperty(
name="motion_left_hand",
items=( ("up", "Up", ""),
("down", "Down", ""),
("blow", "Blow", ""),
("fingers", "Fingers", "")),
default="blow")
finger_left_1 = bpy.props.BoolProperty()
finger_left_2 = bpy.props.BoolProperty()
finger_left_3 = bpy.props.BoolProperty()
finger_left_4 = bpy.props.BoolProperty()
finger_left_5 = bpy.props.BoolProperty()
finger_left_1_connect = bpy.props.BoolProperty()
finger_left_2_connect = bpy.props.BoolProperty()
finger_left_3_connect = bpy.props.BoolProperty()
finger_left_4_connect = bpy.props.BoolProperty()
finger_left_5_connect = bpy.props.BoolProperty()
right_hand = bpy.props.EnumProperty(
name="motion_right_hand",
items=( ("up", "Up", ""),
("down", "Down", ""),
("blow", "Blow", ""),
("fingers", "Fingers", "")),
default="blow")
finger_right_1 = bpy.props.BoolProperty()
finger_right_2 = bpy.props.BoolProperty()
finger_right_3 = bpy.props.BoolProperty()
finger_right_4 = bpy.props.BoolProperty()
finger_right_5 = bpy.props.BoolProperty()
finger_right_1_connect = bpy.props.BoolProperty()
finger_right_2_connect = bpy.props.BoolProperty()
finger_right_3_connect = bpy.props.BoolProperty()
finger_right_4_connect = bpy.props.BoolProperty()
finger_right_5_connect = bpy.props.BoolProperty()
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_import.append(menu_func)
bpy.types.TextCurve.notes = bpy.props.CollectionProperty(type=NoteSettingItem)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_import.remove(menu_func)
del bpy.types.TextCurve.notes
... мне думается, что ксилофон уже должен содержать полный набор действий (action) и задача только их расставить во времени ...Это не помешает ксилофону. По умолчанию я установил удары. :)
class View3DPanel():
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
class VIEW3D_PT_lmms(View3DPanel, bpy.types.Panel):
bl_category = "Animation"
bl_context = "objectmode"
bl_label = "Musicians Animation with LMMS"
def draw(self, context):
ob=context.object
note = 0
if ob and ob.type == 'FONT' and len(ob.data.notes) > 0:
layout = self.layout
ctx = ob.data.notes[note] #develop
layout.prop(ctx,"name")
layout.prop(ctx,"volume")
layout.prop(ctx,"position")
layout.prop(ctx,"duration")
layout.label('Hands:')
layout.prop(ctx,"hands",expand = True)
if ctx.hands in {'left','both'}:
layout.label('Left hand:')
layout.prop(ctx,"left_hand",expand = True)
if ctx.left_hand in {'fingers'}:
box = layout.box()
box.label('Fingers left hand')
row = box.row()
row.prop(ctx,"finger_left_1",text="1")
row.prop(ctx,"finger_left_1_connect",text="done")
row = box.row()
row.prop(ctx,"finger_left_2",text="2")
row.prop(ctx,"finger_left_2_connect",text="done")
row = box.row()
row.prop(ctx,"finger_left_3",text="3")
row.prop(ctx,"finger_left_3_connect",text="done")
row = box.row()
row.prop(ctx,"finger_left_4",text="4")
row.prop(ctx,"finger_left_4_connect",text="done")
row = box.row()
row.prop(ctx,"finger_left_5",text="5")
row.prop(ctx,"finger_left_5_connect",text="done")
if ctx.hands in {'right','both'}:
layout.label('Right hand:')
layout.prop(ctx,"right_hand",expand = True)
if ctx.right_hand in {'fingers'}:
box = layout.box()
box.label('Fingers right hand')
row = box.row()
row.prop(ctx,"finger_right_1",text="1")
row.prop(ctx,"finger_right_1_connect",text="done")
row = box.row()
row.prop(ctx,"finger_right_2",text="2")
row.prop(ctx,"finger_right_2_connect",text="done")
row = box.row()
row.prop(ctx,"finger_right_3",text="3")
row.prop(ctx,"finger_right_3_connect",text="done")
row = box.row()
row.prop(ctx,"finger_right_4",text="4")
row.prop(ctx,"finger_right_4_connect",text="done")
row = box.row()
row.prop(ctx,"finger_right_5",text="5")
row.prop(ctx,"finger_right_5_connect",text="done")
... заморачиваться на коде интерфейса пользователя преждевременно ...Я не заморачиваюсь на коде интерфейса - это касается того, что ты называешь юзабельностью. Развивая какую-то идею я сразу хочу видеть, насколько это будет удобно.
... мне сейчас невидна вся оснастка, только некоторые её фрагменты ...
... вот например такой фрагмент - представление ноток в nla элементов инструмента ...
... допустим что есть связь (пряма или опосредованная) между "ноткой" и действием инструмента, при этом у инструмента прописаны все действия в виде "базовых" треков (на картинке это нижние треки 1 с невыделенными лентами note055, note067) ...
... у базовых треков отключено влияние на канал, они служат только для хранения ленты, которая копируется в рабочие треки note055_gen , note067_gen ...
... таким образом нужно скопировать и расставить в определённых местах рабочих треков действия из базовых треков ...
... на мой взгляд это удобно и контролируемо ...
... а если сразу рисовать во вьюпорте bgl ...Может, тогда лучше создать из кривой безье объект "нота"?
например
http://blender.stackexchange.com/questions/61699/how-to-draw-geometry-in-3d-view-window-with-bgl?rq=1
http://hhttp://blender-3d.ru/forum/Smileys/default/wink.gifttps://svn.blender.org/svnroot/bf-extensions/trunk/py/scripts/addons/space_view3d_screencast_keys.py
... мне думается, что ксилофон уже должен содержать полный набор действий (action) и задача только их расставить во времени ...Николай, пытаюсь расставить во времени автоматически:
class AnimFromOtherKeys(bpy.types.Operator):
bl_idname = "anim.create_from_other_keys"
bl_label = "Create"
bl_description = ""
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
scene = context.scene
ob = bpy.data.objects["Synth Brazz 1"]
keys = []
for fcurve in ob.animation_data.action.fcurves:
fcurve_keys = [fcurve.data_path]
len_points = len(fcurve.keyframe_points)
for i in range(0,len_points,4):
k0 = fcurve.keyframe_points[i].co[:]
k1 = fcurve.keyframe_points[i+1].co[:]
k2 = fcurve.keyframe_points[i+2].co[:]
k3 = fcurve.keyframe_points[i+3].co[:]
note = ''
for symbol in fcurve.data_path:
if symbol.isdigit():
note+=symbol
keys.append((note,k0,k1,k2,k3))
frames = []
for key in keys:
frames.append(key[1][0])
frames.sort()
hands = ['Left','Right']
for frame in frames:
for key in keys:
if key[1][0] == frame:
bpy.ops.object.select_all(action='DESELECT')
ob_anim = bpy.data.objects[hands[0]]
ob_anim.select = True
scene.frame_set(frame)
.....................
hands.reverse()
return {'FINISHED'}
... так тоже пытаюсь :) ...:)
>> я пытаюсь брать данные из твоим кодом созданного объектаЗапустил. Ну, это в принципе то же, что я и писал здесь в своём первом парсере. Или нет? Ты свой код зря выбросил. Ладно, завтра гляну, сегодня времени не было. :)
... упс, сам то уже выкинул этот код и объекты в топку, новая тема ;) ...
... суть - не управлять кривыми, а рулить уже готовыми действиями инструмента ...
... то есть настройка должным образом инструмента это отдельная задача, её нужно решать в рамках подготовки инструмента (ксилофон, саксофон, миелафон, ...) ...
>> уже отдохнуть башке нужно
... попробуй запустить шмеля {ALT+A}...
https://yadi.sk/d/RQpSw8o3z5Qno
>> Ты свой код зря выбросилЯ уже писал, что нужно систематизировать действия. Независимо от инструментов действия музыканта ограничены: игра рукой (рукой, смычком, медиатором, палочкой) и игра пальцами. Но, к этому нужно добавить и поведение инструментов в зависимости от их типа: клавишные, струнные, духовые, какие-то там ещё. Если музыкант играет на пианино, то нужно анимировать пальцы обеих рук и клавиши, если играет на трубе, то так же. Если играет на гитаре медиатором, то анимировать нужно одну руку, пальцы другой руки и струны. Вот я и рисовал гуёвину, объединяющую все доступные действия. А ты считал, что это преждевременно. Своевременно я рисовал. :)
... конечно же код в копилке, но ...
... но в последней варианте уровень абстракции выше ...
... есть файлы с данными (mmp,midi,xml) в которых упакована информация о старте и продолжительности действия ...
... есть 3d модель с анимацией и своим набором действий ...
... нужно связать действия из файла с действиями из анимации ...
class MusicPatternSettings(bpy.types.PropertyGroup):
@classmethod
def register(cls):
bpy.types.TextCurve.music_pattern = PointerProperty(
name="Music Pattern Settings",
description="",
type=cls,
)
auto = BoolProperty()
musician = StringProperty()
instrument = StringProperty()
@classmethod
def unregister(cls):
del bpy.types.TextCurve.music_pattern
if ob and ob.type == 'FONT' and len(ob.data.notes) > 0:
layout = self.layout
+ layout.prop_search(ob,"musician",context.blend_data,'objects',text='Musician')
+ layout.prop_search(ob,"instrument",context.blend_data,'objects',text='Instrument')
+ layout.prop(ob,"auto")
ctx = ob.data.notes[note]
на текущий момент моё видение такое:
А) на стороне чтеца
1) загрузка данных в "универсальную" структуру мета-действий
2) сохранение "универсальной" структуры в .blend модели
Б) на стороне жнеца
1) чтение "универсальной" структуры мета-действий
2) преобразование мета-действий в "соответствии" с "согласованным" профилем выбранного инструмента в действия
В) на стороне кузнеца
1) моделирование инструмента
2) моделирование арматурной оснасти
3) описание "согласованного" профиля
инструмент - это 3d модель (любая модель или группа моделей с прописанными действиями)
согласованный профиль - таблица трансляции мета-действий в действия
универсальная структура - это блок памяти для хранения информации, прочитанной из различных форматов файлов - {код действия, время начала, продолжительность, сила}
твой код служит оснасткой для кузнеца для подготовки инструмента
опять же инструмент , это уже не просто (ксилофон, саксофон, миелафон, ...), а (ксилофон, саксофон, миелафон, ...) + исполнитель (если таковой предусмотрен сценой) ...
notes = {
"48": ["До_4","c4","C4"] # start
"49": ["До_диез_4","Ре_бемоль_4","cis4","des4","C-sharp4","D-flat4"]
"50": ["Ре_4","d4","D4"]
"51": ["Ре_диез_4","Ми_бемоль_4","dis4","es4","D-sharp4","E-flat4"]
"52": ["Ми_4","e4","E4"]
"53": ["Фа_4","f4","F4"]
"54": ["Фа_диез_4","Соль_бемоль_4","fis4","ges4","F-sharp4","G-flat4"]
"55": ["Соль_4","g4","G4"]
"56": ["Соль_диез_4","Ля_бемоль_4","gis4","as4","G-sharp4","A-flat4"]
"57": ["Ля_4","a4","A4"]
"58": ["Ля_диез_4","Си_бемоль_4","ais4","b4","A-sharp4","B-flat4"]
"59": ["Си_4","h4","B4"]}
if key == "49":
for ob in bpy.data.objects:
if ob.name in notes["49"]:
obj = ob
else:
obj = bpy.data.objects.new(name = notes["49"][-1])
>> Название клавиш должно начинаться с названия инструмента, а заканчиваться буквами, соответствующими ноте, например: "Piano_C2".Я писал ранее и добавил крутилки, правда, ни все. В гуи мы выбираем, какой объект мы будем анимировать: музыканта или инструмент?
... это хорошая идея ...
>> Только, не создаём action для каждой ноты, а создаём анимацию каждого объекта - клавиши3д.
... каким образом узнать какой тип анимации нужно создать по указанной ноте для конкретного объекта - клавиши3д? ...
>> Твой код очень подходит для клавишных инструментов, и файл .blend , который ты предоставил, это хорошо демонстрирует.Ну, какой тип анимации для клавишей пианино? Location по Z. Я только не вникал, как по отдельным осям вставлять ключи, но по моему, знаю, как удалить ненужные fcurve. :)
... в том файле не было кода который создавал анимацию, там был код который создавал копии действия и расставлял эти действия в нужные места ...
... поэтому вопрос остаётся открытым - каким образом узнать какой тип анимации (или какую анимацию) нужно создать по указанной ноте для конкретного объекта - клавиши3д? ...
>> Location по Z.Крутилку нужно добавить, и, для того мы указываем оператору bl_options = UNDO, чтобы можно было визуально подобрать. Двигаем крутилку - всё переписывается. Также, можно предварительно приблизительно вычислить величину из габаритов инструмента.
... на какую величину, какой формы? ...
tree = ET.parse(self.filepath)
node = tree.getroot()
for head in node.findall('song'):
for child_song in head.findall('trackcontainer'):
for child_trackcontainer in child_song.findall('track'):
for child_track in child_trackcontainer.findall('pattern'):
for child_pattern in child_track.findall('note'):
print(child_pattern)
def mmp_parse(node):
if node.tag=='pattern':
notes={}
for note in node:
key = note.attrib['key'].zfill(3)
if not (key in notes):
notes[key] = []
note=(int(note.attrib['pos']), int(note.attrib['len']), int(note.attrib['vol']))
notes[key].append(note)
if len(notes)>0:
return [{'name':node.attrib['name'], 'pos':int(node.attrib['pos']), 'len':int(node.attrib['len']), 'notes':notes}]
return []
patterns = []
for child in node:
patterns.extend(mmp_parse(child))
return patterns
>>Чтобы было понятней, вот словарь, написанный для одной октавы:
... кстати, ты сможешь сделать весь словарь ...
notes_data = {
0: ["До_4","c4","C4"],
1: ["До_диез_4","Ре_бемоль_4","cis4","des4","C-sharp4","D-flat4"],
2: ["Ре_4","d4","D4"],
3: ["Ре_диез_4","Ми_бемоль_4","dis4","es4","D-sharp4","E-flat4"],
4: ["Ми_4","e4","E4"],
5: ["Фа_4","f4","F4"],
6: ["Фа_диез_4","Соль_бемоль_4","fis4","ges4","F-sharp4","G-flat4"],
7: ["Соль_4","g4","G4"],
8: ["Соль_диез_4","Ля_бемоль_4","gis4","as4","G-sharp4","A-flat4"],
9: ["Ля_4","a4","A4"],
10: ["Ля_диез_4","Си_бемоль_4","ais4","b4","A-sharp4","B-flat4"],
11: ["Си_4","h4","B4"]
}
notes = {}
for o in range(9):
for i in range(12):
key = i + (o*9)
a=notes_data[i]
n = []
for s in a:
s = s.replace("4","%s"%o)
n.append(s)
notes[key] = n
print(notes)
bpm = None
for head in node.findall('head'):
if 'bpm' in head.attrib:
bpm = int(head.attrib['bpm'])
if not bpm:
for child in head.findall('bpm'):
if 'value' in child.attrib:
bpm = int(child.attrib['value'])
def data_from_mmp(path,name):
filename=path+name+'.mmp'
node = mmp_load(filename)
bpm = 120
elem = node.find('.//head')
if elem:
if 'bpm' in elem.attrib:
bpm = int(elem.attrib['bpm'])
else:
elem = node.find('.//bpm')
bpm = int(elem.attrib['value'])
return {'tempo':bpm, 'patterns':mmp_parse(node)}
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import os, subprocess
import bpy
from bpy.props import *
from bpy_extras.io_utils import ImportHelper
import xml.etree.ElementTree as ET
bl_info = {
"name": "Import LMMP Pattern to Scene (.mmp)",
"author": "blender-3d.ru/forum",
"version": (0, 0, 1),
"blender": (2, 78, 0),
"location": "File > Import > LMMP Pattern to Scene(.mmp)",
"description": "Imports LMMP files as a series of animation properties",
"category": "Import-Export",
}
"""
This script imports LMMP files into 3D Scenes (.mmp)
"""
def fingers_item_callback(self, context):
ob = context.object
items = [("1", "1", ""),
("2", "2", ""),
("3", "3", ""),
("4", "4", ""),
("5", "5", "")]
if ob.instrument_type == 'guitar':
items = [("b", "B", ""),
("1", "1", ""),
("2", "2", ""),
("3", "3", ""),
("4", "4", "")]
return items
class MusicPatternSettings(bpy.types.PropertyGroup):
@classmethod
def register(cls):
bpy.types.TextCurve.music_pattern = PointerProperty(
name="Music Pattern Settings",
description="",
type=cls,
)
autocreate = BoolProperty()
midi_data_object = StringProperty()
musician_instrument = bpy.props.EnumProperty(
name="Musician or Instrument",
items=( ("instrument", "Instrument", ""),
("musician", "Musician", "")),
default="instrument")
instrument_type = bpy.props.EnumProperty(
name="Instrument Type",
items=( ("bowed", "Bowed Strings", ""),
("brass", "Brass Instruments", ""),
("guitar", "Guitar family", ""),
("keyboard", "Keyboard instruments", ""),
("percussion", "Percussion instruments", ""),
("woodwind", "Woodwind", "")),
default="keyboard")
@classmethod
def unregister(cls):
del bpy.types.TextCurve.music_pattern
class NoteSettingItem(bpy.types.PropertyGroup):
name = bpy.props.StringProperty()
index = bpy.props.IntProperty()
volume = bpy.props.IntProperty()
position = bpy.props.IntProperty()
duration = bpy.props.IntProperty()
hands = bpy.props.EnumProperty(
name="Hands",
items=( ("left", "Left", ""),
("right", "Right", ""),
("both", "Both", "")),
default="right")
left_hand = bpy.props.EnumProperty(
name="motion_left_hand",
items=( ("up", "Up", ""),
("down", "Down", ""),
("blow", "Blow", ""),
("fingers", "Fingers", "")),
default="blow")
fingers_left = bpy.props.EnumProperty(
name="Fingers Left Hand",
items=fingers_item_callback)
finger_connect_left = bpy.props.BoolProperty()
right_hand = bpy.props.EnumProperty(
name="motion_right_hand",
items=( ("up", "Up", ""),
("down", "Down", ""),
("blow", "Blow", ""),
("fingers", "Fingers", "")),
default="blow")
fingers_right = bpy.props.EnumProperty(
name="Fingers Right Hand",
items=fingers_item_callback)
finger_connect_right = bpy.props.BoolProperty()
class View3DPanel():
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
class VIEW3D_PT_lmms(View3DPanel, bpy.types.Panel):
bl_category = "Animation"
bl_context = "objectmode"
bl_label = "Musicians Animation with LMMS"
def draw(self, context):
ob=context.object
note = 0
if ob and ob.type == 'FONT' and len(ob.data.notes) > 0:
layout = self.layout
layout.prop_search(ob,"midi_data_object",context.blend_data,'objects',text='Musician')
layout.prop(ob,"auto")
ctx = ob.data.notes[note] #develop
layout.prop(ctx,"name")
layout.prop(ctx,"volume")
layout.prop(ctx,"position")
layout.prop(ctx,"duration")
layout.label('Hands:')
layout.prop(ctx,"hands",expand = True)
if ctx.hands in {'left','both'}:
layout.label('Left hand:')
layout.prop(ctx,"left_hand",expand = True)
if ctx.left_hand in {'fingers'}:
box = layout.box()
box.label('Fingers left hand')
box.prop(ctx,"fingers_left",text="1")
box.prop(ctx,"finger_connect_left",text="Leave Pressed")
if ctx.hands in {'right','both'}:
layout.label('Right hand:')
layout.prop(ctx,"right_hand",expand = True)
if ctx.right_hand in {'fingers'}:
box = layout.box()
box.label('Fingers right hand')
box.prop(ctx,"fingers_right",text="1")
box.prop(ctx,"finger_connect_right",text="Leave Pressed")
scale = 1
offset = 0
scene = None
startframe = 0
class LMMPPatternToScene(bpy.types.Operator, ImportHelper):
bl_idname = "import.lmmp_pattern_to_scene"
bl_label = "LMMP Pattern to Scene"
bl_description = "Imports notes from LMMP files into 3D Scenes"
bl_options = {'REGISTER', 'UNDO'}
filename_ext = {".mmp"}
filter_glob = StringProperty(
default="*.mmp",
options={'HIDDEN'},
)
Offset = FloatProperty(name="Offset Frame", description="Offset frame", min=0, default=1)
DefaultTicksPerTact = FloatProperty(name="Default Ticks Per Tact", description="Default ticks per tact", min=16, default=192)
def draw(self, context):
layout = self.layout
box = layout.box()
box.label('LMMP Pattern:', icon='SORTSIZE')
box.prop(self, 'Offset')
box.prop(self, 'DefaultTicksPerTact')
def execute(self, context):
scene = context.scene
fps = scene.render.fps
layers = 20*[False]
layers[0]=True
tree = ET.parse(self.filepath)
node = tree.getroot()
bpm = None
for head in node.findall('head'):
if 'bpm' in head.attrib:
bpm = int(head.attrib['bpm'])
if not bpm:
for child in head.findall('bpm'):
if 'value' in child.attrib:
bpm = int(child.attrib['value'])
scale = 60/bpm/(self.DefaultTicksPerTact/4)*fps
location = 0
for head in node.findall('song'):
for child_song in head.findall('trackcontainer'):
for child_trackcontainer in child_song.findall('track'):
for child_track in child_trackcontainer.findall('pattern'):
tname=child_track.attrib['name']
tpos=int(child_track.attrib['pos'])
tlen=int(child_track.attrib['len'])
bpy.ops.object.text_add(location=(0, 0, location), layers=layers)
ob = bpy.context.object
ob.data.body = tname
ob.name = tname
location += 0.1
for pnote in child_track.findall('note'):
note = ob.data.notes.add()
key=pnote.attrib['key']
pos=int(pnote.attrib['pos'])
len=int(pnote.attrib['len'])
vol=int(pnote.attrib['vol'])
note.name =
note.index =
note.volume =
note.position =
note.duration =
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(LMMPPatternToScene.bl_idname, text="LMMP Pattern to Scene (.mmp)", icon='PLUGIN')
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_import.append(menu_func)
bpy.types.TextCurve.notes = bpy.props.CollectionProperty(type=NoteSettingItem)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_import.remove(menu_func)
del bpy.types.TextCurve.notes
if __name__ == "__main__":
register()