Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinesh0N authored Nov 13, 2024
1 parent d07cf39 commit 30a85e0
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 0 deletions.
284 changes: 284 additions & 0 deletions Addon-vse_audioexpoter- v4.2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
bl_info = {
"name": "Audio Exporter",
"description": "Export audio with custom options, channel mapping, and speaker arrangements.",
"author": "Dinesh",
"version": (1, 0, 0),
"blender": (2, 8, 0),
"category": "Sequencer",
"location": "Sequencer > AudioExport(Tab)",
}

import bpy
import os
import time

# Operator for exporting audio file
class ExportAudioOperator(bpy.types.Operator):
bl_idname = "sequencer.export_audio_file"
bl_label = "Export Audio File"
bl_icon = 'EXPORT'
bl_description = "Exports selected audio strips with custom settings"

def execute(self, context):
scene = context.scene

# Get the export mode (separate or combined)
export_mode = scene.export_audio_mode

# Store the original mute states of the strips
original_mute_states = {}
for strip in scene.sequence_editor.sequences_all:
if strip.type == 'SOUND':
original_mute_states[strip.channel] = strip.mute

# Get the selected audio strips
selected_strips = [strip for strip in scene.sequence_editor.sequences_all if strip.type == 'SOUND' and strip.select]

if not selected_strips:
self.report({'WARNING'}, "No audio strips selected.")
return {'CANCELLED'}

# Verify that the output path exists
if not os.path.isdir(scene.export_audio_output_path):
self.report({'ERROR'}, "Invalid output path. Please set a valid directory.")
return {'CANCELLED'}

# Helper function to check if a file exists and append a unique counter if necessary
def check_and_generate_filename(file_path):
base_name, ext = os.path.splitext(file_path)
counter = 1
# Loop to ensure the file name is unique
while os.path.exists(file_path):
file_path = f"{base_name}_{counter}{ext}"
counter += 1
return file_path

# Process based on export mode
if export_mode == 'SEPARATE':
# Auto-numbering counter
counter = 1
for strip in selected_strips:
output_path = scene.export_audio_output_path
audio_container = scene.export_audio_container
file_name = f"{strip.name}_{counter}.{audio_container.lower()}"
output_file_path = os.path.join(output_path, file_name)

# Check if file exists and modify the filename if necessary
output_file_path = check_and_generate_filename(output_file_path) # Specific comment line for file existence check

counter += 1 # Increment counter for each separate file

# Set sample rate and bit rate
sample_rate = scene.export_audio_sample_rate
bit_rate = scene.export_audio_bit_rate

# Set render settings
scene.render.image_settings.file_format = 'FFMPEG'
scene.render.ffmpeg.audio_codec = audio_container
scene.render.ffmpeg.audio_bitrate = bit_rate

# Delete existing output file if it exists
if os.path.exists(output_file_path):
os.remove(output_file_path)

# Mute all other strips and set frame range for export
for other_strip in scene.sequence_editor.sequences_all:
if other_strip != strip and other_strip.type == 'SOUND':
other_strip.mute = True
else:
other_strip.mute = False

# Set the frame range for rendering based on the selected strip's duration
scene.frame_start = int(strip.frame_start) # Convert to int
scene.frame_end = int(strip.frame_final_end) # Use frame_final_end for the correct end frame

# Render audio strip
bpy.ops.sound.mixdown(
filepath=output_file_path,
codec=audio_container,
container=audio_container,
)

elif export_mode == 'COMBINED':
combined_file_name = "Combined_Audio." + scene.export_audio_container.lower()
output_file_path = os.path.join(scene.export_audio_output_path, combined_file_name)

# Check if file exists and modify the filename if necessary
output_file_path = check_and_generate_filename(output_file_path) # Specific comment line for file existence check

sample_rate = scene.export_audio_sample_rate
bit_rate = scene.export_audio_bit_rate

scene.render.image_settings.file_format = 'FFMPEG'
scene.render.ffmpeg.audio_codec = scene.export_audio_container
scene.render.ffmpeg.audio_bitrate = bit_rate

if os.path.exists(output_file_path):
os.remove(output_file_path)

for strip in scene.sequence_editor.sequences_all:
if strip.type == 'SOUND' and strip.select:
strip.mute = False
else:
strip.mute = True

combined_start = min(int(strip.frame_start) for strip in selected_strips)
combined_end = max(int(strip.frame_final_end) for strip in selected_strips)

scene.frame_start = combined_start
scene.frame_end = combined_end

bpy.ops.sound.mixdown(
filepath=output_file_path,
codec=scene.export_audio_container,
container=scene.export_audio_container,
)

# Restore the original mute states of the strips
for strip in scene.sequence_editor.sequences_all:
if strip.type == 'SOUND':
strip.mute = original_mute_states.get(strip.channel, False)

self.report({'INFO'}, "Audio Files Exported Successfully!")
return {'FINISHED'}

# Panel for the Audio Export tab with Channel Mapping
class AudioExportPanel(bpy.types.Panel):
bl_idname = "SEQUENCER_PT_audio_export_panel"
bl_label = "Audio Properties"
bl_space_type = 'SEQUENCE_EDITOR'
bl_region_type = 'UI'
bl_category = 'Audio Export'

def draw(self, context):
layout = self.layout
scene = context.scene

# File output path
layout.label(text="Output Path:")
row = layout.row(align=True)
row.prop(context.scene, "export_audio_output_path", text="", icon='FILE_FOLDER')

