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

#!/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.")