File size: 4,448 Bytes
ef8bde4
495ab46
ef8bde4
 
 
 
495ab46
ef8bde4
 
 
 
 
495ab46
ef8bde4
495ab46
ef8bde4
 
495ab46
 
ef8bde4
495ab46
ef8bde4
 
495ab46
 
 
 
 
ef8bde4
495ab46
ef8bde4
 
495ab46
ef8bde4
495ab46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ef8bde4
 
495ab46
 
 
 
 
 
 
ef8bde4
495ab46
ef8bde4
495ab46
ef8bde4
495ab46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ef8bde4
495ab46
 
 
ef8bde4
495ab46
 
 
 
 
 
ef8bde4
495ab46
ef8bde4
495ab46
ef8bde4
 
495ab46
 
ef8bde4
495ab46
 
 
 
ef8bde4
495ab46
 
ef8bde4
495ab46
 
 
ef8bde4
 
495ab46
 
 
 
 
 
 
 
ef8bde4
 
 
495ab46
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
"""Trap Style MIDI Transformer"""

import pretty_midi as pm
import numpy as np
import random
from pathlib import Path

class TrapTransformer:
    def __init__(self, bpm=140):
        self.bpm = bpm
        self.beat_duration = 60.0 / bpm

    def extract_bass_notes(self, midi_data):
        """Pull low notes that can become 808s"""
        bass_notes = []
        for track in midi_data.instruments:
            if track.is_drum:
                continue
            for note in track.notes:
                if note.pitch < 48:  # Below C3
                    bass_notes.append(note)
        return bass_notes

    def create_808_bass(self, bass_notes, duration_seconds):
        """Longer sustain + louder = 808 feel"""
        enhanced = []
        for note in bass_notes:
            new_note = pm.Note(
                velocity=min(note.velocity + 20, 127),
                pitch=note.pitch,
                start=note.start,
                end=min(note.end + 0.3, duration_seconds),
            )
            enhanced.append(new_note)
        return enhanced

    def add_hi_hats(self, duration_seconds):
        """Fast hi-hat rolls (trap signature)"""
        hat_track = pm.Instrument(program=0, is_drum=True, name="HiHats")
        t = 0.0
        while t < duration_seconds:
            step = self.beat_duration / 4  # 1/16 notes
            if random.random() < 0.3:  # 30% chance → roll
                rolls = random.choice([4, 8])
                for i in range(rolls):
                    hat_track.notes.append(
                        pm.Note(
                            velocity=random.randint(80, 120),
                            pitch=42,
                            start=t + (i * step / rolls),
                            end=t + ((i + 1) * step / rolls),
                        )
                    )
            else:
                hat_track.notes.append(
                    pm.Note(
                        velocity=random.randint(90, 115),
                        pitch=42,
                        start=t,
                        end=t + step * 0.5,
                    )
                )
            t += step
        return hat_track

    def add_trap_drums(self, duration_seconds):
        """Half-time kicks + snares"""
        drum_track = pm.Instrument(program=0, is_drum=True, name="TrapDrums")
        bar = self.beat_duration * 2  # Half-time bar
        num_bars = int(duration_seconds / bar)

        for b in range(num_bars):
            kick_t = b * bar
            snare_t = b * bar + self.beat_duration  # Snare on beat 3 (half-time)

            drum_track.notes.append(
                pm.Note(velocity=120, pitch=36, start=kick_t, end=kick_t + 0.1)
            )
            drum_track.notes.append(
                pm.Note(velocity=110, pitch=38, start=snare_t, end=snare_t + 0.08)
            )
        return drum_track

    def apply_sidechain(self, midi_data, kick_times):
        """Simulate sidechain pump — duck melody before kicks"""
        for instrument in midi_data.instruments:
            if instrument.is_drum:
                continue
            for note in instrument.notes:
                for kt in kick_times:
                    if note.start <= kt <= note.end:
                        note.velocity = max(1, note.velocity - 10)
        return midi_data

    def transform_to_trap(self, input_midi_path, output_midi_path=None):
        """Main: transform any MIDI → Trap"""
        midi_data = pm.PrettyMIDI(str(input_midi_path))
        duration = midi_data.get_end_time()

        # Enhance existing bass
        bass_notes = self.extract_bass_notes(midi_data)
        enhanced_bass = self.create_808_bass(bass_notes, duration)

        # Add new drum + hat tracks
        hat_track = self.add_hi_hats(duration)
        drum_track = self.add_trap_drums(duration)

        # Sidechain
        kick_times = [n.start for n in drum_track.notes if n.pitch == 36]
        midi_data = self.apply_sidechain(midi_data, kick_times)

        # Append trap layers
        midi_data.instruments.append(hat_track)
        midi_data.instruments.append(drum_track)

        # Set tempo
        try:
            for tempo_change in midi_data._tempo_changes:
                tempo_change.tempo = self.bpm
        except Exception:
            pass

        if output_midi_path:
            midi_data.write(str(output_midi_path))

        return midi_data