# Audio container dropdown
layout.label(text="Audio Container:")
row = layout.row(align=True)
row.prop(context.scene, "export_audio_container", text="", icon='SOUND')

# Sample Rate
layout.label(text="Sample Rate (Hz):")
layout.prop(context.scene, "export_audio_sample_rate", text="")

# Bit Rate
layout.label(text="Bit Rate (kbps):")
layout.prop(context.scene, "export_audio_bit_rate", text="")

# Export Mode: Separate or Combined
layout.label(text="Export Mode:")
row = layout.row(align=True)
row.prop(context.scene, "export_audio_mode", text="")

# Channel Mapping section (only show when 'COMBINED' is selected)
if scene.export_audio_mode == 'COMBINED':
layout.separator()

# Speaker Arrangement Dropdown (Stereo, 5.1, 7.1)
layout.label(text="Speaker Arrangement:")
row = layout.row(align=True)
row.prop(context.scene, "export_audio_speaker_arrangement", text="", icon='SPEAKER')

# Advanced Channel Mapping Options
layout.label(text="Advanced Channel Mapping:")
for i in range(8): # Max 8 channels
row = layout.row(align=True)
row.prop(context.scene, f"export_audio_channel_{i+1}", text=f"{i+1} Channel ")

# Export Button
layout.operator("sequencer.export_audio_file", text="Export Audio", icon='EXPORT')

# Register the classes
classes = [
ExportAudioOperator,
AudioExportPanel,
]

def register():
for cls in classes:
bpy.utils.register_class(cls)

# Define properties directly with bpy.props
bpy.types.Scene.export_audio_output_path = bpy.props.StringProperty(
name="Output Path",
default="",
subtype='DIR_PATH',
)
bpy.types.Scene.export_audio_sample_rate = bpy.props.IntProperty(
name="Sample Rate",
default=44100,
min=1,
)
bpy.types.Scene.export_audio_bit_rate = bpy.props.IntProperty(
name="Bit Rate",
default=192,
min=1,

)
bpy.types.Scene.export_audio_container = bpy.props.EnumProperty(
name="Audio Container",
items=[
('MP3', 'MP3', 'MP3 audio format'),
('FLAC', 'FLAC', 'FLAC audio format'),
('AC3', 'AC3', 'AC3 audio format'),
('MP2', 'MP2', 'MP2 audio format'),
],
default='MP3',
)
bpy.types.Scene.export_audio_mode = bpy.props.EnumProperty(
name="Export Mode",
items=[
('SEPARATE', 'Separate Files', 'Export each audio strip separately'),
('COMBINED', 'Combined File', 'Combine selected audio strips into one file'),
],
default='SEPARATE',
)
bpy.types.Scene.export_audio_speaker_arrangement = bpy.props.EnumProperty(
name="Speaker Arrangement",
items=[
('STEREO', 'Stereo', '2 channels (Left, Right)'),
('SURROUND_5_1', '5.1 Surround', '6 channels (Front Left, Front Right, Center, Subwoofer, Rear Left, Rear Right)'),
('SURROUND_7_1', '7.1 Surround', '8 channels (Front Left, Front Right, Center, Subwoofer, Rear Left, Rear Right, Side Left, Side Right)'),
],
default='STEREO',
)

# Default different types for each channel (from 1 to 8)
default_speaker_mapping = ['LEFT', 'RIGHT', 'CENTER', 'SUBWOOFER', 'REAR_LEFT', 'REAR_RIGHT', 'SIDE_LEFT', 'SIDE_RIGHT']
for i, speaker_type in enumerate(default_speaker_mapping):
setattr(bpy.types.Scene, f"export_audio_channel_{i+1}", bpy.props.EnumProperty(
name=f"Channel {i+1}",
items=[
('LEFT', 'Left', 'Left speaker'),
('RIGHT', 'Right', 'Right speaker'),
('CENTER', 'Center', 'Center speaker'),
('SUBWOOFER', 'Subwoofer', 'Subwoofer speaker'),
('REAR_LEFT', 'Rear Left', 'Rear Left speaker'),
('REAR_RIGHT', 'Rear Right', 'Rear Right speaker'),
('SIDE_LEFT', 'Side Left', 'Side Left speaker'),
('SIDE_RIGHT', 'Side Right', 'Side Right speaker'),
],
default=speaker_type,
))
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)

del bpy.types.Scene.export_audio_output_path
del bpy.types.Scene.export_audio_sample_rate
del bpy.types.Scene.export_audio_bit_rate
del bpy.types.Scene.export_audio_container
del bpy.types.Scene.export_audio_mode
del bpy.types.Scene.export_audio_speaker_arrangement
for i in range(8):
delattr(bpy.types.Scene, f"export_audio_channel_{i+1}")

if __name__ == "__main__":
register()
12 changes: 12 additions & 0 deletions Addon-vse_audioexpoter- v4.2/blender_manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
schema_version = "1.0.0"
id = "audio_exporter"
name = "Audio Exporter"
version = "1.0.0"
tagline = "ExportAudio with customOptions, ChannelMapping, SpeakerArrangements."
maintainer = "Dinesh"
type = "add-on"
tags = ["Sequencer"]
blender_version_min = "4.2.0"
license = ["SPDX:GPL-3.0-or-later"]
website = "https://extensions.blender.org/author/139/"
copyright = ["2024 Dinesh"]

0 comments on commit 30a85e0

Please sign in to comment.