Extensions:2.6/Py/Scripts/System/HID Mapper

提供: wiki
< Extensions:2.6‎ | Py‎ | Scripts‎ | System
2017年12月26日 (火) 11:18時点におけるwiki>Georg Kによる版 (fixed broken downloadlink, expanded version compatibility info)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
移動先: 案内検索
HID Mapper
Maps HID devices (e.g. MIDI devices) to custom blender functions.
UI location View Text Editor > Templates > HID Mapping
Usage Create a HID Mapping script similar to the provided template and run it. Then you can control blender with your MIDI device like specified in your mapping.
Version 0.2 (2012-06-06) Author(s) David Gnedt, Manuel Steiner, Christian Voglhuber
Blender >= 2.61 (except 2.63/2.63a), not:2.79 License GPL
Category System Distribution Extern
Note(s) Doesn't run with Blender 2.63/2.63a because of broken multiprocessing module. Also doesn't run in 2.79 and later (releases 2.64-2.78 not tested).


Executable information
File name system_hid_mapper.py
Current version download https://developer.blender.org/F20307
Python modules atexit, functools, inspect, threading, multiprocessing, subprocess
External Python Modules or dependencies amidi executable (can be found in alsa-utils package)


Warning Currently only tested on Linux with ALSA


Sample mapping

This mapping is also provided within Blender under Text Editor > Templates > HID Mapping.

# Functions:
#
#   reset_mapper()
#
#
# Constructors/Factories:
#
#   AMidiSubsystem.create(device=<Device>, name=<Device Name>, debug=<True/False*>)
#   MidiMessageType(status=<Status>, control=<Control>)
#
#
# Mapping class decorators:
#
#   register_mapping(<subsystem>)
#
#
# Mapping method decorators:
#
#   map_message(<MessageType>)
#   switch_pressed(value=<Pressed Value/0x7F*>)
#   switch_released(value=<Released Value/0x00*>)
#   relative_value(max=<Max Value/0x80*>, break=<Break Point Value/0x40*>)
#   merge(type=<MergeType.LAST*/MergeType.SUM>)
#
#
# MIDI message methods:
#
#   msg.get_status()
#   msg.get_control()
#   msg.get_value()
#

import bpy
from system_hid_mapper import *

reset_mapper()

@register_mapping(AMidiSubsystem.create(name='TotalTrack Control MIDI 1', debug=True))
class VSEMapping(object):
    @map_message(MidiMessageType(status=144, control=67)) # play left
    @switch_pressed()
    def play(self, msg):
        ''' play '''
        bpy.ops.screen.animation_play()
    
    @map_message(MidiMessageType(status=144, control=51)) # pause left
    @switch_pressed()
    def pause(self, msg):
        ''' pause '''
        bpy.ops.screen.animation_cancel(restore_frame=False)
    
    def seek(self, value):
        ''' move current frame '''
        bpy.context.scene.frame_current = bpy.context.scene.frame_current + value
    
    @map_message(MidiMessageType(status=144, control=73)) # loop in left
    @switch_pressed()
    def seek_prev(self, msg):
        ''' move to prev frame '''
        self.seek(self, -1)
    
    @map_message(MidiMessageType(status=144, control=74)) # loop out left
    @switch_pressed()
    def seek_next(self, msg):
        ''' move to next frame '''
        self.seek(self, 1)
    
    @map_message(MidiMessageType(status=176, control=25)) # jog left
    @relative_value()
    @merge(type=MergeType.SUM)
    def seek_jog(self, msg):
        ''' move current frame '''
        self.seek(self, msg.get_value())
    
    @map_message(MidiMessageType(status=144, control=77)) # loop in right
    @switch_pressed()
    def jump_keyframe_prev(self, msg):
        ''' jump to prev keyframe '''
        # requires Jump to Cut Addon
        bpy.ops.screen.keyframe_jump(next=False)
    
    @map_message(MidiMessageType(status=144, control=78)) # loop out right
    @switch_pressed()
    def jump_keyframe_next(self, msg):
        ''' jump to next keyframe '''
        # requires Jump to Cut Addon
        bpy.ops.screen.keyframe_jump()
    
    @map_message(MidiMessageType(status=144, control=75)) # load track left
    @switch_pressed()
    def strip_toggle_mute(self, msg):
        ''' toggle mute of selected clip '''
        sqe = bpy.context.scene.sequence_editor
        if sqe is not None and sqe.active_strip is not None:
            sqe.active_strip.mute = not sqe.active_strip.mute
    
    @map_message(MidiMessageType(status=176, control=8)) # volume left
    @merge(type=MergeType.LAST)
    def strip_volume_keyframe_insert(self, msg):
        ''' set key-framed volume of selected sound clip '''
        sqe = bpy.context.scene.sequence_editor
        if (sqe is not None and sqe.active_strip is not None and
            sqe.active_strip.type == 'SOUND'):
            sqe.active_strip.volume = msg.get_value() / 127.0
            sqe.active_strip.keyframe_insert(data_path='volume')