예제 #1
0
파일: instrument.py 프로젝트: tnason/NetSeq
    def __init__(self, music_player, wavetype=LEAD, volume=.7, reverb_mix=.1, 
                 notes=None):
        """ Create new WaveInstrument

        Arguments
        music_player: Parent MusicPlayer of this instrument
        
        """

        Instrument.__init__(self, music_player, volume, reverb_mix, notes)

        self.names = ["C", "B", "A", "G", "F", "E", "D", "C"] 

        self.wavetype = wavetype
        if (self.wavetype == self.BASS):
            table = SquareTable()
            self.frequencies = [(freq / 4) for freq in self.C_FREQUENCIES]
        elif (self.wavetype == self.LEAD):
            table = CosTable()
            self.frequencies = self.C_FREQUENCIES

        self.generator_cutoff = True

        # Generate oscillators for every pitch, feed into mixer
        self.oscillators = []
        for i in range(0, music_player.NUM_ROWS):
            oscillator = Osc(table=table, freq=self.frequencies[i])
            oscillator.stop()
            self.row_generators.append(oscillator)
            self.mixer.addInput(i, oscillator)
            self.mixer.setAmp(i, 0, (1.0 / self.LIMITING_FACTOR))
     
        pre_effect = self.mixer[0]

        if (self.wavetype == self.BASS):
            distorted = Disto(pre_effect)
	    volume_reducer = Mixer(outs=1)
	    volume_reducer.addInput(0, distorted)
	    volume_reducer.setAmp(0, 0, 0.3)
	    post_effect = volume_reducer[0]
        elif (self.wavetype == self.LEAD):
            #chorus = Chorus(pre_effect)
            #post_effect = chorus
	    post_effect = pre_effect
        else:
	    post_effect = pre_effect
   
        # Apply reverb to omixer
        self.reverb_out = WGVerb(post_effect, feedback=0.8, cutoff=3500, 
                                 bal=self.reverb_mix)

        #use generator.setBal(x) to modify reverb
        self.generator = self.reverb_out
예제 #2
0
 def mixer_setup(self):
     # Combine all tracks in mixer
     self.track_mixer = Mixer(outs=1)
     for inst_index in range(0, len(self.instruments)):
         instrument = self.instruments[inst_index]
         generator = instrument.get_generator()
         self.track_mixer.addInput(inst_index, generator)
         self.track_mixer.setAmp(inst_index, 0, instrument.get_volume())
     
     # Prepare master output
     self.master_out = Mixer(outs=1, chnls=2)
     self.master_out.addInput(0, self.track_mixer[0])
     self.master_out.setAmp(0, 0, self.global_volume)
     self.master_out.out()
예제 #3
0
파일: instrument.py 프로젝트: tnason/NetSeq
    def __init__(self, music_player, volume, reverb_mix, notes):
        """ Create generic Instrument

        Arguments:
        music_player: Parent MusicPlayer of this instrument
        """

        self.music_player = music_player
        self.reverb_mix = reverb_mix

        # This field is just for storage. Volume is taken care of in
        # the MusicPlayer's mixer
        self.volume = volume

        # Initialize notes array to all zeroes if no notes input as argument
        if notes == None:
            self.notes = []
            for beat_index in range(0, music_player.NUM_BEATS):
                beat_column = []
                for row_index in range(0, music_player.NUM_ROWS):
                    beat_column.append(0)
                self.notes.append(beat_column)
        else:
            self.notes = notes

        # For generating sound
        self.row_generators = []
        self.reverb_out = None
        self.mixer = Mixer(outs=1)

        # For GUI purposes, override this
        self.names = []
예제 #4
0
 def add_modules_to_mixer(self, mixer: pyo.Mixer) -> None:
     for module in self.modules:
         for (
                 physical_output,
                 mixer_channel,
         ) in settings.MODULE_MIXER2CHANNEL_MAPPING.items():
             track_mixer_number = (
                 settings.TRACK2MIXER_NUMBER_MAPPING["{}_{}".format(
                     module.name,
                     physical_output.split("_")[1])], )
             mixer.addInput(
                 track_mixer_number,
                 module.mixer[mixer_channel][0],
             )
             mixer.setAmp(
                 track_mixer_number,
                 settings.PHYSICAL_OUTPUT2CHANNEL_MAPPING[physical_output],
                 1,
             )
