Permalink
Browse files

basic buffer functionality

  • Loading branch information...
1 parent 92fc776 commit f383bc025db22441e447f1446737f36d8afec644 Macario committed Jul 19, 2009
Showing with 161 additions and 61 deletions.
  1. +40 −39 lib/scruby/audio/buffer.rb
  2. +25 −1 lib/scruby/audio/server.rb
  3. +47 −21 spec/audio/buffer_spec.rb
  4. +49 −0 spec/audio/server_spec.rb
View
@@ -1,69 +1,49 @@
module Scruby
class Buffer
- # allocReadChannel
- # allocReadMsg
- # alloc ReadCannelMsg
+
+
# read
# readChannel
# readNoUpdate
- # readMsg
- # readChannelMsg
# cueSoundFile
- # cueSoundFilMsg
# loadCollection
# sendCollection
# streamCollection
# loadToFloatArray
# getToFloatArray
# write
# writeMsg
- # free
# zero
- # zeroMsg
# set
- # setMsg
# setn
- # setnMsgArgs
- # setnMsg
# get
- # getMsg
# getn
- # getnMsg
# fill
- # fillMsg
# normalize
# gen
- # genMsg
# sine1
# ...
# copy
# copyData
- # copyMsg
# close
- # closeMsg
# query
# updateInfo
- # cache
- # uncache
# queryDone
# printOn
# play
# duration
-
-
# asBufWithValues
-
attr_reader :server
attr_accessor :path, :frames, :channels, :rate
def initialize server, frames = -1, channels = 1
@server, @frames, @channels = server, frames, channels
- @server.allocate_buffers self
end
def allocate &message
message ||= 0
+ @server.allocate_buffers self
@server.send '/b_alloc', buffnum, frames, channels, message.value(self)
self
end
@@ -73,19 +53,31 @@ def buffnum
end
alias :as_ugen_input :buffnum
# alias :as_control_input :buffnum
+
+ def free &message
+ message ||= 0
+ @server.send "/b_free", buffnum, message.value(self)
+ @server.buffers.delete self
+ end
# :nodoc:
def allocate_and_read path, start, frames, &message
+ @server.allocate_buffers self
message ||= ["/b_query", buffnum]
@server.send "/b_allocRead", buffnum, @path = path, start, frames, message.value(self)
self
end
+ def allocate_read_channel path, start, frames, channels, &message
+ @server.allocate_buffers self
+ message ||= ["/b_query", buffnum]
+ @server.send *(["/b_allocReadChannel", buffnum, path, start, frames] + channels << message.value(self))
+ self
+ end
+
class << self
- # Allocate a buffer and immediately read a soundfile into it.
- def read server, path, start = 0, frames = -1, &message
- buffer = new server, &message
- buffer.allocate_and_read path, start, frames
+ def allocate server, frames = -1, channels = 1, &message
+ new(server, frames, channels).allocate &message
end
def cue_sound_file server, path, start, channels = 2, buff_size = 32768, &message
@@ -95,23 +87,32 @@ def cue_sound_file server, path, start, channels = 2, buff_size = 32768, &messag
end
end
- def allocate server, frames = -1, channels = 1, &message
- buffer = new server, frames, channels
- buffer.allocate &message
+ # Allocate a buffer and immediately read a soundfile into it.
+ def read server, path, start = 0, frames = -1, &message
+ buffer = new server, &message
+ buffer.allocate_and_read path, start, frames
+ end
+
+ def read_channel server, path, start = 0, frames = -1, channels = [], &message
+ new(server, frames, channels).allocate_read_channel path, start, frames, channels, &message
end
- named_arguments_for :allocate, :read, :cue_sound_file
-
- # allocConsecutive
- # readChannel
+ def alloc_consecutive buffers, server, frames = -1, channels = 1, &message
+ buffers = Array.new(buffers){ new server, frames, channels }
+ server.allocate_buffers buffers
+ message ||= 0
+ buffers.each do |buff|
+ server.send '/b_alloc', buff.buffnum, frames, channels, message.value(buff)
+ end
+ end
+
+ # def send_collection server, collection, channels = 1, wait = 0.0, &message
+ # end
+ named_arguments_for :allocate, :read, :cue_sound_file, :alloc_consecutive, :read_channel
+
# readNoUpdate
# loadCollection
# sendCollection
- # freeAllinitServerCache
- # initServerCache
- # clearServerCaches
- # cachedBuffersDo
- # cachedBufferAt
# loadDialog
end
end
View
@@ -118,12 +118,36 @@ def send_synth_def synth_def
send Bundle.new( nil, Message.new( '/d_recv', Blob.new(synth_def.encode), 0 ) )
end
+ # This method should not be directly called, it will add passed +buffers+ to the @buffers array, the +Buffer#buffnum+
+ # will be it's index in this array. Max number of buffers is 1024.
+ # It will try to fill first consecutive nil indices of the array and if there are not enough consecutive nil indices for the +buffers+
+ # passed and the maximum number is not reached it will push the buffers to the array, otherwise will raise an error.
def allocate_buffers *buffers
buffers.peel!
if @buffers.compact.size + buffers.size > 1024
raise SCError, 'No more buffer numbers -- free some buffers before allocating more.'
end
- @buffers += buffers
+
+ return @buffers += buffers unless @buffers.index nil # just concat arrays if no nil item in @buffers
+
+ indices = []
+ @buffers.each_with_index do |item, index| # find n number of consecutive nil indices
+ break if indices.size >= buffers.size
+ if item.nil?
+ indices << index
+ else
+ indices.clear
+ end
+ end
+
+ case
+ when indices.size >= buffers.size
+ @buffers[indices.first, buffers.size] = buffers
+ when @buffers.size + buffers.size <= 1024
+ @buffers += buffers
+ else
+ raise SCError, "No block of #{ buffers.size } consecutive buffer slots is available."
+ end
end
@@servers = []
View
@@ -19,26 +19,6 @@ def puts string
end
describe Buffer do
- describe '#server buffer allocation' do
- before do
- @server = Server.new
- end
-
- it "should register itself in server" do
- buffer = Buffer.new @server
- @server.buffers.should include(buffer)
- end
-
- it "should allow less than 1024 buffers" do
- @server.allocate_buffers( (1..1024).map{ mock(Buffer) } )
- @server.buffers.size.should == 1024
- end
-
- it "should not allow more than 1024 buffers" do
- lambda { @server.allocate_buffers( (1..1025).map{ mock(Buffer) } ) }.should raise_error(SCError)
- end
- end
-
describe "messaging" do
before :all do
@server = Server.new
@@ -99,6 +79,52 @@ def puts string
it "should allow passing a completion message"
end
+
+ describe '#free' do
+ before do
+ @buffer = Buffer.allocate @server, 44100 * 10.0, 2
+ @buffer2 = Buffer.allocate @server, 44100 * 10.0, 2
+ @bnum = @buffer2.buffnum
+ @buffer2.free
+ sleep 0.1
+ end
+
+ it "should remove itself from the server @buffers array and send free message" do
+ @buffer2.buffnum.should be_nil
+ @server.output.should =~ %r{\[ "/b_free", #{ @bnum }, 0 \]}
+ end
+
+ it "should allow passing a completion message"
+
+ end
+
+ describe 'Buffer.alloc_consecutive' do
+ before do
+ @buffers = Buffer.alloc_consecutive 8, @server, 4096, 2
+ sleep 0.1
+ end
+
+ it "should send alloc message for each Buffer and instantiate" do
+ @buffers.should have(8).buffers
+ @buffers.each do |buff|
+ @server.output.should =~ %r{\[ "/b_alloc", #{ buff.buffnum }, 4096, 2, 0 \]}
+ end
+ end
+
+ it "should allow passing a message"
+ end
+
+ describe 'Buffer.read_channel' do
+ before do
+ @buffer = Buffer.read_channel @server, "sounds/SinedPink.aiff", :channels => [0]
+ sleep 0.1
+ end
+
+ it "should allocate and send /b_allocReadChannel message" do
+ @buffer.should be_a(Buffer)
+ @server.output.should =~ %r{\[ "/b_allocReadChannel", #{ @buffer.buffnum }, "sounds/SinedPink.aiff", 0, -1, 0, DATA\[20\] \]}
+ @server.output.should =~ /73 6f 75 6e 64 73 2f 53 69 6e 65 64 50 69 6e 6b/
+ end
+ end
end
-
end
View
@@ -25,6 +25,12 @@ def puts string
end
end
+class Scruby::Audio::Buffer
+ def == other
+ self.class == other.class
+ end
+end
+
describe Message do
it "should encode array as Message Blob" do
m = Message.new "/b_allocRead", 1, "path", 1, -1, ["/b_query", 1]
@@ -91,6 +97,49 @@ def puts string
@server.output.should =~ %r{\[ "#bundle", 1, \n\s*\[ "/d_recv", DATA\[56\], 0 \]\n\]}
end
end
+
+ describe 'buffers' do
+ before do
+ @server = Server.new
+ end
+
+ it "should allow less than 1024 buffers" do
+ @server.allocate_buffers( (1..1024).map{ mock(Buffer) } )
+ @server.buffers.size.should == 1024
+ end
+ it "should not allow more than 1024 buffers" do
+ lambda { @server.allocate_buffers( (1..1025).map{ Buffer.new } ) }.should raise_error(SCError)
+ end
+
+ it "should try to allocate lowest nil slot" do
+ @server.buffers.concat([nil, nil, Buffer.new, nil, nil, nil, Buffer.new])
+ @server.allocate_buffers buffer = Buffer.new
+ @server.buffers.index(buffer).should == 0
+ end
+
+ it "should allocate various buffers in available indices" do
+ @server.buffers.concat([nil, nil, Buffer.new, nil, nil, nil, Buffer.new])
+ @server.allocate_buffers Buffer.new, Buffer.new, Buffer.new
+ @server.buffers.should == [nil, nil, Buffer.new, Buffer.new, Buffer.new, Buffer.new, Buffer.new]
+ end
+
+ it "should allocate by appending various buffers" do
+ @server.buffers.concat([nil, nil, Buffer.new, nil, nil, nil, Buffer.new])
+ @server.allocate_buffers Buffer.new, Buffer.new, Buffer.new, Buffer.new
+ @server.buffers.should == [nil, nil, Buffer.new, nil, nil, nil, Buffer.new, Buffer.new, Buffer.new, Buffer.new, Buffer.new]
+ end
+
+ it "should not surpass the max buffer limit" do
+ @server.allocate_buffers( (1..1022).map{ |i| Buffer.new if i % 2 == 0 } )
+ lambda { @server.allocate_buffers Buffer.new, Buffer.new, Buffer.new }.should raise_error
+ end
+
+ it "should allocate by appending" do
+ @server.allocate_buffers( (1..1021).map{ |i| Buffer.new if i % 2 == 0 } )
+ @server.allocate_buffers Buffer.new, Buffer.new, Buffer.new
+ @server.buffers.size.should == 1024
+ end
+ end
end

0 comments on commit f383bc0

Please sign in to comment.