You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
6.4 KiB
196 lines
6.4 KiB
#!/usr/bin/env python3
|
|
"""
|
|
Windows installation script with fallback options for problematic packages.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
|
|
def run_pip_install(packages):
|
|
"""Run pip install with error handling"""
|
|
for package in packages:
|
|
try:
|
|
print(f"Installing {package}...")
|
|
result = subprocess.run([sys.executable, '-m', 'pip', 'install', package],
|
|
capture_output=True, text=True, check=True)
|
|
print(f"✓ Successfully installed {package}")
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"✗ Failed to install {package}: {e}")
|
|
print("STDOUT:", e.stdout)
|
|
print("STDERR:", e.stderr)
|
|
return False
|
|
return True
|
|
|
|
def install_dependencies():
|
|
"""Install dependencies with Windows-specific handling"""
|
|
|
|
# Core packages that usually install fine
|
|
core_packages = [
|
|
"PyQt5>=5.15.0",
|
|
"numpy>=1.21.0",
|
|
"pygame>=2.1.0",
|
|
"mido>=1.2.10"
|
|
]
|
|
|
|
print("Installing core packages...")
|
|
if not run_pip_install(core_packages):
|
|
print("Failed to install some core packages")
|
|
return False
|
|
|
|
# Try to install python-rtmidi with different approaches
|
|
rtmidi_packages = [
|
|
"python-rtmidi>=1.5.8", # Newer version with better Windows support
|
|
"python-rtmidi", # Latest version
|
|
"rtmidi-python>=1.1.0" # Alternative package
|
|
]
|
|
|
|
rtmidi_installed = False
|
|
for package in rtmidi_packages:
|
|
print(f"Trying to install {package}...")
|
|
if run_pip_install([package]):
|
|
rtmidi_installed = True
|
|
break
|
|
print(f"Failed to install {package}, trying next option...")
|
|
|
|
if not rtmidi_installed:
|
|
print("\n⚠️ Warning: Could not install any RTMIDI package.")
|
|
print("The application will still work in simulator mode.")
|
|
print("For hardware MIDI support, you may need to:")
|
|
print("1. Install Visual Studio Build Tools")
|
|
print("2. Try: pip install --no-cache-dir python-rtmidi")
|
|
print("3. Or use the simulator mode only")
|
|
|
|
print("\n✓ Installation completed!")
|
|
return True
|
|
|
|
def create_fallback_rtmidi():
|
|
"""Create a fallback rtmidi module using pygame.midi"""
|
|
fallback_code = '''"""
|
|
Fallback RTMIDI implementation using pygame.midi for Windows compatibility.
|
|
"""
|
|
|
|
import pygame.midi
|
|
import time
|
|
from typing import List, Optional, Callable
|
|
|
|
class MidiOut:
|
|
def __init__(self, device_id):
|
|
pygame.midi.init()
|
|
self.device_id = device_id
|
|
self.midi_out = pygame.midi.Output(device_id)
|
|
|
|
def send_message(self, message):
|
|
"""Send MIDI message"""
|
|
if hasattr(message, 'bytes'):
|
|
# mido message
|
|
data = message.bytes()
|
|
else:
|
|
# Raw bytes
|
|
data = message
|
|
|
|
if len(data) == 3:
|
|
self.midi_out.write_short(data[0], data[1], data[2])
|
|
elif len(data) == 2:
|
|
self.midi_out.write_short(data[0], data[1])
|
|
|
|
def close(self):
|
|
if hasattr(self, 'midi_out'):
|
|
self.midi_out.close()
|
|
|
|
class MidiIn:
|
|
def __init__(self, device_id, callback=None):
|
|
pygame.midi.init()
|
|
self.device_id = device_id
|
|
self.midi_in = pygame.midi.Input(device_id)
|
|
self.callback = callback
|
|
|
|
def set_callback(self, callback):
|
|
self.callback = callback
|
|
|
|
def poll(self):
|
|
"""Poll for MIDI input (call this regularly)"""
|
|
if self.midi_in.poll() and self.callback:
|
|
midi_events = self.midi_in.read(10)
|
|
for event in midi_events:
|
|
# Convert pygame midi event to mido-like message
|
|
if self.callback:
|
|
self.callback(event)
|
|
|
|
def close(self):
|
|
if hasattr(self, 'midi_in'):
|
|
self.midi_in.close()
|
|
|
|
def get_output_names() -> List[str]:
|
|
"""Get available MIDI output device names"""
|
|
pygame.midi.init()
|
|
devices = []
|
|
for i in range(pygame.midi.get_count()):
|
|
info = pygame.midi.get_device_info(i)
|
|
if info[3]: # is_output
|
|
devices.append(info[1].decode())
|
|
return devices
|
|
|
|
def get_input_names() -> List[str]:
|
|
"""Get available MIDI input device names"""
|
|
pygame.midi.init()
|
|
devices = []
|
|
for i in range(pygame.midi.get_count()):
|
|
info = pygame.midi.get_device_info(i)
|
|
if info[2]: # is_input
|
|
devices.append(info[1].decode())
|
|
return devices
|
|
|
|
def open_output(name: str) -> MidiOut:
|
|
"""Open MIDI output by name"""
|
|
pygame.midi.init()
|
|
for i in range(pygame.midi.get_count()):
|
|
info = pygame.midi.get_device_info(i)
|
|
if info[3] and info[1].decode() == name: # is_output and name matches
|
|
return MidiOut(i)
|
|
raise ValueError(f"MIDI output '{name}' not found")
|
|
|
|
def open_input(name: str, callback=None) -> MidiIn:
|
|
"""Open MIDI input by name"""
|
|
pygame.midi.init()
|
|
for i in range(pygame.midi.get_count()):
|
|
info = pygame.midi.get_device_info(i)
|
|
if info[2] and info[1].decode() == name: # is_input and name matches
|
|
return MidiIn(i, callback)
|
|
raise ValueError(f"MIDI input '{name}' not found")
|
|
'''
|
|
|
|
# Create fallback directory
|
|
fallback_dir = os.path.join(os.path.dirname(__file__), 'fallback')
|
|
os.makedirs(fallback_dir, exist_ok=True)
|
|
|
|
# Write fallback rtmidi module
|
|
with open(os.path.join(fallback_dir, 'rtmidi_fallback.py'), 'w') as f:
|
|
f.write(fallback_code)
|
|
|
|
with open(os.path.join(fallback_dir, '__init__.py'), 'w') as f:
|
|
f.write('# Fallback MIDI implementations')
|
|
|
|
if __name__ == "__main__":
|
|
print("Windows MIDI Arpeggiator Installation Script")
|
|
print("=" * 50)
|
|
|
|
# Update pip first
|
|
print("Updating pip...")
|
|
subprocess.run([sys.executable, '-m', 'pip', 'install', '--upgrade', 'pip'],
|
|
capture_output=True)
|
|
|
|
# Install dependencies
|
|
success = install_dependencies()
|
|
|
|
# Create fallback MIDI implementation
|
|
create_fallback_rtmidi()
|
|
|
|
if success:
|
|
print("\n🎉 Installation completed successfully!")
|
|
print("\nYou can now run the application with:")
|
|
print(" python run.py")
|
|
print("\nIf you have MIDI hardware issues, the app will work in simulator mode.")
|
|
else:
|
|
print("\n⚠️ Installation completed with warnings.")
|
|
print("Some packages failed to install, but the app should still work in simulator mode.")
|