예제 #5
0
파일: instrument.py 프로젝트: tnason/NetSeq
class Instrument:
    """This class holds the sound generator and playback parameters for a track"""

    SECONDS_PER_MIN = 60

    def __init__(self, music_player, volume, reverb_mix, notes):
        """ Create generic Instrument

        Arguments:
        music_player: Parent MusicPlayer of this instrument
        """

        self.music_player = music_player
        self.reverb_mix = reverb_mix

        # This field is just for storage. Volume is taken care of in
        # the MusicPlayer's mixer
        self.volume = volume

        # Initialize notes array to all zeroes if no notes input as argument
        if notes == None:
            self.notes = []
            for beat_index in range(0, music_player.NUM_BEATS):
                beat_column = []
                for row_index in range(0, music_player.NUM_ROWS):
                    beat_column.append(0)
                self.notes.append(beat_column)
        else:
            self.notes = notes

        # For generating sound
        self.row_generators = []
        self.reverb_out = None
        self.mixer = Mixer(outs=1)

        # For GUI purposes, override this
        self.names = []

    def __del__(self):
        pass

    def get_generator(self):
        return self.generator

    def get_names(self):
        return self.names

    def pause(self):
        self.mixer.stop()

    def set_note(self, note):
        beat_index = note.page_index * self.music_player.NUM_COLS +\
                     note.column
        beat_col = self.notes[beat_index]
        if note.turn_on == True:
            beat_col[note.row] = 1
        elif note.turn_on == False:
            beat_col[note.row] = 0

    def get_volume(self):
        return self.volume

    def set_volume(self, new_volume):
        self.volume = new_volume

    def get_reverb(self):
        return self.reverb_mix
    
    def set_reverb(self, new_reverb):
        self.reverb_mix = new_reverb    
        self.reverb_out.setBal(new_reverb)

    def get_page(self, page_index):
        """ Get a page of notes with <row, col> indexing """
        start_column = page_index * self.music_player.NUM_COLS
        end_column = start_column + (self.music_player.NUM_COLS - 1)

        page_notes = []
        for row_index in range(0, self.music_player.NUM_ROWS):
            row_notes = []
            for col_index in range(start_column, end_column + 1):
                row_notes.append(self.notes[col_index][row_index])
            page_notes.append(row_notes)
        
        return page_notes

    """Abstract methods: override these for your instrument"""
    def play_step(self):
        pass

    def get_data(self):
        pass
