theremin-pi/theremin/lib/fluidsynth.rb

191 lines
3.4 KiB
Ruby
Raw Permalink Normal View History

# -*- ruby -*-
# vim: set noet sta sw=4 ts=4 :
require 'socket'
require 'configurability'
require 'loggability'
# Communicate with a running FluidSynth daemon via it's TCP socket API.
#
class FluidSynth
extend Configurability,
Loggability
# The default host for the fluidsynth server.
DEFAULT_HOST = 'localhost'
# The default port for the fluidsynth server.
DEFAULT_PORT = 9800
# Loggability API
log_as :fluidsynth
# Configuration API
#
configurability( :fluidsynth ) do
##
# How many simultaneous notes can play. Keep low for better CPU.
setting :polyphony, default: 6
##
# Reverb amount.
setting :reverb_size, default: 0.9
##
# Reverb gain level.
setting :reverb_gain, default: 0.7
##
# Chorus depth.
setting :chorus_depth, default: 64.0
##
# Chorus gain level.
setting :chorus_gain, default: 1.5
end
### Instance a new fluidsynth interface.
###
def initialize( host=DEFAULT_HOST, port=DEFAULT_PORT )
@host = host
@port = port
@lastnote = nil
@velocity = 80
end
# The TCP socket to fluidsynth.
attr_reader :socket
# The previous note played, to enable glides.
attr_reader :lastnote
# The current velocity to play new notes at.
attr_accessor :velocity
### Open the TCP socket to the synth and perform initial setups.
###
def connect
@socket = TCPSocket.new( @host, @port )
self.setup_effects
self.polyphony( self.class.polyphony )
end
### Setup polyphony amount.
###
def polyphony( val )
self.send "synth.polyphony #{val}"
end
### Stop all notes and reset effects.
###
def panic
self.send "reset"
end
### Alter the default volume.
###
def gain( level )
self.send "gain #{level}"
end
### Select a loaded soundfont and instrument by index.
### (Always using channel 0.)
###
def instrument( font_idx, instrument_idx )
self.send "select 0 #{font_idx} 0 #{instrument_idx}"
end
### Turn glide (portamento) on or off. Val is an integer that
### represents 128ms. 4 == 512ms.
###
def glide( val )
if val.zero?
self.send "cc 0 68 0"
self.send "cc 0 65 0"
self.send "cc 0 5 0"
else
self.send "setlegatomode 0 1"
self.send "cc 0 68 127" # legato
self.send "cc 0 65 127" # portamento
self.send "cc 0 5 #{val}"
end
end
### Play a midi +note+ at +velocity+, then silence the previous note.
### (This allows portamento to function if enabled.)
###
def playnote( note, velocity=self.velocity )
return if @lastnote == note
self.send "noteon 0 #{note} #{velocity}"
self.stopnote
@lastnote = note
end
### Silences the previous note played.
###
def stopnote
return unless @lastnote
self.send "noteoff 0 #{@lastnote}"
end
### Loads a resource (sf2, etc) from +path+.
###
def load( path )
self.send "load #{path}"
end
### Enable/disable reverb.
###
def reverb( toggle=true )
toggle = toggle ? 1 : 0
self.send "set synth.reverb.active #{toggle}"
end
### Enable/disable chorus.
###
def chorus( toggle=true )
toggle = toggle ? 1 : 0
self.send "set synth.chorus.active #{toggle}"
end
#########
protected
#########
### Initial effect setup.
###
def setup_effects
self.send "set synth.reverb.room-size 0.9"
self.send "set synth.reverb.level 0.7"
self.send "set synth.chorus.level 1.5"
self.send "set synth.chorus.depth 64.0"
end
### Issue a command to the synth, after possible logging.
###
def send( cmd )
self.log.debug( cmd )
self.socket.puts( cmd )
end
end