├── .gitignore ├── example.png ├── .gitattributes ├── Pipfile ├── README.md ├── LICENSE └── src └── BPMmarker.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | .venv 4 | Pipfile.lock 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aodaruma/BPMmarker/HEAD/example.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | "fake-bpy-module-3.0" = "*" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.9" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BPMmarker 2 | the Blender addon adding mark beats to timeline with BPM automatically 3 | 4 | ![example](https://raw.githubusercontent.com/Aodaruma/BPMmarker/master/example.png) 5 | 6 | ## how to install & use 7 | 8 | 1. download `BPMmarker.py` 9 | 2. install it into Blender with `installing addon from file` 10 | 3. find it in the toolshelf of 3Dviewport 11 | 4. click the button and enjoy! 12 | 13 | ## description of dialog 14 | 15 | - **BPM** 16 | the number of BPM 17 | - **Beat** 18 | the number of beats 19 | - **clear previous mark** 20 | whether clear markers that created previously 21 | 22 | --- 23 | 24 | Developed by Aodaruma ([@Aodaruma_](https://twitter.com/Aodaruma_)) 25 | 26 | Please tell me if you have any questions or bug issues. 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Aodaruma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/BPMmarker.py: -------------------------------------------------------------------------------- 1 | import re 2 | from math import ceil, floor 3 | from typing import List 4 | import bpy 5 | 6 | bl_info = { 7 | "name": "animation: BPM marker", 8 | "author": "Aodaruma", 9 | "version": (2, 0), 10 | "blender": (3, 0, 0), 11 | "location": "", 12 | "description": "automatically marking beats with BPM", 13 | "warning": "", 14 | "support": "COMMUNITY", 15 | "wiki_url": "https://github.com/Aodaruma/BPMmark_maker", 16 | "tracker_url": "", 17 | "category": "Animation" 18 | } 19 | #################################### 20 | 21 | VERSION = bpy.app.version 22 | 23 | 24 | class BPMmarker_DopesheetPanel(bpy.types.Panel): 25 | bl_label = "BPM marker" 26 | bl_space_type = "DOPESHEET_EDITOR" 27 | bl_region_type = "TOOLS" if bpy.app.version < (2, 80, 0) else "UI" 28 | bl_category = "BPM marker" 29 | 30 | def draw(self, context): 31 | self.layout.operator("aodaruma.bpmmarker", text="Run to Mark") 32 | 33 | 34 | class BPMmarker_GrapheditorPanel(bpy.types.Panel): 35 | bl_label = "BPM marker" 36 | bl_space_type = "GRAPH_EDITOR" 37 | bl_region_type = "TOOLS" if bpy.app.version < (2, 80, 0) else "UI" 38 | bl_category = "BPM marker" 39 | 40 | def draw(self, context): 41 | self.layout.operator("aodaruma.bpmmarker", text="Run to Mark") 42 | 43 | # ------------------------------------------------ # 44 | 45 | 46 | class AODARUMA_OT_BPMmaker(bpy.types.Operator): 47 | bl_idname = "aodaruma.bpmmarker" 48 | bl_label = "BPM marker" 49 | bl_description = "automatically marking beats with BPM" 50 | bl_options = {'REGISTER', 'UNDO'} 51 | 52 | # def execute(self, context): 53 | # print("pushed") 54 | # return {'FINISHED'} 55 | BPM: bpy.props.IntProperty(name="BPM", default=120, min=1) 56 | beat: bpy.props.IntProperty(name="Beats", default=4, min=1, max=256) 57 | start: bpy.props.IntProperty(name="BeginFrame", default=0) 58 | end: bpy.props.IntProperty( 59 | name="EndFrame", default=250) 60 | isClearPreMark: bpy.props.BoolProperty( 61 | name="clear previous marks", default=True) 62 | 63 | def execute(self, context: bpy.types.Context): 64 | scene = context.scene 65 | 66 | bpm = self.BPM 67 | beat = self.beat 68 | start = self.start 69 | end = self.end 70 | fps = scene.render.fps 71 | tms = scene.timeline_markers 72 | 73 | if self.isClearPreMark: 74 | for m in tms: 75 | if re.match(r"\|?[\d]\|?", m.name): 76 | tms.remove(m) 77 | 78 | fpb = 60 * fps / bpm 79 | # print(fps, bpm) 80 | print("frames per beat", fpb) 81 | # frame = scene.frame_start 82 | frame = 0 83 | while frame < end-start: 84 | counter = floor((frame / fpb) % beat) 85 | if VERSION < (2, 80, 0): 86 | tms.new("{m}{c}{m}".format( 87 | c=counter+1, m="|" if counter == 0 else ""), frame+start) 88 | else: 89 | tms.new("{m}{c}{m}".format( 90 | c=counter+1, m="|" if counter == 0 else ""), frame=int(frame+start)) 91 | frame += fpb 92 | 93 | return{'FINISHED'} 94 | 95 | def invoke(self, context, event): 96 | return context.window_manager.invoke_props_dialog(self) 97 | 98 | 99 | classes = [ 100 | AODARUMA_OT_BPMmaker, 101 | BPMmarker_DopesheetPanel, 102 | BPMmarker_GrapheditorPanel 103 | ] 104 | 105 | #################################### 106 | 107 | 108 | def register(): 109 | for c in classes: 110 | bpy.utils.register_class(c) 111 | # bpy.types.VIEW3D_MT_object.append(UI) 112 | print(bl_info["name"]+" registered.") 113 | 114 | 115 | def unregister(): 116 | # bpy.types.VIEW3D_MT_object.remove(UI) 117 | for c in classes: 118 | bpy.utils.unregister_class(c) 119 | print(bl_info["name"]+" unregistered.") 120 | 121 | 122 | if __name__ == "__main__": 123 | register() 124 | 125 | #################################### 126 | 127 | # print("done!") 128 | --------------------------------------------------------------------------------