|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- #!/usr/bin/env ruby
- # bess1.rb -- some examples from clm/rt.lisp and clm/bess5.cl
-
- # Copyright (C) 2002--2009 Michael Scholz
-
- # Author: Michael Scholz <mi-scholz@users.sourceforge.net>
- # Created: Sun Sep 15 19:11:12 CEST 2002
- # Changed: Tue Sep 29 02:05:49 CEST 2009
-
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
-
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
-
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
- # Commentary:
-
- # Requires sndlib.so and libxm.so!
- #
- # This file provides simple mono real time output to DAC. Tempo,
- # frequency, amplitude, and FM index can be controlled via sliders.
- # The music algorithms are taken from clm/rt.lisp and clm/bess5.cl.
- #
- # Bess.new.start -- starts a Motif widget with two DAC tests.
- #
- # Bess.new.start(:srate, $clm_srate # 22050
- # :bufsize, $clm_rt_bufsize # 128
- # :sample_type, $clm_sample_type # Mus_lshort
- # :which, :agn # :agn or :vct_test
- # :play, false)
-
- # Code:
-
- def warn(*args)
- str = format(*args) << ($! ? ": #{$!}" : "") << "\n"
- str << (($@ and $DEBUG) ? "\n[#{$@.join("\n")}]" : "")
- $stdout.print str
- $! = nil
- end
-
- def die(*args)
- warn(*args)
- exit 1
- end
-
- def rbm_require(lib)
- puts "loading #{lib.inspect}" if $VERBOSE
- require lib.to_s
- rescue ScriptError
- die "\aScriptError"
- end
-
- rbm_require "sndlib"
- $output = nil # holds fd from mus_audio_open_output()
- $clm_srate = 22050
- $clm_sample_type = Mus_lshort
- $clm_rt_bufsize = 128
-
- module Bess_utils
- def rbm_random(n)
- mus_random(n).abs
- end
-
- def get_args(args, key, val)
- if(key == :help and (args == key or args.member?(key) or args.assoc(key)))
- val = true
- elsif(args.member?(key))
- x = args[args.index(key) + 1]
- val = ((x == nil) ? val : x)
- elsif(args.assoc(key))
- val = (args.assoc(key)[1] rescue val)
- end
- val
- end
-
- def seconds2samples(sec)
- sr = (mus_srate() rescue $clm_srate)
- (sec * sr).round
- end
-
- def envelope_interp(*args)
- x = args[0]
- env = args[1]
- base = args[2]
- if (not env) or env.empty?
- 0.0
- elsif x <= env[0] or env[2..-1].empty?
- env[1]
- elsif env[2] > x
- if env[1] == env[3] or (base and base == 0.0)
- env[1]
- elsif (not base) or base == 1.0
- env[1] + (x - env[0]) * ((env[3] - env[1]) / (env[2] - env[0]))
- else
- env[1] + ((env[3] - env[1]) / (base - 1.0)) *
- ((base ** ((x - env[0]) / (env[2] - env[0]))) - 1.0)
- end
- else
- envelope_interp(x, env[2..-1])
- end
- end
-
- include Math
-
- # simple violin, see snd/fm.html
- def make_rt_violin(dur = 1.0, freq = 440.0, amp = 0.3, *args)
- fm_index = get_args(args, :fm_index, 1.0)
- amp_env = get_args(args, :amp_env, [0, 0, 25, 1, 75, 1, 100, 0])
- frq_scl = hz2radians(freq)
- maxdev = frq_scl * fm_index
- index1 = maxdev * (5.0 / log(freq))
- index2 = maxdev * 3.0 * ((8.5 - log(freq)) / (3.0 + freq / 1000.0))
- index3 = maxdev * (4.0 / sqrt(freq))
- carrier = make_oscil(:frequency, freq)
- fmosc1 = make_oscil(:frequency, freq)
- fmosc2 = make_oscil(:frequency, freq * 3.0)
- fmosc3 = make_oscil(:frequency, freq * 4.0)
- ampf = make_env(:envelope, amp_env, :scaler, amp, :duration, dur)
- indf1 = make_env(:envelope, [0, 1, 25, 0.4, 75, 0.6, 100, 0], :scaler, index1, :duration, dur)
- indf2 = make_env(:envelope, [0, 1, 25, 0.4, 75, 0.6, 100, 0], :scaler, index2, :duration, dur)
- indf3 = make_env(:envelope, [0, 1, 25, 0.4, 75, 0.6, 100, 0], :scaler, index3, :duration, dur)
- pervib = make_triangle_wave(:frequency, 0.5, :amplitude, 0.0025 * frq_scl)
- ranvib = make_rand_interp(:frequency, 16.0, :amplitude, 0.005 * frq_scl)
- lambda do | |
- vib = triangle_wave(pervib) + rand_interp(ranvib)
- env(ampf) * oscil(carrier,
- vib + env(indf1) * oscil(fmosc1, vib) +
- env(indf2) * oscil(fmosc2, 3.0 * vib) +
- env(indf3) * oscil(fmosc3, 4.0 * vib))
- end
- end
- end
-
- # class Agn is a simplified translation of clm/bess5.cl and
- # clm/clm-example.lisp.
- class Agn
- include Bess_utils
-
- def initialize
- @tempo = 0.25
- @amp = 1.0
- @freq = 1.0
- @index = 1.0
- @play = false
- @lim = 256
- @time = 60
- @octs = Array.new(@lim + 1) do |i| (4 + 2 * rbell(rbm_random(1.0))).floor end
- @rhys = Array.new(@lim + 1) do |i| (4 + 6 * rbm_random(1.0)).floor end
- @amps = Array.new(@lim + 1) do |i| (1 + 8 * rbell(rbm_random(1.0))).floor end
- @pits = Array.new(@lim + 1) do |i|
- [0, 0, 2, 4, 11, 11, 5, 6, 7, 9, 2, 0, 0].at((12 * rbm_random(1.0)).floor)
- end
- @begs = Array.new(@lim + 1) do |i|
- if rbm_random(1.0) < 0.9
- (4 + 2 * rbm_random(1.0)).floor
- else
- (6 * rbm_random(4.0)).floor
- end
- end
- end
-
- # called by XtAppAddWorkProc
- def rt_send2dac(func)
- if @play
- mus_audio_write($output, vct2sound_data(vct_map!(make_vct($clm_rt_bufsize), func.call),
- make_sound_data(1, $clm_rt_bufsize), 0),
- $clm_rt_bufsize)
- false
- else
- mus_audio_close($output)
- $output = nil
- true
- end
- end
-
- # see clm/rt.lisp
- def make_vct_test(*args)
- srate = get_args(args, :srate, $clm_srate)
- bufsize = get_args(args, :bufsize, $clm_rt_bufsize)
- sample_type = get_args(args, :sample_type, $clm_sample_type)
- $clm_srate = set_mus_srate(srate).to_i
- $clm_rt_bufsize = bufsize
- $output = mus_audio_open_output(Mus_audio_default, srate, 1, sample_type, bufsize * 2)
- mode = [0, 12, 2, 4, 14, 4, 5, 5, 0, 7, 7, 11, 11]
- pits = Array.new(@lim + 1) do rbm_random(12.0).floor end
- begs = Array.new(@lim + 1) do 1 + rbm_random(3.0).floor end
- cellbeg, cellsiz, cellctr = 0, 6, 0
- func = nil
- len = dur = 0
- lambda do | |
- if len > 1
- len -= 1
- else
- dur = @tempo * begs[cellctr + 1]
- cellctr += 1
- if cellctr > (cellsiz + cellbeg)
- cellbeg += 1 if rbm_random(1.0) > 0.5
- cellsiz += 1 if rbm_random(1.0) > 0.5
- cellctr = cellbeg
- end
- func = make_rt_violin(dur, @freq * 16.351 * 16 * 2 ** (mode[pits[cellctr]] / 12.0),
- @amp * 0.3, :fm_index, @index)
- len = (seconds2samples(dur) / bufsize).ceil
- end
- func
- end
- end
-
- def tune(x)
- [1.0, 256.0 / 243, 9.0 / 8, 32.0 / 27, 81.0 / 64,
- 4.0 / 3, 1024.0 / 729, 3.0 / 2, 128.0 / 81, 27.0 / 16,
- 16.0 / 9, 243.0 / 128, 2.0].at(x % 12) * 2 ** x.divmod(12).first
- end
-
- def rbell(x)
- envelope_interp(x * 100, [0, 0, 10, 0.25, 90, 1.0, 100, 1.0])
- end
-
- # see clm/bess5.cl
- def make_agn(*args)
- srate = get_args(args, :srate, $clm_srate)
- bufsize = get_args(args, :bufsize, $clm_rt_bufsize)
- sample_type = get_args(args, :sample_type, $clm_sample_type)
- $clm_srate = set_mus_srate(srate).to_i
- $clm_rt_bufsize = bufsize
- $output = mus_audio_open_output(Mus_audio_default, srate, 1, sample_type, bufsize * 2)
- die("can't open DAC (%s)", $output.inspect) if $output < 0
- wins = [[0, 0, 40, 0.1, 60, 0.2, 75, 0.4, 82, 1, 90, 1, 100, 0],
- [0, 0, 60, 0.1, 80, 0.2, 90, 0.4, 95, 1, 100, 0],
- [0, 0, 10, 1, 16, 0, 32, 0.1, 50, 1, 56, 0, 60, 0, 90, 0.3, 100, 0],
- [0, 0, 30, 1, 56, 0, 60, 0, 90, 0.3, 100, 0],
- [0, 0, 50, 1, 80, 0.3, 100, 0],
- [0, 0, 40, 0.1, 60, 0.2, 75, 0.4, 82, 1, 90, 1, 100, 0],
- [0, 0, 40, 0.1, 60, 0.2, 75, 0.4, 82, 1, 90, 1, 100, 0],
- [0, 0, 10, 1, 32, 0.1, 50, 1, 90, 0.3, 100, 0],
- [0, 0, 60, 0.1, 80, 0.3, 95, 1, 100, 0],
- [0, 0, 80, 0.1, 90, 1, 100, 0]]
- cellbeg, cellsiz, cellctr, whichway = 0, 4, 0, 1
- nextbeg = beg = 0.0
- func = nil
- len = dur = 0
- lambda do | |
- if len > 1
- len -= 1
- else
- beg += nextbeg
- nextbeg += [0.025, @tempo * (0.95 + rbm_random(0.1)) * @begs[cellctr]].max
- dur = [0.025, @tempo * (0.85 + rbm_random(0.1)) * @rhys[cellctr]].max
- freq = @freq * 16.351 * tune(@pits[cellctr]) * 2 ** @octs[cellctr]
- dur += dur if freq < 100
- ampl = @amp * 10 * [0.003, @amps[cellctr] * 0.01].max
- ind = @index * rbm_random(1.0) * 3.0
- cellctr += 1
- if cellctr > (cellsiz + cellbeg)
- cellbeg += 1
- if rbm_random(1.0) > 0.5
- cellsiz += whichway
- end
- if cellsiz > 10 and rbm_random(1.0) > 0.99
- whichway = -2
- if cellsiz > 6 and rbm_random(1.0) > 0.999
- whichway = -1
- if cellsiz < 4
- whichway = 1
- end
- end
- end
- nextbeg += rbm_random(1.0)
- cellctr = cellbeg
- end
- func = make_rt_violin(dur, freq, ampl, :fm_index, ind,
- :amp_env, wins[(10 * (beg - beg.floor)).floor])
- len = (seconds2samples(dur) / bufsize).ceil
- end
- func
- end
- end
- end
-
- class Bess < Agn
- rbm_require "libxm"
-
- def initialize
- super
- @sliderback = "lightsteelblue"
- @background = "lightsteelblue1"
- @which = @proc = nil
- @shell_app = @form = nil
- @tl = @ts = @fl = @fs = @al = @as = @il = @is = nil
- 1.upto(15) do |i|
- trap(i) do |sig|
- puts "\nSignal #{sig} received. Process #{$$} canceled."
- RXtRemoveWorkProc(@proc) if @proc
- exit 0
- end
- end
- end
-
- def get_color(color)
- col = RXColor()
- dpy = RXtDisplay(@shell_app[0])
- cmap = RDefaultColormap(dpy, RDefaultScreen(dpy))
- warn("Can't allocate #{color.inspect}!") if RXAllocNamedColor(dpy, cmap, color, col, col).zero?
- Rpixel(col)
- end
-
- def set_label(wid, *args)
- RXtVaSetValues(wid, [RXmNlabelString, RXmStringCreate(format(*args), RXmFONTLIST_DEFAULT_TAG)])
- end
-
- def make_label(wid, name)
- RXtCreateManagedWidget(name, RxmLabelWidgetClass, @form,
- [RXmNleftAttachment, RXmATTACH_FORM,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNtopAttachment, RXmATTACH_WIDGET,
- RXmNtopWidget, wid,
- RXmNrightAttachment, RXmATTACH_NONE,
- RXmNalignment, RXmALIGNMENT_END,
- RXmNrecomputeSize, false,
- RXmNbackground, get_color(@background)])
- end
-
- def make_scale_label(wid)
- RXtCreateManagedWidget("label", RxmLabelWidgetClass, @form,
- [RXmNleftAttachment, RXmATTACH_WIDGET,
- RXmNleftWidget, wid,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNtopAttachment, RXmATTACH_OPPOSITE_WIDGET,
- RXmNtopWidget, wid,
- RXmNrightAttachment, RXmATTACH_NONE,
- RXmNbackground, get_color(@background)])
- end
-
- def make_scale(wid)
- RXtCreateManagedWidget("scale", RxmScaleWidgetClass, @form,
- [RXmNleftAttachment, RXmATTACH_WIDGET,
- RXmNleftWidget, wid,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNtopAttachment, RXmATTACH_OPPOSITE_WIDGET,
- RXmNtopWidget, wid,
- RXmNrightAttachment, RXmATTACH_FORM,
- RXmNshowValue, false,
- RXmNorientation, RXmHORIZONTAL,
- RXmNheight, 20,
- RXmNbackground, get_color(@sliderback)])
- end
-
- # return label and scale widget
- def make_scales(wid, name, val, callback)
- label = make_scale_label(make_label(wid, name))
- scale = make_scale(label)
- set_label(label, val.kind_of?(Integer) ? "%4d" : "%4.3f", val)
- RXtAddCallback(scale, RXmNdragCallback, callback, label)
- RXtAddCallback(scale, RXmNvalueChangedCallback, callback ,label)
- [label, scale]
- end
-
- def do_play(*args)
- if @play
- case @which
- when :agn
- func = make_agn(*args)
- when :vct_test
- func = make_vct_test(*args)
- else
- func = make_agn(*args)
- end
- @proc = RXtAppAddWorkProc(@shell_app[1], lambda do |c| rt_send2dac(func) end)
- else
- RXtRemoveWorkProc(@proc) if @proc
- end
- end
-
- def set_defaults(parent)
- @tempo = 0.25
- @amp = 1.0
- @freq = 1.0
- @index = 1.0
- low_tempo = 0.05
- high_tempo = 0.5
- low_freq = 0.1
- high_freq = 4.0
- high_index = 2.0
- set_label(@tl, "%4.3f", @tempo)
- RXmScaleSetValue(@ts, (100 * (@tempo - low_tempo) / (high_tempo - low_tempo)).round)
- set_label(@fl, "%4.3f", @freq)
- RXmScaleSetValue(@fs, (100 * (@freq - low_freq) / (high_freq - low_freq)).round)
- set_label(@al, "%4.3f", @amp)
- RXmScaleSetValue(@as, (100 * @amp).round)
- set_label(@il, "%4.3f", @index)
- RXmScaleSetValue(@is, (100 * (@index / high_index)).round)
- end
-
- def start(*args)
- @play = get_args(args, :play, false)
- @which = get_args(args, :which, :agn)
- # rest args are going to make_vct_test() or make_agn()
- cargs = [$0] + $*
- @shell_app = RXtVaOpenApplication("FM", cargs.length, cargs, RapplicationShellWidgetClass,
- [RXmNallowShellResize, true, RXmNtitle, "FM forever!"])
- RXtAddEventHandler(@shell_app[0], 0, true,
- lambda do |w, c, i, f| R_XEditResCheckMessages(w, c, i, f) end)
- @form = RXtCreateManagedWidget("form", RxmFormWidgetClass, @shell_app[0],
- [RXmNresizePolicy, RXmRESIZE_GROW,
- RXmNbackground, get_color(@background)])
- play = RXtCreateManagedWidget("play", RxmToggleButtonWidgetClass, @form,
- [RXmNtopAttachment, RXmATTACH_FORM,
- RXmNleftAttachment, RXmATTACH_FORM,
- RXmNrightAttachment, RXmATTACH_NONE,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNbackground, get_color(@background)])
- radio = RXmCreateRadioBox(@form, "radio",
- [RXmNorientation, RXmHORIZONTAL,
- RXmNtopAttachment, RXmATTACH_FORM,
- RXmNleftAttachment, RXmATTACH_WIDGET,
- RXmNleftWidget, play,
- RXmNrightAttachment, RXmATTACH_NONE,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNbackground, get_color(@background)])
- p_agn = RXtCreateManagedWidget("agn", RxmToggleButtonWidgetClass, radio,
- [RXmNtopAttachment, RXmATTACH_FORM,
- RXmNleftAttachment, RXmATTACH_FORM,
- RXmNrightAttachment, RXmATTACH_NONE,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNbackground, get_color(@background)])
- p_test = RXtCreateManagedWidget("test", RxmToggleButtonWidgetClass, radio,
- [RXmNtopAttachment, RXmATTACH_FORM,
- RXmNleftAttachment, RXmATTACH_WIDGET,
- RXmNleftWidget, p_agn,
- RXmNrightAttachment, RXmATTACH_NONE,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNbackground, get_color(@background)])
- quit = RXtCreateManagedWidget(" quit ", RxmPushButtonWidgetClass, @form,
- [RXmNtopAttachment, RXmATTACH_FORM,
- RXmNleftAttachment, RXmATTACH_WIDGET,
- RXmNleftWidget, radio,
- RXmNrightAttachment, RXmATTACH_FORM,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNbackground, get_color(@background)])
- sep = RXtCreateManagedWidget("sep", RxmSeparatorWidgetClass, @form,
- [RXmNleftAttachment, RXmATTACH_FORM,
- RXmNbottomAttachment, RXmATTACH_NONE,
- RXmNtopAttachment, RXmATTACH_WIDGET,
- RXmNtopWidget, radio,
- RXmNrightAttachment, RXmATTACH_FORM,
- RXmNheight, 4,
- RXmNorientation, RXmHORIZONTAL])
- RXmToggleButtonSetState(play, @play, true)
- RXtAddCallback(play, RXmNvalueChangedCallback,
- lambda do |w, c, i|
- @play = Rset(i)
- set_defaults(sep) if @play
- do_play(*args)
- end)
- RXmToggleButtonSetState(p_agn, @which == :agn, true)
- RXtAddCallback(p_agn, RXmNvalueChangedCallback,
- lambda do |w, c, i|
- @which = c if Rset(i)
- @play = false
- RXmToggleButtonSetState(play, @play, true)
- end, :agn)
- RXmToggleButtonSetState(p_test, @which == :vct_test, true)
- RXtAddCallback(p_test, RXmNvalueChangedCallback,
- lambda do |w, c, i|
- @which = c if Rset(i)
- @play = false
- RXmToggleButtonSetState(play, @play, true)
- end, :vct_test)
- RXtAddCallback(quit, RXmNactivateCallback,
- lambda do |w, c, i|
- RXtRemoveWorkProc(@proc) if @proc
- exit 0
- end)
- low_tempo = 0.05
- high_tempo = 0.5
- low_freq = 0.1
- high_freq = 4.0
- high_index = 2.0
- @tl, @ts = make_scales(sep, " tempo:", @tempo,
- lambda do |w, c, i|
- @tempo = low_tempo + Rvalue(i) * (high_tempo - low_tempo) * 0.01
- set_label(c, "%4.3f", @tempo)
- end)
- RXmScaleSetValue(@ts, (100 * (@tempo - low_tempo) / (high_tempo - low_tempo)).round)
- @fl, @fs = make_scales(@ts, " freq:", @freq,
- lambda do |w, c, i|
- @freq = low_freq + Rvalue(i) * ((high_freq - low_freq) * 0.01)
- set_label(c, "%4.3f", @freq)
- end)
- RXmScaleSetValue(@fs, (100 * (@freq - low_freq) / (high_freq - low_freq)).round)
- @al, @as = make_scales(@fs, " amp:", @amp,
- lambda do |w, c, i|
- @amp = Rvalue(i) * 0.01
- set_label(c, "%4.3f", @amp)
- end)
- RXmScaleSetValue(@as, (100 * @amp).round)
- @il, @is = make_scales(@as, " index:", @index,
- lambda do |w, c, i|
- @index = Rvalue(i) * high_index * 0.01
- set_label(c, "%4.3f", @index)
- end)
- RXmScaleSetValue(@is, (100 * (@index / high_index)).round)
- do_play(*args)
- RXtManageChild(radio)
- RXtRealizeWidget(@shell_app[0])
- RXtAppMainLoop(@shell_app[1])
- end
- end
-
- begin
- # Bess.new.start(:srate, $clm_srate,
- # :bufsize, $clm_rt_bufsize,
- # :sample_type, $clm_sample_type,
- # :which, :agn,
- # :play, false)
- Bess.new.start
- end
-
- # bess1.rb ends here
|