예제 #6
0
class MusicPlayer:
    """Playback engine for sequencer samples and sounds"""

    NUM_PAGES = 8
    NUM_ROWS = 8
    NUM_COLS = 8
    NUM_TRACKS = 3
    NUM_BEATS = NUM_PAGES * NUM_COLS

    SECONDS_PER_MIN = 60.0

    # Parameters for GUI to build sliders
    MIN_TEMPO = 40.0
    MAX_TEMPO = 240.0
    MIN_VOLUME = 0.0
    MAX_VOLUME = 1.0
    MIN_REVERB = 0.0
    MAX_REVERB = 1.0 

    # Instrument descriptive constants
    WAVETABLE_A = 0
    WAVETABLE_B = 1
    DRUM_KIT = 2

    def __init__(self):
        """Constructor for music_player"""
        """Make sure to call add_gui once initialized"""
        self.instruments = [] #instrument/track volume is here
        self.tempo = 120.0 #BPM (for now)
        self.global_volume = 0.75 #between 0 and 1
        self.page_index = 0 #1st page
        self.play_all = False
        self.playhead_index = 0
        self.beat_index = 0

        self.server = Server(duplex=0)
        """Set proper output device for latency-free playback on Windows"""
        """Source: https://groups.google.com/d/msg/pyo-discuss/9fvFiGbch3c/tzJTfbpLUY8J"""
        if platform.system() == "Windows":
            out_devices = pa_get_output_devices()
            od_index = 0
            for od in out_devices[0]:
                if "Primary Sound Driver" in od:
                    pai = int(out_devices[1][od_index])
                    self.server.setOutputDevice(pai)
                    break
                od_index += 1

        self.server.boot()
        self.server.start()

        metronome_time = self.SECONDS_PER_MIN / self.tempo
        self.metronome = Metro(time=metronome_time)
        self.metronome_callback = TrigFunc(self.metronome, function=self.step)
        
        # Create instruments
        wavetable_a = WaveInstrument(self, WaveInstrument.BASS)
        wavetable_b = WaveInstrument(self, WaveInstrument.LEAD)
        drums = DrumInstrument(self)
        
        self.instruments.append(wavetable_a)
        self.instruments.append(wavetable_b)
        self.instruments.append(drums)

        self.mixer_setup()
    
    def mixer_setup(self):
        # Combine all tracks in mixer
        self.track_mixer = Mixer(outs=1)
        for inst_index in range(0, len(self.instruments)):
            instrument = self.instruments[inst_index]
            generator = instrument.get_generator()
            self.track_mixer.addInput(inst_index, generator)
            self.track_mixer.setAmp(inst_index, 0, instrument.get_volume())
        
        # Prepare master output
        self.master_out = Mixer(outs=1, chnls=2)
        self.master_out.addInput(0, self.track_mixer[0])
        self.master_out.setAmp(0, 0, self.global_volume)
        self.master_out.out()

    def add_gui(self, gui):
        """ Sets the GUI that this music player must instruct to update playhead.
        Must be called right after constructor before operation.

        Arguments:
        gui: GUI object monitoring this player

        """
        self.gui = gui
    
    def terminate(self):
        """Terminate MusicPlayer server in preparation for shutdown"""
        self.server.stop()
        self.server.shutdown()

    def step(self):
        """ Step the music player through next beat """
        # Set GUI to reflect current beat
        self.gui.update_playhead()
        
        # Play step for instruments
        for instrument in self.instruments:
            instrument.play_step()

        # For next iteration, increment playhead and beat indices
        self.playhead_index = (self.playhead_index + 1) % self.NUM_COLS
        if (self.play_all == True):
            self.beat_index = (self.beat_index + 1) % self.NUM_BEATS
        elif (self.play_all == False):
            self.beat_index = (self.page_index * self.NUM_COLS) +\
                              self.playhead_index

    def add_network_handler(self, network_handler):
        self.network_handler = network_handler
        
    """playback methods"""
    def play(self):
        self.metronome.play()

    def pause(self):
        for instrument in self.instruments:
            instrument.pause()
        self.metronome.stop()

    def set_session(self, session):
        """used to load a session into the music player"""
        # Reload pertinent MusicPlayer variables
        self.set_tempo(session.tempo)

        # Reconstruct each instrument from session data
        self.instruments = []
        instrument_data = session.instrument_data
        for data in instrument_data:
            volume = data.volume
            reverb_mix = data.reverb_mix
            notes = data.notes
            if isinstance(data, WaveInstrumentData):
                wavetype = data.wavetype
                wave_instrument = WaveInstrument(self, wavetype, volume, 
                                                 reverb_mix, notes)
                self.instruments.append(wave_instrument)
            elif isinstance(data, DrumInstrumentData):
                drum_instrument = DrumInstrument(self, volume, reverb_mix,
                                                 notes)
                self.instruments.append(drum_instrument)

        # Reload mixer to reflect new instruments
        self.mixer_setup()

    """ Modifiers """
    def set_note(self, note):
        instrument = self.instruments[note.track_id]
        instrument.set_note(note)

    def set_global_volume(self, volume):
        self.global_volume = volume
        self.master_out.setAmp(0, 0, volume)

    def set_volume(self, track_id, volume):
        self.instruments[track_id].set_volume(volume)
        self.track_mixer.setAmp(track_id, 0, volume)
    
    def set_reverb(self, track_id, reverb):
        self.instruments[track_id].set_reverb(reverb)

    def set_tempo(self, new_tempo):
        new_time = self.SECONDS_PER_MIN / new_tempo
        self.metronome.setTime(new_time)
        self.tempo = new_tempo

    """getter methods"""
    def get_session(self):
        """Get descriptive MusicPlayer session to restore later"""
        session = Session(self, self.instruments)
        return session

    """getter methods for GUI"""
    def get_names(self, track_id):
        return self.instruments[track_id].get_names()

    def get_reverb(self, track_id):
        return self.instruments[track_id].get_reverb()

    def get_volume(self, track_id):
        return self.instruments[track_id].get_volume()

    def get_global_volume(self):
        return self.global_volume

    def get_tempo(self):
        return self.tempo

    def get_note(self, track_id, page_index, position, pitch):
        pass

    def get_current_page(self, track_id):
        instrument = self.instruments[track_id]
        notes = instrument.get_page(self.page_index)
        return notes