rsynth 0.0.1.alpha0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +3 -0
- data/Rakefile +2 -0
- data/lib/rsynth/combiner.rb +18 -0
- data/lib/rsynth/functions.rb +91 -0
- data/lib/rsynth/notes.rb +101 -0
- data/lib/rsynth/oscillator.rb +32 -0
- data/lib/rsynth/phase_shifter.rb +17 -0
- data/lib/rsynth/pipe/pipeline.rb +84 -0
- data/lib/rsynth/pipe/portaudio.rb +93 -0
- data/lib/rsynth/rsynth.rb +14 -0
- data/lib/rsynth/scales.rb +41 -0
- data/lib/rsynth/version.rb +3 -0
- data/rsynth.gemspec +24 -0
- metadata +107 -0
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module RSynth
|
2
|
+
class Combiner
|
3
|
+
include RSynth::Functions
|
4
|
+
attr_accessor :a, :b
|
5
|
+
|
6
|
+
def initialize(a, b, proc=nil)
|
7
|
+
proc = Proc.new{|a,b| yield a, b} if block_given?
|
8
|
+
raise 'No block or proc given' if proc.nil?
|
9
|
+
@proc = proc
|
10
|
+
@a = a
|
11
|
+
@b = b
|
12
|
+
end
|
13
|
+
|
14
|
+
def value_at(time)
|
15
|
+
@proc.call(@a.value_at(time), @b.value_at(time))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module RSynth
|
2
|
+
module Functions
|
3
|
+
def combine(type, other)
|
4
|
+
case type
|
5
|
+
when :add, :plus
|
6
|
+
RSynth::Combiner.new(self, other) { |a, b| a + b }
|
7
|
+
when :sub, :subtract, :minus
|
8
|
+
RSynth::Combiner.new(self, other) { |a, b| a - b }
|
9
|
+
when :div, :divide
|
10
|
+
RSynth::Combiner.new(self, other) { |a, b| a / b }
|
11
|
+
when :mul, :multiply, :times
|
12
|
+
RSynth::Combiner.new(self, other) { |a, b| a * b }
|
13
|
+
when :exp, :exponent, :pow, :power
|
14
|
+
RSynth::Combiner.new(self, other) { |a, b| a ** b }
|
15
|
+
when :mod, :modulo, :rem, :remainder
|
16
|
+
RSynth::Combiner.new(self, other) { |a, b| a % b }
|
17
|
+
when :mix
|
18
|
+
RSynth::Combiner.new(self, other) do |a, b|
|
19
|
+
# Normalise between 0 and 1
|
20
|
+
a = (a + 1) / 2
|
21
|
+
b = (b + 1) / 2
|
22
|
+
z = (a < 0.5 and b < 0.5)? 2*a*b : 2*(a+b) - (2*a*b) - 1
|
23
|
+
# Convert back
|
24
|
+
(z * 2) - 1
|
25
|
+
end
|
26
|
+
else
|
27
|
+
raise "Unknown combiner type: #{type}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def phase_shift(offset=nil)
|
32
|
+
offset = Proc.new{ |time| yield time } if block_given?
|
33
|
+
raise 'No offset or block given' if offset.nil?
|
34
|
+
RSynth::PhaseShifer.new(self, offset)
|
35
|
+
end
|
36
|
+
|
37
|
+
# won't work for numeric or proc (unless we override but there'd be a huge performance hit)
|
38
|
+
def +(other)
|
39
|
+
combine(:add, other)
|
40
|
+
end
|
41
|
+
|
42
|
+
def -(other)
|
43
|
+
combine(:sub, other)
|
44
|
+
end
|
45
|
+
|
46
|
+
def /(other)
|
47
|
+
combine(:div, other)
|
48
|
+
end
|
49
|
+
|
50
|
+
def *(other)
|
51
|
+
combine(:mul, other)
|
52
|
+
end
|
53
|
+
|
54
|
+
def **(other)
|
55
|
+
combine(:exp, other)
|
56
|
+
end
|
57
|
+
|
58
|
+
def %(other)
|
59
|
+
combine(:mod, other)
|
60
|
+
end
|
61
|
+
|
62
|
+
def &(other)
|
63
|
+
combine(:mix, other)
|
64
|
+
end
|
65
|
+
|
66
|
+
def <<(offset)
|
67
|
+
phase_shift(-offset)
|
68
|
+
end
|
69
|
+
|
70
|
+
def >>(offset)
|
71
|
+
phase_shift(offset)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Proc
|
77
|
+
include RSynth::Functions
|
78
|
+
|
79
|
+
def value_at(time)
|
80
|
+
self.call(time)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add mixins to int and float :O
|
85
|
+
class Numeric
|
86
|
+
include RSynth::Functions
|
87
|
+
|
88
|
+
def value_at(time)
|
89
|
+
self
|
90
|
+
end
|
91
|
+
end
|
data/lib/rsynth/notes.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
module RSynth
|
2
|
+
class Note
|
3
|
+
ChromaticInterval = 2**(1.0/12)
|
4
|
+
ChromaticNotes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
|
5
|
+
IntervalMap = [
|
6
|
+
%w(perfect_unison unison P1 diminished_second d2),
|
7
|
+
%w(minor_second m2 augmented_unison A1 semitone S),
|
8
|
+
%w(major_second M2 diminished_third d3 tone whole_tone T),
|
9
|
+
%w(minor_third m3 augmented_second A2),
|
10
|
+
%w(major_third M3 diminished_fourth d4),
|
11
|
+
%w(perfect_fourth fourth P4 augmented_third A3),
|
12
|
+
%w(tritone diminished_fifth d5 augmented_fourth A4 TT),
|
13
|
+
%w(perfect_fifth fifth P5 diminished_sixth d6),
|
14
|
+
%w(minor_sixth m6 augmented_fifth A5),
|
15
|
+
%w(major_sixth M6 diminished_seventh d7),
|
16
|
+
%w(minor_seventh m7 augmented_sixth A6),
|
17
|
+
%w(major_seventh M7 diminished_octave d8),
|
18
|
+
%w(perfect_octave octave P8 augmented_seventh A7)
|
19
|
+
]
|
20
|
+
include RSynth::Functions
|
21
|
+
|
22
|
+
attr_reader :note, :octave, :freq
|
23
|
+
|
24
|
+
def initialize(note, octave)
|
25
|
+
@note = note.upcase
|
26
|
+
@octave = octave
|
27
|
+
raise "Invalid note value: #{note}" unless ChromaticNotes.include?(@note)
|
28
|
+
raise "Invalid octave: #{octave}, must be >= 0" if octave < 0
|
29
|
+
|
30
|
+
# Calculate frequency (notes are relative to A4 (440Hz))
|
31
|
+
rn = -(ChromaticNotes.index('A') - ChromaticNotes.index(@note))
|
32
|
+
ro = @octave - 4
|
33
|
+
steps = (ro * 12) + rn
|
34
|
+
@freq = 440*(ChromaticInterval**steps)
|
35
|
+
end
|
36
|
+
|
37
|
+
def value_at(time)
|
38
|
+
@freq
|
39
|
+
end
|
40
|
+
|
41
|
+
def next
|
42
|
+
transpose(1)
|
43
|
+
end
|
44
|
+
|
45
|
+
def previous
|
46
|
+
transpose(-1)
|
47
|
+
end
|
48
|
+
|
49
|
+
def previous_octave
|
50
|
+
transpose(-12)
|
51
|
+
end
|
52
|
+
|
53
|
+
def transpose(count=1)
|
54
|
+
oct = @octave
|
55
|
+
ni = ChromaticNotes.index(@note)
|
56
|
+
ni += count
|
57
|
+
while ni >= ChromaticNotes.length
|
58
|
+
oct += 1
|
59
|
+
ni -= ChromaticNotes.length
|
60
|
+
end
|
61
|
+
while ni < 0
|
62
|
+
oct -= 1
|
63
|
+
ni += ChromaticNotes.length
|
64
|
+
end
|
65
|
+
Note.retrieve(ChromaticNotes[ni], oct)
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
"#@note#@octave"
|
70
|
+
end
|
71
|
+
|
72
|
+
def <=>(note)
|
73
|
+
os = @octave <=> note.octave
|
74
|
+
return os unless os == 0
|
75
|
+
ChromaticNotes.index(@note) <=> ChromaticNotes.index(note.note)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.retrieve(note, octave)
|
79
|
+
note.upcase!
|
80
|
+
name = "#{note.sub('#', 'Sharp')}#{octave}"
|
81
|
+
RSynth.const_set(name, Note.new(note, octave)) unless RSynth.const_defined?(name)
|
82
|
+
RSynth.const_get(name)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Define the interval methods...
|
86
|
+
IntervalMap.each_with_index do |names, interval|
|
87
|
+
names.each do |name|
|
88
|
+
define_method(name.to_sym) do
|
89
|
+
self.transpose(interval)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Define the chromatic scale at octaves 0-9
|
96
|
+
9.times do |o|
|
97
|
+
Note::ChromaticNotes.each do |n|
|
98
|
+
Note.retrieve(n, o)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RSynth
|
2
|
+
class Oscillator
|
3
|
+
PI2 = Math::PI*2
|
4
|
+
TableLength = 1024
|
5
|
+
SinTable = TableLength.times.map{|i| Math.sin((PI2 * i) / TableLength)}
|
6
|
+
CosTable = TableLength.times.map{|i| Math.cos((PI2 * i) / TableLength)}
|
7
|
+
|
8
|
+
include RSynth::Functions
|
9
|
+
|
10
|
+
attr_accessor :freq, :func
|
11
|
+
|
12
|
+
def initialize(func, freq)
|
13
|
+
@freq = freq
|
14
|
+
@func = func
|
15
|
+
@phase = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def value_at(time)
|
19
|
+
f = @freq.value_at(time)
|
20
|
+
i = @phase
|
21
|
+
@phase = (@phase + (f.to_f / RSynth::SampleRate)) % 1.0
|
22
|
+
case @func
|
23
|
+
when :sin, :sine then SinTable[(i*TableLength).to_i]
|
24
|
+
when :cos, :cosine then CosTable[(i*TableLength).to_i]
|
25
|
+
when :sq, :square then i <= 0.5? 1: -1
|
26
|
+
when :tri, :triangle then i <= 0.25? (i * 4): i <= 0.5? (1 - (i - 0.25) * 4): i <= 0.75? (0 - (i - 0.5) * 4): (i - 0.75) * 4 - 1
|
27
|
+
when :saw, :sawtooth then i * 2 - 1
|
28
|
+
else @func.value_at(@phase)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RSynth
|
2
|
+
class PhaseShifter
|
3
|
+
include RSynth::Functions
|
4
|
+
attr_accessor :source, :offset
|
5
|
+
|
6
|
+
def initialize(source, offset=nil)
|
7
|
+
offset = Proc.new {|time| yield time} if block_given?
|
8
|
+
raise 'No offset or block defined' if offset.nil?
|
9
|
+
@source = source
|
10
|
+
@offset = offset
|
11
|
+
end
|
12
|
+
|
13
|
+
def value_at(time)
|
14
|
+
@source.value_at(time + @offset.value_at(time))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module RSynth
|
2
|
+
module Pipe
|
3
|
+
class Pipeline
|
4
|
+
NumBuffers = 2 # Number of buffers we keep in our ring buffer
|
5
|
+
FramesPerBuffer = 512 # How many bytes per buffer (higher is higher latency, obviously...)
|
6
|
+
|
7
|
+
attr_accessor :sequence, :source
|
8
|
+
attr_reader :time
|
9
|
+
|
10
|
+
def initialize(source, *sequence)
|
11
|
+
source = Proc.new { |time| yield time } if block_given?
|
12
|
+
raise 'No source or block given' if source.nil?
|
13
|
+
@sequence = sequence || []
|
14
|
+
@source = source
|
15
|
+
@stopped = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def start(time=Float::INFINITY)
|
19
|
+
return unless @stopped
|
20
|
+
@stopped = false
|
21
|
+
@length = time
|
22
|
+
Thread.new {generate}
|
23
|
+
end
|
24
|
+
|
25
|
+
def stop
|
26
|
+
@stopped = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def restart
|
30
|
+
stop
|
31
|
+
start
|
32
|
+
end
|
33
|
+
|
34
|
+
def consume(sequence)
|
35
|
+
sidx = @sequence.index(sequence) || @sequence.length
|
36
|
+
bidx = @seqpos[sidx] || 0
|
37
|
+
@generated[bidx]
|
38
|
+
end
|
39
|
+
|
40
|
+
def consumed(sequence)
|
41
|
+
sidx = @sequence.index(sequence) || @sequence.length
|
42
|
+
bidx = @seqpos[sidx] || 0
|
43
|
+
@seqpos[sidx] = bidx + 1 # increment this sequences buffer index entry...
|
44
|
+
|
45
|
+
# If all sequences have consumed the bottom buffer, release it
|
46
|
+
if @seqpos.all?{|s| s > 0}
|
47
|
+
@buffers.push(@generated.shift)
|
48
|
+
@seqpos.map!{|i| i - 1}
|
49
|
+
@time += FramesPerBuffer * RSynth::TimeStep
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def generate
|
54
|
+
@time = 0
|
55
|
+
@generate_time = 0
|
56
|
+
timePerLoop = FramesPerBuffer * RSynth::TimeStep
|
57
|
+
@buffers = 10.times.map{Array.new(FramesPerBuffer)}
|
58
|
+
@generated = []
|
59
|
+
@seqpos = @sequence.length.times.map{0}
|
60
|
+
@sequence.each { |s| s.start(self) }
|
61
|
+
while not @stopped
|
62
|
+
if @generate_time < @length
|
63
|
+
sleep(RSynth::TimeStep) while (@buffers.length == 0 or @generated.length >= NumBuffers) and not @stopped
|
64
|
+
buf = @buffers.shift
|
65
|
+
len = @generate_time + timePerLoop > @length ? (@length - @generate_time) / RSynth::TimeStep : FramesPerBuffer
|
66
|
+
len.times do |i|
|
67
|
+
buf[i] = @source.value_at(@generate_time)
|
68
|
+
@generate_time += RSynth::TimeStep
|
69
|
+
end
|
70
|
+
(FramesPerBuffer - len).times do |i|
|
71
|
+
buf[i] = 0
|
72
|
+
end
|
73
|
+
@generated << buf
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@sequence.each{ |s| s.stop }
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
"RSynth::Pipe::Pipeline source=#{@source},seq=#{@sequence}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'ffi-portaudio'
|
2
|
+
|
3
|
+
module RSynth
|
4
|
+
module Pipe
|
5
|
+
class PortAudio
|
6
|
+
include ::FFI::PortAudio
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
wrap_call('initialise portaudio') { | | API.Pa_Initialize() }
|
10
|
+
@buf = FFI::Buffer.new(:float, RSynth::Pipe::Pipeline::FramesPerBuffer, true) # Create our native buffer
|
11
|
+
end
|
12
|
+
|
13
|
+
def start(pipeline)
|
14
|
+
@source = pipeline
|
15
|
+
open_stream
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
close_stream
|
20
|
+
@source = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def process input, output, frame_count, time_info, status_flags, user_data
|
25
|
+
n = @source.consume(self)
|
26
|
+
return :pcComplete if n.nil?
|
27
|
+
output.write_array_of_float(n)
|
28
|
+
@source.consumed(self)
|
29
|
+
:paContinue
|
30
|
+
end
|
31
|
+
|
32
|
+
def open_stream
|
33
|
+
close_stream unless @stream.nil?
|
34
|
+
# Get the audio host
|
35
|
+
info = wrap_call('get default audio host') do | |
|
36
|
+
return idx if error?(idx = API.Pa_GetDefaultHostApi())
|
37
|
+
API.Pa_GetHostApiInfo(idx)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get the default device
|
41
|
+
inIdx = info[:defaultInputDevice] # Todo: add sampling for luls
|
42
|
+
outIdx = info[:defaultOutputDevice]
|
43
|
+
outDev = wrap_call 'get device info' do | |
|
44
|
+
API.Pa_GetDeviceInfo(outIdx)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create parameters for the output stream
|
48
|
+
outopts = API::PaStreamParameters.new
|
49
|
+
outopts[:device] = outIdx
|
50
|
+
outopts[:channelCount] = RSynth::Channels
|
51
|
+
outopts[:sampleFormat] = API::Float32
|
52
|
+
outopts[:suggestedLatency] = outDev[:defaultHighOutputLatency]
|
53
|
+
outopts[:hostApiSpecificStreamInfo] = nil
|
54
|
+
|
55
|
+
@stream = FFI::Buffer.new :pointer
|
56
|
+
@callback = method(:process)
|
57
|
+
wrap_call('open output stream') do | |
|
58
|
+
API.Pa_OpenStream(
|
59
|
+
@stream, # Stream
|
60
|
+
nil, # Input options
|
61
|
+
outopts, # Output options
|
62
|
+
RSynth::SampleRate, # the sample rate
|
63
|
+
RSynth::Pipe::Pipeline::FramesPerBuffer, # frames per buffer
|
64
|
+
API::PrimeOutputBuffersUsingStreamCallback, # flags
|
65
|
+
@callback, # Callback
|
66
|
+
nil # User data
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Start the stream playing
|
71
|
+
wrap_call('starting output stream') do | |
|
72
|
+
API.Pa_StartStream @stream.read_pointer
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def close_stream
|
77
|
+
return if @stream.nil?
|
78
|
+
API.Pa_CloseStream(@stream.read_pointer)
|
79
|
+
@stream = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def error?(val)
|
83
|
+
(Symbol === val and val != :paNoError) or (Integer === val and val < 0)
|
84
|
+
end
|
85
|
+
|
86
|
+
def wrap_call(msg, &block)
|
87
|
+
ret = yield block
|
88
|
+
raise "Failed to #{msg}: #{API.Pa_GetErrorText(ret)}" if error?(ret)
|
89
|
+
return ret
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'pipe/pipeline'
|
2
|
+
require_relative 'pipe/portaudio'
|
3
|
+
require_relative 'functions'
|
4
|
+
require_relative 'combiner'
|
5
|
+
require_relative 'phase_shifter'
|
6
|
+
require_relative 'oscillator'
|
7
|
+
require_relative 'notes'
|
8
|
+
require_relative 'scales'
|
9
|
+
|
10
|
+
module RSynth
|
11
|
+
SampleRate = 22050
|
12
|
+
TimeStep = 1.0/SampleRate
|
13
|
+
Channels = 1
|
14
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module RSynth
|
2
|
+
class Note
|
3
|
+
# Add some methods to the note class :O
|
4
|
+
def major_scale
|
5
|
+
DiatonicScale.new(self, :ionian)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# A diatonic scale :O
|
10
|
+
class DiatonicScale < Array
|
11
|
+
Degrees = %w(tonic supertonic mediant subdominant dominant submediant leading_tone octave).map(&:to_sym)
|
12
|
+
Modes = %w(ionian dorian phyrgian lydian aeolian locrian).map(&:to_sym)
|
13
|
+
Pattern = %w(2 2 1 2 2 2 1).map(&:to_i)
|
14
|
+
|
15
|
+
def initialize(key, mode)
|
16
|
+
@key = key
|
17
|
+
@mode = mode
|
18
|
+
|
19
|
+
# Define degrees...
|
20
|
+
pat = Pattern.rotate(Modes.index(mode))
|
21
|
+
semitones = 0
|
22
|
+
Degrees.each_with_index do |name, i|
|
23
|
+
idx = Degrees.index(name)
|
24
|
+
self << @key.transpose(pat.take(idx).inject(:+) || 0)
|
25
|
+
semitones += pat[i] unless i >= pat.length
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Modes.each do |name|
|
30
|
+
define_method name do
|
31
|
+
DiatonicScale.new(@key, name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Degrees.each_with_index do |name, i|
|
36
|
+
define_method name do
|
37
|
+
self[i]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/rsynth.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require 'rsynth/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "rsynth"
|
8
|
+
s.version = RSynth::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = %w(James Lawrence)
|
11
|
+
s.email = %w(james@kukee.co.uk)
|
12
|
+
|
13
|
+
s.summary = "Rsynth aims to be a simple to use audio synthesis library for use in ruby"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } # Stolen from github gem, uses git to list files, very nice!
|
18
|
+
s.require_paths = %w(lib)
|
19
|
+
|
20
|
+
s.add_dependency "ffi-portaudio", "~>0.1"
|
21
|
+
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
s.add_development_dependency "rspec", "~>1.3.1"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rsynth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha0
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- James
|
9
|
+
- Lawrence
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-01-19 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ffi-portaudio
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0.1'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rake
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.1
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.3.1
|
63
|
+
description:
|
64
|
+
email:
|
65
|
+
- james@kukee.co.uk
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- Gemfile
|
71
|
+
- Rakefile
|
72
|
+
- lib/rsynth/combiner.rb
|
73
|
+
- lib/rsynth/functions.rb
|
74
|
+
- lib/rsynth/notes.rb
|
75
|
+
- lib/rsynth/oscillator.rb
|
76
|
+
- lib/rsynth/phase_shifter.rb
|
77
|
+
- lib/rsynth/pipe/pipeline.rb
|
78
|
+
- lib/rsynth/pipe/portaudio.rb
|
79
|
+
- lib/rsynth/rsynth.rb
|
80
|
+
- lib/rsynth/scales.rb
|
81
|
+
- lib/rsynth/version.rb
|
82
|
+
- rsynth.gemspec
|
83
|
+
homepage:
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>'
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 1.3.1
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Rsynth aims to be a simple to use audio synthesis library for use in ruby
|
107
|
+
test_files: []
|