You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2939 line
90KB

  1. # dlocsig.rb -- CLM -> Snd/Ruby translation of dlocsig.lisp
  2. # Translator/Author: Michael Scholz <mi-scholz@users.sourceforge.net>
  3. # Copyright (c) 2003-2012 Michael Scholz <mi-scholz@users.sourceforge.net>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. # 1. Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25. # SUCH DAMAGE.
  26. # Original Copyright of Fernando Lopez Lezcano:
  27. # ;;; Copyright (c) 92, 93, 94, 98, 99, 2000, 2001 Fernando Lopez Lezcano.
  28. # ;;; All rights reserved.
  29. # ;;; Use and copying of this software and preparation of derivative works
  30. # ;;; based upon this software are permitted and may be copied as long as
  31. # ;;; no fees or compensation are charged for use, copying, or accessing
  32. # ;;; this software and all copies of this software include this copyright
  33. # ;;; notice. Suggestions, comments and bug reports are welcome. Please
  34. # ;;; address email to: nando@ccrma.stanford.edu
  35. # ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  36. # ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  37. # ;;; Dynamic multichannel three-dimentional signal locator
  38. # ;;; (wow that sound good! :-)
  39. # ;;;
  40. # ;;; by Fernando Lopez Lezcano
  41. # ;;; CCRMA, Stanford University
  42. # ;;; nando@ccrma.stanford.edu
  43. # ;;;
  44. # ;;; Thanks to Juan Pampin for help in the initial coding of the new version
  45. # ;;; and for prodding me to finish it. To Joseph L. Anderson and Marcelo Perticone
  46. # ;;; for insights into the Ambisonics coding and decoding process.
  47. # ;;; http://www.york.ac.uk/inst/mustech/3d_audio/ambison.htm for more details...
  48. # Commentary:
  49. # Tested with Snd 7.10, Motif 2.2.2, Gtk+ 2.2.1, Ruby 1.6.6, 1.6.8 and 1.9.0.
  50. #
  51. # The code is a translation of the Lisp code of Fernando Lopez Lezcano
  52. # found in clm-2/dlocsig of the CLM distribution. An extensive
  53. # documentation of the purpose and usage of it can be found in
  54. # clm-2/dlocsig/dlocsig.html.
  55. #
  56. # Note: dlocsig.rb handles not more rev_channels than out_channels;
  57. # B_format_ambisonics handles only 4 out_channels and 0, 1, or 4
  58. # rev_channels.
  59. #
  60. # The simple example
  61. #
  62. # [[-10, 10], [0, 5], [10, 10]].to_path.snd_plot
  63. #
  64. # draws trajectory, velocity, doppler curve, and the acceleration in
  65. # Snd's lisp-graph. If you have gnuplot installed, the example
  66. #
  67. # [[-10, 10], [0, 5], [10, 10]].to_path.pplot
  68. #
  69. # draws all four curves in one gnuplot window.
  70. # DL.make_path
  71. # DL.make_polar_path
  72. # DL.make_closed_path
  73. # and to_path take the following options
  74. #
  75. # Open_bezier_path.new(path, *args)
  76. # :d3, true
  77. # :polar, false
  78. # :error, 0.01
  79. # :curvature, nil
  80. # :initial_direction, [0.0, 0.0, 0.0]
  81. # :final_direction, [0.0, 0.0, 0.0]
  82. #
  83. # Closed_bezier_path.new(path, *args)
  84. # :d3, true
  85. # :polar, false
  86. # :error, 0.01
  87. # :curvature, nil
  88. #
  89. # DL.make_literal_path
  90. # DL.make_literal_polar_path take the following options
  91. #
  92. # Literal_path.new(path, *args)
  93. # :d3, true
  94. # :polar, false
  95. #
  96. # DL.make_spiral_path takes these options
  97. #
  98. # Spiral_path.new :start_angle, 0
  99. # :turns, 2
  100. #
  101. # The make_locsig-replacement make_dlocsig takes these arguments:
  102. #
  103. # DL.make_dlocsig(startime, dur, *args)
  104. # :path, nil
  105. # :scaler, 1.0
  106. # :reverb_amount, 0.05
  107. # :rbm_output, $output
  108. # :rbm_reverb, $reverb
  109. # :output_power, 1.5
  110. # :reverb_power, 0.5
  111. # :render_using, :amplitude_panning
  112. # or :b_format_ambisonics
  113. # or :decoded_ambisonics
  114. #
  115. # Sample instruments (sinewave() and move() below) show how to replace
  116. # the usual make_locsig() and locsig() by DL.make_dlocsig() and
  117. # DL.dlocsig().
  118. # Example functions at the end of the file:
  119. # class Instrument
  120. # sinewave(start, dur, freq, amp, path, amp_env, *dlocsig_args)
  121. # move(start, file, path, *dlocsig_args)
  122. # move_sound(path, *dlocsig_args) do ... end
  123. #
  124. # class With_sound
  125. # run_dlocsig(start, dur, *dlocsig_args) do |samp| ... end
  126. # Classes and Modules:
  127. # module Inject
  128. # inject(n)
  129. # sum(initial)
  130. # product(initial)
  131. #
  132. # class Array
  133. # to_trias
  134. # to_path(*args)
  135. #
  136. # class Sndplot
  137. # initialize(chns)
  138. # snd
  139. # open
  140. # close
  141. #
  142. # class Gnuplot
  143. # initialize
  144. # open
  145. # close
  146. # command(*args)
  147. # reset
  148. # set_autoscale
  149. # set_x_range(range)
  150. # set_y_range(range)
  151. # set_z_range(range)
  152. # set_grid
  153. # set_surface
  154. # set_parametric
  155. # set_ticslevel(level)
  156. # set_title(title)
  157. # set_label(label)
  158. # set_margins(margin)
  159. # set_border(border)
  160. # start_multiplot
  161. # end_multiplot
  162. # size(xorigin, yorigin, xsize, ysize)
  163. # data(data, *args)
  164. # plot_2d_curve(curve, *args)
  165. # plot_2d_curves(curves, *args)
  166. # plot_3d_curve(curve, *args)
  167. #
  168. # module DL
  169. # class Dlocsig < Dlocs
  170. # initialize(start, dur, *args)
  171. # one_turn
  172. # one_turn=(val)
  173. # speed_of_sound
  174. # speed_of_sound=(val)
  175. # run_beg
  176. # run_end
  177. # angles_in_degree
  178. # angles_in_radians
  179. # angles_in_turns
  180. # distance_in_meters
  181. # distance_in_feet
  182. #
  183. # class Path
  184. # initialize(path, *args)
  185. # path_x
  186. # path_y
  187. # path_z
  188. # path_time
  189. # scale_path(scaling)
  190. # translate_path(translation)
  191. # rotate_path(rotation, *args)
  192. #
  193. # path_trajectory
  194. # path_2d_trajectory
  195. # path_velocity
  196. # path_doppler
  197. # path_acceleration
  198. #
  199. # plot_open
  200. # plot_close
  201. # cmd(*args)
  202. # plot_trajectory(*args)
  203. # plot_velocity(reset)
  204. # plot_doppler(reset)
  205. # plot_acceleration(reset)
  206. # pplot(normalize)
  207. #
  208. # snd_open(chns)
  209. # snd_close
  210. # snd_trajectory(chn, label)
  211. # snd_velocity(chn, label)
  212. # snd_doppler(chn, label)
  213. # snd_acceleration(chn, label)
  214. # snd_plot
  215. #
  216. # DL.make_dlocsig(start, dur, *args)
  217. # DL.dlocsig(dl, loc, input)
  218. #
  219. # DL.make_path(path, *args)
  220. # DL.make_polar_path(path, *args)
  221. # DL.make_closed_path(path, *args)
  222. # DL.make_literal_path(path, *args)
  223. # DL.make_literal_polar_path(path, *args)
  224. # DL.make_spiral_path(*args)
  225. #
  226. # class Dlocsig_menu
  227. # initialize(label, snd_p)
  228. # post_dialog
  229. # Code:
  230. require "ws"
  231. require "matrix"
  232. include Math
  233. provided?(:snd_motif) and (not provided?(:xm)) and require("libxm.so")
  234. provided?(:snd_gtk) and (not provided?(:xg)) and require("libxg.so")
  235. class DlocsigError < StandardError
  236. end
  237. Ruby_exceptions[:dlocsig_error] = DlocsigError
  238. def dl_error(*msg)
  239. Snd.raise(:dlocsig_error, (msg.empty? ? "" : format(*msg)))
  240. end
  241. # module Inject, see Thomas, David, Hunt, Andrew: Programming Ruby --
  242. # The Pragmatic Programmer's Guide, 2001 Addison-Wesley, page 102n
  243. module Inject
  244. def inject(n)
  245. each do |x| n = yield(n, x) end
  246. n
  247. end
  248. def sum(initial = 0)
  249. inject(initial) do |n, v| n + v end
  250. end
  251. def product(initial = 1)
  252. inject(initial) do |n, v| n * v end
  253. end
  254. end unless defined? Inject
  255. # used by plotting curves
  256. # to_trias: [0, 1, 2, 3, 4, 5] --> [[0, 1, 2], [3, 4, 5]]
  257. # to_path: [[-10, 10], [0, 5], [10, 10]].to_path <=> DL.make_path([[-10, 10], [0, 5], [10, 10]])
  258. # uses the same options as DL.make_path()
  259. class Array
  260. include Inject
  261. def to_trias
  262. ary = []
  263. unless self.length.divmod(3).last.nonzero?
  264. 0.step(self.length - 2, 3) do |i|
  265. ary.push([self[i], self[i + 1], self[i + 2]])
  266. end
  267. end
  268. ary
  269. end
  270. def to_path(*args)
  271. DL.make_path(self, *args)
  272. end
  273. end
  274. class Sndplot
  275. def initialize(chns = 1)
  276. @chns = chns
  277. @snd = open
  278. end
  279. attr_reader :snd
  280. def inspect
  281. format("#<%s: snd: %d, chns: %d>", self.class, @snd, @chns)
  282. end
  283. def open
  284. if snds = sounds()
  285. snds.each do |s| set_sound_property(:selected, false, s) end
  286. set_sound_property(:selected, true, selected_sound)
  287. end
  288. if @snd = snds.detect do |s| channels(s) >= @chns end
  289. set_sound_property(:dlocsig_created, false, @snd)
  290. select_sound(@snd)
  291. else
  292. @snd = new_sound(snd_tempnam, @chns, default_output_srate,
  293. default_output_sample_type,
  294. default_output_header_type)
  295. set_sound_property(:dlocsig_created, true, @snd)
  296. end
  297. channels(@snd).times do |chn|
  298. set_channel_property(:time_graph, time_graph?(@snd, chn), @snd, chn)
  299. set_channel_property(:transform_graph, transform_graph?(@snd, chn), @snd, chn)
  300. set_channel_property(:lisp_graph, lisp_graph?(@snd, chn), @snd, chn)
  301. set_time_graph?(false, @snd, chn)
  302. set_transform_graph?(false, @snd, chn)
  303. set_lisp_graph?(true, @snd, chn)
  304. end
  305. $exit_hook.add_hook!("dlocsig-hook") do | |
  306. close
  307. false
  308. end
  309. @snd
  310. end
  311. def close
  312. if snds = sounds()
  313. snds.each do |snd|
  314. set_sound_property(:selected, false, snd)
  315. unless sound_property(:dlocsig_created, snd).nil?
  316. if sound_property(:dlocsig_created, snd)
  317. close_sound_extend(snd)
  318. else
  319. channels(snd).times do |chn|
  320. set_time_graph?(channel_property(:time_graph, snd, chn), snd, chn)
  321. set_transform_graph?(channel_property(:transform_graph, snd, chn), snd, chn)
  322. set_lisp_graph?(channel_property(:lisp_graph, snd, chn), snd, chn)
  323. end
  324. end
  325. end
  326. end
  327. end
  328. $exit_hook.remove_hook!("dlocsig-hook")
  329. self
  330. end
  331. end
  332. class Gnuplot
  333. @@plot_stream = nil
  334. def initialize
  335. if (not @@plot_stream) or @@plot_stream.closed?
  336. open
  337. end
  338. end
  339. def inspect
  340. format("#<%s: plot_stream: %s>", self.class, @@plot_stream.inspect)
  341. end
  342. def open
  343. if (gnuplot = `which gnuplot`).empty?
  344. dl_error("gnuplot not found?")
  345. else
  346. @@plot_stream = IO.popen(gnuplot, "w")
  347. end
  348. end
  349. def close
  350. unless @@plot_stream.closed?
  351. @@plot_stream.puts("quit")
  352. @@plot_stream.close
  353. @@plot_stream = nil
  354. end
  355. end
  356. def command(*args)
  357. open if @@plot_stream.closed?
  358. @@plot_stream.printf(*args)
  359. format(*args).chomp
  360. rescue
  361. Snd.warning("%s#%s", self.class, get_func_name)
  362. end
  363. def reset
  364. command "reset\n"
  365. end
  366. def set_autoscale
  367. command "set autoscale\n"
  368. end
  369. def set_x_range(range = [])
  370. command("set xrange [%f:%f]\n", range[0], range[1]) if range.length == 2
  371. end
  372. def set_y_range(range = [])
  373. command("set yrange [%f:%f]\n", range[0], range[1]) if range.length == 2
  374. end
  375. def set_z_range(range = [])
  376. command("set zrange [%f:%f]\n", range[0], range[1]) if range.length == 2
  377. end
  378. def set_grid
  379. command "set grid xtics; set grid ytics; set grid ztics\n"
  380. end
  381. def set_surface
  382. command "set surface\n"
  383. end
  384. def set_parametric
  385. command "set parametric\n"
  386. end
  387. def set_ticslevel(level = 0)
  388. command("set ticslevel %.2f\n", level)
  389. end
  390. def set_title(title = "")
  391. command("set title \"%s\"\n", title) unless title.empty?
  392. end
  393. def set_label(label = "")
  394. command("set label \"%s\"\n", label) unless label.empty?
  395. end
  396. def set_margins(margin = 1)
  397. command("set tmargin %f\n", margin)
  398. command("set lmargin %f\n", margin)
  399. command("set rmargin %f\n", margin)
  400. command("set bmargin %f\n", margin)
  401. end
  402. def set_border(border = nil)
  403. command("set border %d\n", border.to_i) if border
  404. end
  405. def start_multiplot
  406. command "set multiplot\n"
  407. end
  408. def end_multiplot
  409. command "set nomultiplot\n"
  410. end
  411. def size(xorigin, yorigin, xsize, ysize)
  412. command("set origin %f,%f\n", xorigin.to_f, yorigin.to_f)
  413. command("set size %f,%f\n", xsize.to_f, ysize.to_f)
  414. end
  415. def data(data, *args)
  416. style, label = nil
  417. optkey(args, binding,
  418. [:style, "linespoints"],
  419. [:label, ""])
  420. command("plot '-' %s %s\n", label.empty? ? "" : "title \"#{label}\"",
  421. style.empty? ? "" : "with #{style}")
  422. data.each_with_index do |y, x| command("%f %f\n", x, y) end
  423. command "e\n"
  424. end
  425. def plot_2d_curve(curve, *args)
  426. style, label = nil
  427. optkey(args, binding,
  428. [:style, "linespoints"],
  429. [:label, ""])
  430. set_grid()
  431. command("plot '-' %s %s\n", label.empty? ? "" : "title \"#{label}\"",
  432. style.empty? ? "" : "with #{style}")
  433. curve.each_pair do |x, y| command("%.8f %.8f\n", x, y) end
  434. command "e\n"
  435. end
  436. def plot_2d_curves(curves, *args)
  437. styles, labels = nil
  438. optkey(args, binding,
  439. [:styles, "linespoints"],
  440. [:labels, ""])
  441. set_grid()
  442. styles = curves.map do |i| styles end unless array?(styles)
  443. labels = curves.map do |i| labels end unless array?(labels)
  444. command "plot"
  445. curves.each_with_index do |x, i|
  446. style = styles[i]
  447. label = labels[i]
  448. command " '-' "
  449. command(" title \"%s\"", label) if label or (not label.empty?)
  450. command(" with %s", style) if style or (not style.empty?)
  451. command(", ") if i != (curves.length - 1)
  452. end
  453. command "\n"
  454. curves.each do |curve|
  455. curve.each_pair do |x, y| command("%.8f %.8f\n", x, y) end
  456. command "e\n"
  457. end
  458. end
  459. def plot_3d_curve(curve, *args)
  460. style, label, zstyle, xrot, zrot, scale, zscale = nil
  461. optkey(args, binding,
  462. [:style, "linespoints"],
  463. [:label, ""],
  464. [:zstyle, "impulses"],
  465. :xrot,
  466. :zrot,
  467. :scale,
  468. :zscale)
  469. set_border(127 + 256 + 512)
  470. set_grid()
  471. set_surface()
  472. set_parametric()
  473. set_ticslevel(0)
  474. if xrot or zrot or scale or zscale
  475. command("set view %s,%s,%s,%s\n", xrot, zrot, scale, zscale)
  476. end
  477. command "splot '-'"
  478. command(" title \"%s\"", label) unless label.empty?
  479. command(" with %s 1", style) unless style.empty?
  480. command(", '-' notitle with %s 1", zstyle) unless zstyle.empty?
  481. command "\n"
  482. curve.to_trias.each do |x, y, z| command("%.8f %.8f %.8f\n", x, y, z) end
  483. command "e\n"
  484. if zstyle
  485. curve.to_trias.each do |x, y, z| command("%.8f %.8f %.8f\n", x, y, z) end
  486. command "e\n"
  487. end
  488. end
  489. end
  490. module DL
  491. Path_maxcoeff = 8
  492. Point707 = cos(TWO_PI / 8.0)
  493. Amplitude_panning = 1
  494. B_format_ambisonics = 2
  495. Decoded_ambisonics = 3
  496. def which_render(val)
  497. case val
  498. when :amplitude_panning, Amplitude_panning
  499. Amplitude_panning
  500. when :b_format_ambisonics, B_format_ambisonics
  501. B_format_ambisonics
  502. when :decoded_ambisonics, Decoded_ambisonics
  503. Decoded_ambisonics
  504. else
  505. Amplitude_panning
  506. end
  507. end
  508. def cis(r)
  509. Complex(cos(r), sin(r))
  510. end
  511. def distance(x, y, z)
  512. sqrt(x * x + y * y + z * z)
  513. end
  514. def nearest_point(x0, y0, z0, x1, y1, z1, px, py, pz)
  515. if same?(x0, y0, z0, px, py, pz)
  516. [x0, y0, z0]
  517. elsif same?(x1, y1, z1, px, py, pz)
  518. [x1, y1, z1]
  519. elsif same?(x0, y0, z0, x1, y1, z1)
  520. [x0, y0, z0]
  521. else
  522. xm0 = x1 - x0
  523. ym0 = y1 - y0
  524. zm0 = z1 - z0
  525. xm1 = px - x0
  526. ym1 = py - y0
  527. zm1 = pz - z0
  528. d0 = distance(xm0, ym0, zm0)
  529. d1 = distance(xm1, ym1, zm1)
  530. p = d1 * ((xm0 * xm1 + ym0 * ym1 + zm0 * zm1) / (d0 * d1))
  531. ratio = p / d0
  532. [x0 + xm0 * ratio, y0 + ym0 * ratio, z0 + zm0 * ratio]
  533. end
  534. end
  535. def same?(a0, b0, c0, a1, b1, c1)
  536. a0 == a1 and b0 == b1 and c0 == c1
  537. end
  538. def rotation_matrix(x, y, z, angle)
  539. mag = distance(x, y, z)
  540. dx = x / mag
  541. dy = y / mag
  542. dz = z / mag
  543. ri = Matrix.I(3)
  544. ra = Matrix.rows([[0.0, dz, -dy], [-dz, 0.0, dx], [dy, -dx, 0.0]])
  545. raa = ra * ra
  546. sn = sin(-angle)
  547. omcs = 1 - cos(-angle)
  548. raa = raa.map do |xx| omcs * xx end
  549. ra = ra.map do |xx| sn * xx end
  550. (ri + ra + raa)
  551. end
  552. class Dlocsig_base
  553. include DL
  554. def initialize
  555. @one_turn = 360.0
  556. @speed_of_sound = 344.0
  557. end
  558. attr_accessor :one_turn, :speed_of_sound
  559. def angles_in_degree
  560. @one_turn = 360.0
  561. end
  562. def angles_in_radians
  563. @one_turn = TWO_PI
  564. end
  565. def angles_in_turns
  566. @one_turn = 1.0
  567. end
  568. def distances_in_meters
  569. @speed_of_sound = 344.0
  570. end
  571. def distances_in_feet
  572. @speed_of_sound = 1128.0
  573. end
  574. end
  575. class Speaker_config < Dlocsig_base
  576. Groups = Struct.new("Groups", :size, :vertices, :speakers, :matrix)
  577. def initialize
  578. super
  579. @number = nil
  580. @coords = nil
  581. @groups = nil
  582. end
  583. protected
  584. def set_speakers(channels, d3)
  585. if channels.between?(1, 8)
  586. d3 = false if channels < 4
  587. arrange_speakers(unless d3
  588. case channels
  589. when 1
  590. [[0]]
  591. when 2
  592. [[-60, 60]]
  593. when 3
  594. [[-45, 45, 180]]
  595. when 4
  596. [[-45, 45, 135, 225]]
  597. when 5
  598. [[-45, 0, 45, 135, -135]]
  599. when 6
  600. [[-60, 0, 60, 120, 180, 240]]
  601. when 7
  602. [[-45, 0, 45, 100, 140, -140, -100]]
  603. when 8
  604. [[-22.5, 22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5]]
  605. end
  606. else
  607. case channels
  608. when 4
  609. [[[-60, 0], [60, 0], [180, 0], [0, 90]],
  610. [[0, 1, 3], [1, 2, 3], [2, 0, 3], [0, 1, 2]]]
  611. when 5
  612. [[[-45, 0], [45, 0], [135, 0], [-135, 0], [0, 90]],
  613. [[0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4], [0, 1, 2], [2, 3, 0]]]
  614. when 6
  615. [[[-45, 0], [45, 0], [135, 0], [-135, 0], [-90, 60], [90, 60]],
  616. [[0, 1, 4], [1, 4, 5], [1, 2, 5], [2, 3, 5],
  617. [3, 4, 5], [3, 0, 4], [0, 1, 2], [2, 3, 0]]]
  618. when 7
  619. [[[-45, 0], [45, 0], [135, 0], [-135, 0],
  620. [-60, 60], [60, 60], [180, 60]],
  621. [[0, 1, 4], [1, 4, 5], [1, 2, 5], [2, 6, 5], [2, 3, 6],
  622. [3, 4, 6], [3, 0, 4], [4, 5, 6], [0, 1, 2], [2, 3, 0]]]
  623. when 8
  624. [[[-45, 10], [45, -10], [135, -10], [225, -10],
  625. [-45, 45], [45, 45], [135, 45], [225, 45]],
  626. [[0, 4, 5], [0, 5, 1], [5, 1, 2], [2, 6, 5], [6, 7, 2], [2, 3, 7],
  627. [3, 7, 4], [3, 0, 4], [4, 7, 6], [6, 5, 4], [0, 1, 2], [2, 3, 0]]]
  628. end
  629. end)
  630. else
  631. dl_error(channels, "only 1 to 8 channels possible")
  632. end
  633. end
  634. private
  635. def arrange_speakers(args)
  636. speakers = args.shift
  637. groups = args.shift
  638. @number = speakers.length
  639. @coords = speakers.map do |s|
  640. a = (array?(s) ? s[0] : s).to_f
  641. e = (array?(s) ? s[1] : 0).to_f
  642. evec = cis((e / @one_turn) * TWO_PI)
  643. dxy = evec.real
  644. avec = cis((a / @one_turn) * TWO_PI)
  645. x = (dxy * avec.imag)
  646. y = (dxy * avec.real)
  647. z = evec.imag
  648. mag = distance(x, y, z)
  649. [x / mag, y / mag, z / mag]
  650. end
  651. unless groups
  652. if @number == 1
  653. groups = [[0]]
  654. else
  655. groups = make_array(@number) do |i| [i, i + 1] end
  656. groups[-1][-1] = 0
  657. end
  658. end
  659. @groups = groups.map do |group|
  660. size = group.length
  661. vertices = group.map do |vertice| @coords[vertice] end
  662. matrix = case size
  663. when 3
  664. if (m = Matrix[vertices[0], vertices[1], vertices[2]]).regular?
  665. m.inverse.to_a
  666. else
  667. nil
  668. end
  669. when 2
  670. if (m = Matrix[vertices[0][0, 2], vertices[1][0, 2]]).regular?
  671. m.inverse.to_a
  672. else
  673. nil
  674. end
  675. else
  676. nil
  677. end
  678. Groups.new(size, vertices, group, matrix)
  679. end
  680. end
  681. end
  682. class Dlocsig < Speaker_config
  683. def initialize
  684. super
  685. @render_using = Amplitude_panning
  686. @output_power = 1.5
  687. @reverb_power = 0.5
  688. @rbm_output = nil
  689. @rbm_reverb = nil
  690. @out_channels = 4
  691. @rev_channels = 1
  692. @clm = true
  693. @delay = []
  694. @prev_time = @prev_dist = @prev_group = @prev_x = @prev_y = @prev_z = false
  695. @first_dist = @last_dist = 0.0
  696. @min_dist = @max_dist = 0.0
  697. @start = nil
  698. @end = nil
  699. @output_gains = nil
  700. @reverb_gains = nil
  701. @path = nil
  702. @run_beg = @run_end = nil
  703. end
  704. attr_reader :run_beg, :run_end, :out_channels, :rev_channels
  705. def inspect
  706. format("#<%s: channels: %d, reverb: %s>", self.class, @out_channels, @rev_channels.inspect)
  707. end
  708. def each
  709. (@run_beg...@run_end).each do |i| yield(i) end
  710. end
  711. alias run each
  712. # general clm version
  713. # dl.dlocsig(loc, val)
  714. def dlocsig(loc, input)
  715. if loc < @start
  716. delay(@path, (loc >= @end ? 0.0 : input), 0.0)
  717. @out_channels.times do |chn| out_any(loc, 0.0, chn, @rbm_output) end
  718. else
  719. sample = delay(@path, (loc >= @end ? 0.0 : input), env(@delays))
  720. @out_channels.times do |chn|
  721. out_any(loc, sample * env(@output_gains[chn]), chn, @rbm_output)
  722. end
  723. @rev_channels.times do |chn|
  724. out_any(loc, sample * env(@reverb_gains[chn]), chn, @rbm_reverb)
  725. end
  726. end
  727. end
  728. # dl.ws_dlocsig do |loc| ...; val; end
  729. # @clm == true @rbm_output/@rbm_reverb: sample2files
  730. # @clm == false @rbm_output/@rbm_reverb: sound index numbers
  731. # With_sound#run_dlocsig below uses this method
  732. def ws_dlocsig
  733. len = @run_end - @run_beg
  734. out_data = make_vct(len)
  735. len.times do |i|
  736. loc = i + @run_beg
  737. input = yield(loc)
  738. if loc < @start
  739. delay(@path, (loc >= @end ? 0.0 : input), 0.0)
  740. else
  741. out_data[i] = delay(@path, (loc >= @end ? 0.0 : input), env(@delays))
  742. end
  743. end
  744. @out_channels.times do |chn|
  745. if @clm
  746. (@run_beg...@run_end).each do |i|
  747. out_any(i, out_data[i - @run_beg] * env(@output_gains[chn]), chn, @rbm_output)
  748. end
  749. else
  750. out = vct_multiply!(vct_copy(out_data),
  751. Vct.new(len) do |x| env(@output_gains[chn]) end)
  752. mix_vct(out, @run_beg, @rbm_output, chn, false)
  753. end
  754. end
  755. @rev_channels.times do |chn|
  756. if @clm
  757. (@run_beg...@run_end).each do |i|
  758. out_any(i, out_data[i - @run_beg] * env(@reverb_gains[chn]), chn, @rbm_reverb)
  759. end
  760. else
  761. out = vct_multiply!(vct_copy(out_data),
  762. Vct.new(len) do |x| env(@reverb_gains[chn]) end)
  763. mix_vct(out, @run_beg, @rbm_reverb, chn, false)
  764. end
  765. end
  766. end
  767. # :amplitude_panning
  768. # :b_format_ambisonics
  769. # :decoded_ambisonics
  770. def make_dlocsig(startime, dur, *args)
  771. path, scaler, reverb_amount, output_power, reverb_power, render_using = nil
  772. rbm_output, rbm_reverb, out_channels, rev_channels, clm = nil
  773. optkey(args, binding,
  774. :path,
  775. [:scaler, 1.0],
  776. [:reverb_amount, 0.05],
  777. [:output_power, 1.5],
  778. [:reverb_power, 0.5],
  779. [:render_using, Amplitude_panning],
  780. [:rbm_output, $output],
  781. [:rbm_reverb, $reverb],
  782. [:out_channels, 4],
  783. [:rev_channels, 1],
  784. [:clm, true])
  785. @output_power = output_power
  786. @reverb_power = reverb_power
  787. @render_using = which_render(render_using)
  788. @rbm_output = rbm_output
  789. @rbm_reverb = rbm_reverb
  790. @out_channels = out_channels
  791. @rev_channels = rev_channels
  792. @clm = clm
  793. if @render_using == B_format_ambisonics and @out_channels != 4
  794. dl_error("B_format_ambisonics requires 4 output channels")
  795. end
  796. if @render_using == B_format_ambisonics and
  797. @rev_channels.nonzero? and
  798. (@rev_channels != 1 and @rev_channels != 4)
  799. dl_error("B_format_ambisonics accepts only 0, 1 or 4 rev_channels")
  800. end
  801. if @rev_channels > @out_channels
  802. dl_error("more rev_channels than out_channels")
  803. end
  804. if @render_using == B_format_ambisonics
  805. scaler *= 0.8
  806. end
  807. unless path.kind_of?(Path)
  808. if array?(path) and !path.empty?
  809. path = make_path(path)
  810. else
  811. dl_error(path, "sorry, need a path")
  812. end
  813. end
  814. xpoints = path.path_x
  815. ypoints = path.path_y
  816. zpoints = path.path_z
  817. tpoints = path.path_time
  818. @channel_gains = make_array(@out_channels) do [] end
  819. @channel_rev_gains = make_array(@rev_channels) do [] end
  820. self.set_speakers(@out_channels, (not zpoints.detect do |x| x.nonzero? end.nil?))
  821. @speed_limit = (@speed_of_sound * (tpoints[-1] - tpoints[0])) / dur
  822. if xpoints.length == 1
  823. walk_all_rooms(xpoints[0], ypoints[0], zpoints[0], tpoints[0])
  824. else
  825. xb = yb = zb = tb = 0.0
  826. (tpoints.length - 1).times do |i|
  827. xa, xb = xpoints[i, 2]
  828. ya, yb = ypoints[i, 2]
  829. za, zb = zpoints[i, 2]
  830. ta, tb = tpoints[i, 2]
  831. minimum_segment_length(xa, ya, za, ta, xb, yb, zb, tb)
  832. end
  833. walk_all_rooms(xb, yb, zb, tb)
  834. end
  835. @start = dist2samples(@first_dist - @min_dist)
  836. @end = seconds2samples(startime + dur)
  837. min_delay = dist2samples(@min_dist)
  838. @run_beg = seconds2samples(startime)
  839. @run_end = @end + dist2samples(@last_dist) - min_delay
  840. real_dur = dur + dist2seconds(@last_dist - @first_dist)
  841. min_dist_unity = [@min_dist, 1.0].max
  842. unity_gain = scaler * min_dist_unity ** @output_power
  843. @output_gains = make_array(@number) do |i|
  844. make_env(:envelope, @channel_gains[i], :scaler, unity_gain, :duration, real_dur)
  845. end
  846. if @rev_channels.nonzero?
  847. unity_rev_gain = reverb_amount * scaler * min_dist_unity ** @reverb_power
  848. @reverb_gains = make_array(@rev_channels) do |i|
  849. make_env(:envelope, @channel_rev_gains[i], :scaler, unity_rev_gain, :duration, real_dur)
  850. end
  851. end
  852. @delays = make_env(:envelope, @delay, :offset, -min_delay, :duration, real_dur)
  853. @path = make_delay(:size, 1, :max_size, [1, dist2samples(@max_dist)].max)
  854. self
  855. end
  856. private
  857. def dist2samples(d)
  858. (d * (mus_srate() / @speed_of_sound)).round
  859. end
  860. def dist2seconds(d)
  861. d / @speed_of_sound.to_f
  862. end
  863. def transition_point_3(vert_a, vert_b, xa, ya, za, xb, yb, zb)
  864. line_b = vct(xa, ya, za)
  865. line_m = tr3_sub(vct(xb, yb, zb), line_b)
  866. normal = tr3_cross(vert_a, vert_b)
  867. if (denominator = tr3_dot(normal, line_m)).abs <= 0.000001
  868. false
  869. else
  870. vct2list(tr3_add(line_b, tr3_scale(line_m, -tr3_dot(normal, line_b) / denominator)))
  871. end
  872. end
  873. def tr3_cross(v1, v2)
  874. vct(v1[1] * v2[2] - v1[2] * v2[1],
  875. v1[2] * v2[0] - v1[0] * v2[2],
  876. v1[0] * v2[1] - v1[1] * v2[0])
  877. end
  878. def tr3_dot(v1, v2)
  879. dot_product(v1, v2)
  880. end
  881. def tr3_sub(v1, v2)
  882. vct_subtract!(vct_copy(v1), v2)
  883. end
  884. def tr3_add(v1, v2)
  885. vct_add!(vct_copy(v1), v2)
  886. end
  887. def tr3_scale(v1, c)
  888. vct_scale!(vct_copy(v1), c)
  889. end
  890. def transition_point_2(vert, xa, ya, xb, yb)
  891. ax = vert[0]
  892. bx = xa - xb
  893. ay = vert[1]
  894. by = ya - yb
  895. cx = -xa
  896. cy = -ya
  897. d = by * cx - bx * cy
  898. f = ay * bx - ax * by
  899. if f.zero?
  900. false
  901. else
  902. [(d * ax) / f, (d * ay) / f]
  903. end
  904. end
  905. def calculate_gains(x, y, z, group)
  906. zero_coord = 1e-10
  907. zero_gain = 1e-10
  908. size = group.size
  909. if mat = group.matrix
  910. if x.abs < zero_coord and y.abs < zero_coord and z.abs < zero_coord
  911. [true, [1.0, 1.0, 1.0]]
  912. else
  913. case size
  914. when 3
  915. gain_a = mat[0][0] * x + mat[0][1] * y + mat[0][2] * z
  916. gain_b = mat[1][0] * x + mat[1][1] * y + mat[1][2] * z
  917. gain_c = mat[2][0] * x + mat[2][1] * y + mat[2][2] * z
  918. mag = distance(gain_a, gain_b, gain_c)
  919. if gain_a.abs < zero_gain then gain_a = 0.0 end
  920. if gain_b.abs < zero_gain then gain_b = 0.0 end
  921. if gain_c.abs < zero_gain then gain_c = 0.0 end
  922. [(gain_a >= 0 and gain_b >= 0 and gain_c >= 0),
  923. [gain_a / mag, gain_b / mag, gain_c / mag]]
  924. when 2
  925. gain_a = mat[0][0] * x + mat[0][1] * y
  926. gain_b = mat[1][0] * x + mat[1][1] * y
  927. mag = distance(gain_a, gain_b, 0.0)
  928. if gain_a.abs < zero_gain then gain_a = 0.0 end
  929. if gain_b.abs < zero_gain then gain_b = 0.0 end
  930. [(gain_a >= 0 and gain_b >= 0), [gain_a / mag, gain_b / mag]]
  931. when 1
  932. [true, [1.0]]
  933. end
  934. end
  935. else
  936. [true, [1.0, 1.0, 1.0]]
  937. end
  938. end
  939. def find_group(x, y, z)
  940. grp = gns = false
  941. @groups.detect do |group|
  942. inside, gains = calculate_gains(x, y, z, group)
  943. if inside
  944. grp, gns = group, gains
  945. true
  946. end
  947. end
  948. [grp, gns]
  949. end
  950. def push_zero_gains(time)
  951. @out_channels.times do |i| @channel_gains[i].push(time, 0.0) end
  952. @rev_channels.times do |i| @channel_rev_gains[i].push(time, 0.0) end
  953. end
  954. def push_gains(group, gains, dist, time)
  955. outputs = make_vct(@out_channels)
  956. revputs = if @rev_channels > 0
  957. make_vct(@rev_channels)
  958. else
  959. false
  960. end
  961. if dist >= 1.0
  962. att = 1.0 / dist ** @output_power
  963. ratt = 1.0 / dist ** @reverb_power
  964. else
  965. att = 1.0 - dist ** (1.0 / @output_power)
  966. ratt = 1.0 - dist ** (1.0 / @reverb_power)
  967. end
  968. if dist >= 1.0
  969. group.speakers.each_with_index do |speaker, i|
  970. gain = gains[i]
  971. outputs[speaker] = gain * att
  972. if @rev_channels > 1
  973. revputs[speaker] = gain * ratt
  974. end
  975. end
  976. else
  977. @number.times do |speaker|
  978. if found = group.speakers.index(speaker)
  979. gain = gains[found]
  980. outputs[speaker] = gain + (1.0 - gain) * att
  981. if @rev_channels > 1
  982. revputs[speaker] = gain + (1.0 - gain) * ratt
  983. end
  984. else
  985. outputs[speaker] = att
  986. if @rev_channels > 1
  987. revputs[speaker] = ratt
  988. end
  989. end
  990. end
  991. end
  992. vct2list(outputs).each_with_index do |val, i| @channel_gains[i].push(time, val) end
  993. if @rev_channels == 1
  994. @channel_rev_gains[0].push(time, ratt)
  995. elsif @rev_channels > 1
  996. vct2list(revputs).each_with_index do |val, i| @channel_rev_gains[i].push(time, val) end
  997. end
  998. end
  999. def amplitude_panning(x, y, z, dist, time)
  1000. if @prev_group
  1001. if time != @prev_time and ((dist - @prev_dist) / (time - @prev_time)) > @speed_limit
  1002. Snd.display("%s#%s: supersonic radial movement", self.class, get_func_name)
  1003. end
  1004. inside, gains = calculate_gains(x, y, z, @prev_group)
  1005. if inside
  1006. push_gains(@prev_group, gains, dist, time)
  1007. @prev_x, @prev_y, @prev_z = x, y, z
  1008. else
  1009. group, gains = find_group(x, y, z)
  1010. if group
  1011. edge = group.vertices & @prev_group.vertices
  1012. if edge.length == 2
  1013. if pint = transition_point_3(edge[0], edge[1], x, y, z, @prev_x, @prev_y, @prev_z)
  1014. xi, yi, zi = pint
  1015. di = distance(xi, yi, zi)
  1016. ti = @prev_time +
  1017. (distance(xi - @prev_x, yi - @prev_y, zi - @prev_z) / \
  1018. distance(x - @prev_x, y - @prev_y, z - @prev_z)) * (time - @prev_time)
  1019. if ti < @prev_time
  1020. inside, gains = calculate_gains(xi, yi, zi, @prev_group)
  1021. if inside
  1022. push_gains(@prev_group, gains, di, ti)
  1023. else
  1024. inside, gains = calculate_gains(xi, yi, zi, group)
  1025. if inside
  1026. push_gains(group, gains, di, ti)
  1027. else
  1028. dl_error("outside of both adjacent groups")
  1029. end
  1030. end
  1031. else
  1032. if $DEBUG
  1033. Snd.warning("%s#%s: current time <= previous time", self.class, get_func_name)
  1034. end
  1035. end
  1036. end
  1037. elsif edge.length == 1 and group.size == 2
  1038. if pint = transition_point_2(edge[0], x, y, @prev_x, @prev_y)
  1039. xi, yi = pint
  1040. di = distance(xi, yi, 0.0)
  1041. ti = @prev_time +
  1042. (distance(xi - @prev_x, yi - @prev_y, 0.0) / \
  1043. distance(x - @prev_x, y - @prev_y, 0.0)) * (time - @prev_time)
  1044. if ti < @prev_time
  1045. inside, gains = calculate_gains(xi, yi, 0.0, @prev_group)
  1046. if inside
  1047. push_gains(@prev_group, gains, di, ti)
  1048. inside, gains = calculate_gains(xi, yi, 0.0, group)
  1049. if inside
  1050. push_gains(group, gains, di, ti)
  1051. else
  1052. dl_error("outside of both adjacent groups")
  1053. end
  1054. end
  1055. else
  1056. if $DEBUG
  1057. Snd.warning("%s#%s: current time <= previous time", self.class, get_func_name)
  1058. end
  1059. end
  1060. end
  1061. elsif edge.length == 1
  1062. Snd.display("%s#%s: only one point in common", self.class, get_func_name)
  1063. elsif edge.length.zero?
  1064. Snd.display("%s#%s: with no common points", self.class, get_func_name)
  1065. end
  1066. push_gains(group, gains, dist, time)
  1067. @prev_group, @prev_x, @prev_y, @prev_z = group, x, y, z
  1068. else
  1069. push_zero_gains(time)
  1070. @prev_group = false
  1071. end
  1072. end
  1073. else
  1074. group, gains = find_group(x, y, z)
  1075. if group
  1076. push_gains(group, gains, dist, time)
  1077. @prev_group, @prev_x, @prev_y, @prev_z = group, x, y, z
  1078. else
  1079. push_zero_gains(time)
  1080. @prev_group = false
  1081. end
  1082. end
  1083. @prev_time = time
  1084. @prev_dist = dist
  1085. end
  1086. def b_format_ambisonics(x, y, z, dist, time)
  1087. if dist > 1.0
  1088. att = (1.0 / dist) ** @output_power
  1089. @channel_gains[0].push(time, Point707 * att)
  1090. @channel_gains[1].push(time, (y / dist) * att)
  1091. @channel_gains[2].push(time, (-x / dist) * att)
  1092. @channel_gains[3].push(time, (z / dist) * att)
  1093. if @rev_channels == 1
  1094. @channel_rev_gains[0].push(time, 1.0 / (dist ** @reverb_power))
  1095. elsif @rev_channels == 4
  1096. ratt = (1.0 / dist) ** @reverb_power
  1097. @channel_rev_gains[0].push(time, Point707 * ratt)
  1098. @channel_rev_gains[1].push(time, (y / dist) * ratt)
  1099. @channel_rev_gains[2].push(time, (-x / dist) * ratt)
  1100. @channel_rev_gains[3].push(time, (z / dist) * ratt)
  1101. end
  1102. elsif dist.zero?
  1103. @channel_gains[0].push(time, 1.0)
  1104. (1..3).each do |i| @channel_gains[i].push(time, 0.0) end
  1105. if @rev_channels >= 1
  1106. @channel_rev_gains[0].push(time, 1.0)
  1107. end
  1108. if @rev_channels == 4
  1109. (1..3).each do |i| @channel_rev_gains[i].push(time, 0.0) end
  1110. end
  1111. else
  1112. att = dist ** (1.0 / @output_power)
  1113. @channel_gains[0].push(time, 1.0 - (1.0 - Point707) * dist ** @output_power)
  1114. @channel_gains[1].push(time, (y / dist) * att)
  1115. @channel_gains[2].push(time, (-x / dist) * att)
  1116. @channel_gains[3].push(time, (z / dist) * att)
  1117. if @rev_channels == 1
  1118. @channel_rev_gains[0].push(time, 1.0 - dist ** (1.0 / @reverb_power))
  1119. elsif @rev_channels == 4
  1120. ratt = dist ** (1.0 / @reverb_power)
  1121. @channel_rev_gains[0].push(time, 1.0 - (1.0 - Point707) * dist ** @reverb_power)
  1122. @channel_rev_gains[1].push(time, (y / dist) * ratt)
  1123. @channel_rev_gains[2].push(time, (-x / dist) * ratt)
  1124. @channel_rev_gains[3].push(time, (z / dist) * ratt)
  1125. end
  1126. end
  1127. end
  1128. def decoded_ambisonics(x, y, z, dist, time)
  1129. if dist > 1.0
  1130. att = (1.0 / dist) ** @output_power
  1131. attw = Point707 * Point707 * att
  1132. attx = att * (x / dist)
  1133. atty = att * (y / dist)
  1134. attz = att * (z / dist)
  1135. @coords.each_with_index do |s, i|
  1136. @channel_gains[i].push(time, Point707 * (attw + attx * s[0] + atty * s[1] + attz * s[2]))
  1137. end
  1138. if @rev_channels == 1
  1139. @channel_rev_gains[0].push(time, 1.0 / (dist ** @reverb_power))
  1140. elsif @rev_channels == 4
  1141. ratt = (1.0 / dist) ** @reverb_power
  1142. rattw = Point707 * Point707 * ratt
  1143. rattx = ratt * (x / dist)
  1144. ratty = ratt * (y / dist)
  1145. rattz = ratt * (z / dist)
  1146. @rev_channels.times do |i|
  1147. s = @coords[i]
  1148. @channel_rev_gains[i].push(time, Point707 * \
  1149. (rattw + rattx * s[0] + ratty * s[1] + rattz * s[2]))
  1150. end
  1151. end
  1152. elsif dist.zero?
  1153. att = Point707 * Point707
  1154. @coords.each_index do |i| @channel_gains[i].push(time, att) end
  1155. if @rev_channels == 1
  1156. @channel_rev_gains[0].push(time, 1.0)
  1157. else
  1158. @rev_channels.times do |i| @channel_rev_gains[i].push(time, att) end
  1159. end
  1160. else
  1161. att = dist ** (1.0 / @output_power)
  1162. attw = Point707 * (1.0 - (1.0 - Point707) * dist ** @output_power)
  1163. attx = att * (x / dist)
  1164. atty = att * (y / dist)
  1165. attz = att * (z / dist)
  1166. @coords.each_with_index do |s, i|
  1167. @channel_gains[i].push(time, Point707 * (attw + attx * s[0] + atty * s[1] + attz * s[2]))
  1168. end
  1169. if @rev_channels == 1
  1170. @channel_rev_gains[0].push(time, 1.0 - dist ** (1.0 / @reverb_power))
  1171. elsif @rev_channels == 4
  1172. ratt = dist ** (1.0 / @reverb_power)
  1173. rattw = Point707 * (1.0 - (1.0 - Point707) * dist ** @reverb_power)
  1174. rattx = ratt * (x / dist)
  1175. ratty = ratt * (y / dist)
  1176. rattz = ratt * (z / dist)
  1177. @rev_channels.times do |i|
  1178. s = @coords[i]
  1179. @channel_rev_gains[i].push(time, Point707 * \
  1180. (rattw + rattx * s[0] + ratty * s[1] + rattz * s[2]))
  1181. end
  1182. end
  1183. end
  1184. end
  1185. def walk_all_rooms(x, y, z, time)
  1186. dist = distance(x, y, z)
  1187. if @first_dist.zero?
  1188. @first_dist = dist
  1189. end
  1190. @last_dist = dist
  1191. if @min_dist.zero? or dist < @min_dist
  1192. @min_dist = dist
  1193. end
  1194. if @max_dist.zero? or dist > @max_dist
  1195. @max_dist = dist
  1196. end
  1197. @delay.push(time, dist2samples(dist))
  1198. case @render_using
  1199. when Amplitude_panning
  1200. amplitude_panning(x, y, z, dist, time)
  1201. when B_format_ambisonics
  1202. b_format_ambisonics(x, y, z, dist, time)
  1203. when Decoded_ambisonics
  1204. decoded_ambisonics(x, y, z, dist, time)
  1205. end
  1206. end
  1207. def change_direction(xa, ya, za, ta, xb, yb, zb, tb)
  1208. walk_all_rooms(xa, ya, za, ta)
  1209. if xa != xb or ya != yb or za != zb or ta != tb
  1210. xi, yi, zi = nearest_point(xa, ya, za, xb, yb, zb, 0.0, 0.0, 0.0)
  1211. if (((xa < xb) ? (xa <= xi and xi <= xb) : (xb <= xi and xi <= xa)) and
  1212. ((ya < yb) ? (ya <= yi and yi <= yb) : (yb <= yi and yi <= ya)) and
  1213. ((za < zb) ? (za <= zi and zi <= zb) : (zb <= zi and zi <= za)))
  1214. walk_all_rooms(xi, yi, zi,
  1215. tb + (ta - tb) * (distance(xb - xi, yb - yi, zb - zi) / \
  1216. distance(xb - xa, yb - ya, zb - za)))
  1217. end
  1218. end
  1219. end
  1220. def intersects_inside_radius(xa, ya, za, ta, xb, yb, zb, tb)
  1221. mag = distance(xb - xa, yb - ya, zb - za)
  1222. vx = (xb - xa) / mag
  1223. vy = (yb - ya) / mag
  1224. vz = (zb - za) / mag
  1225. bsq = xa * vx + ya * vy + za * vz
  1226. disc = bsq * bsq - ((xa * xa + ya * ya + za * za) - 1.0)
  1227. if disc >= 0.0
  1228. root = sqrt(disc)
  1229. rin = -bsq - root
  1230. rout = -bsq + root
  1231. xi = xo = nil
  1232. if rin > 0 and rin < mag
  1233. xi = xa + vx * rin
  1234. yi = ya + vy * rin
  1235. zi = za + vz * rin
  1236. ti = tb + (ta - tb) *
  1237. (distance(xb - xi, yb - yi, zb - zi) / distance(xb - xa, yb - ya, zb - za))
  1238. end
  1239. if rout > 0 and rout.abs < mag
  1240. xo = xa + vx * rout
  1241. yo = ya + vy * rout
  1242. zo = za + vz * rout
  1243. to = tb + (ta - tb) *
  1244. (distance(xb - xo, yb - yo, zb - zo) / distance(xb - xa, yb - ya, zb - za))
  1245. end
  1246. if xi
  1247. change_direction(xa, ya, za, ta, xi, yi, zi, ti)
  1248. if xo
  1249. change_direction(xi, yi, zi, ti, xo, yo, zo, to)
  1250. change_direction(xo, yo, zo, to, xb, yb, zb, tb)
  1251. else
  1252. change_direction(xi, yi, zi, ti, xb, yb, zb, tb)
  1253. end
  1254. else
  1255. if xo
  1256. change_direction(xa, ya, za, ta, xo, yo, zo, to)
  1257. change_direction(xo, yo, zo, to, xb, yb, zb, tb)
  1258. else
  1259. change_direction(xa, ya, za, ta, xb, yb, zb, tb)
  1260. end
  1261. end
  1262. else
  1263. change_direction(xa, ya, za, ta, xb, yb, zb, tb)
  1264. end
  1265. end
  1266. def minimum_segment_length(xa, ya, za, ta, xb, yb, zb, tb)
  1267. if distance(xb - xa, yb - ya, zb - za) < 1.0
  1268. intersects_inside_radius(xa, ya, za, ta, xb, yb, zb, tb)
  1269. else
  1270. xi = (xa + xb) * 0.5
  1271. yi = (ya + yb) * 0.5
  1272. zi = (za + zb) * 0.5
  1273. ti = tb + (ta - tb) *
  1274. (distance(xb - xi, yb - yi, zb - zi) / distance(xb - xa, yb - ya, zb - za))
  1275. minimum_segment_length(xa, ya, za, ta, xi, yi, zi, ti)
  1276. minimum_segment_length(xi, yi, zi, ti, xb, yb, zb, tb)
  1277. end
  1278. end
  1279. end
  1280. class Path < Dlocsig_base
  1281. def initialize
  1282. super
  1283. @rx = @ry = @rz = @rv = @rt = @tx = @ty = @tz = @tt = nil
  1284. @gnuplot = @sndplot = nil
  1285. end
  1286. def path_x
  1287. (@tx or (@rx or (render_path(); @rx)))
  1288. end
  1289. def path_y
  1290. (@ty or (@ry or (render_path(); @ry)))
  1291. end
  1292. def path_z
  1293. (@tz or (@rz or (render_path(); @rz)))
  1294. end
  1295. def path_time
  1296. (@tt or (@rt or (render_path(); @rt)))
  1297. end
  1298. def scale_path(scaling)
  1299. assert_type((number?(scaling) or array?(scaling)), 0, scaling, "a number or an array")
  1300. if number?(scaling) then scaling = [scaling, scaling, scaling] end
  1301. transform_path(:scaling, scaling)
  1302. end
  1303. def translate_path(translation)
  1304. assert_type((number?(translation) or array?(translation)),
  1305. 0, translation, "a number or an array")
  1306. if number?(translation) then translation = [translation, translation, translation] end
  1307. transform_path(:translation, translation)
  1308. end
  1309. def rotate_path(rotation, *args)
  1310. assert_type(number?(rotation), 0, rotation, "a number")
  1311. rotation_center, rotation_axis = nil
  1312. optkey(args, binding,
  1313. :rotation_center,
  1314. [:rotation_axis, [0.0, 0.0, 1.0]])
  1315. transform_path(:rotation, rotation,
  1316. :rotation_center, rotation_center,
  1317. :rotation_axis, rotation_axis)
  1318. end
  1319. def path_trajectory
  1320. path_x.map_with_index do |d, i| [d, path_y[i], path_z[i]] end.flatten
  1321. end
  1322. def path_2d_trajectory
  1323. path_x.map_with_index do |d, i| [d, path_y[i]] end.flatten
  1324. end
  1325. # if velocity is zero, ti == tf
  1326. Secure_distance = 0.0001
  1327. def path_velocity
  1328. xp, yp, zp, tp = path_x, path_y, path_z, path_time
  1329. (0...(tp.length - 1)).map do |i|
  1330. xi, xf = xp[i, 2]
  1331. yi, yf = yp[i, 2]
  1332. zi, zf = zp[i, 2]
  1333. ti, tf = tp[i, 2]
  1334. if tf == ti
  1335. tf += Secure_distance
  1336. end
  1337. [(ti + tf) / 2.0, distance(xf - xi, yf - yi, zf - zi) / (tf - ti)]
  1338. end.flatten
  1339. end
  1340. def path_doppler
  1341. xp, yp, zp, tp = path_x, path_y, path_z, path_time
  1342. (0...(tp.length - 1)).map do |i|
  1343. xi, xf = xp[i, 2]
  1344. yi, yf = yp[i, 2]
  1345. zi, zf = zp[i, 2]
  1346. ti, tf = tp[i, 2]
  1347. if tf == ti
  1348. tf += Secure_distance
  1349. end
  1350. [(tf + ti) / 2.0, -((distance(xf, yf, zf) - distance(xi, yi, zi)) / (tf - ti))]
  1351. end.flatten
  1352. end
  1353. def path_acceleration
  1354. v = path_velocity()
  1355. result = []
  1356. 0.step(v.length - 3, 2) do |i|
  1357. ti, vi, tf, vf = v[i, 4]
  1358. if tf == ti
  1359. tf += Secure_distance
  1360. end
  1361. am = (vf - vi) / (tf - ti)
  1362. result << ti << am << tf << am
  1363. end
  1364. result
  1365. end
  1366. # Gnuplot
  1367. def plot_open
  1368. if @gnuplot.kind_of?(Gnuplot)
  1369. @gnuplot
  1370. else
  1371. @gnuplot = Gnuplot.new
  1372. end
  1373. end
  1374. def plot_close
  1375. @gnuplot.close if @gnuplot.kind_of?(Gnuplot)
  1376. end
  1377. def cmd(*args)
  1378. @gnuplot = plot_open
  1379. @gnuplot.command(format(*args) << "\n")
  1380. end
  1381. def plot_trajectory(*args)
  1382. @gnuplot = plot_open
  1383. label, reset = nil
  1384. optkey(args, binding,
  1385. [:label, "trajectory"],
  1386. [:reset, true])
  1387. @gnuplot.reset() if reset
  1388. @gnuplot.set_autoscale()
  1389. if path_z.detect do |z| z.nonzero? end
  1390. @gnuplot.plot_3d_curve(path_trajectory(), :label, label, *args)
  1391. else
  1392. @gnuplot.plot_2d_curve(path_2d_trajectory(), :label, label, *args)
  1393. end
  1394. end
  1395. def plot_velocity(reset = true)
  1396. @gnuplot = plot_open
  1397. @gnuplot.reset() if reset
  1398. @gnuplot.set_autoscale()
  1399. @gnuplot.plot_2d_curve(path_velocity(), :label, "velocity", :style, "steps")
  1400. end
  1401. def plot_doppler(reset = true)
  1402. @gnuplot = plot_open
  1403. @gnuplot.reset() if reset
  1404. @gnuplot.set_autoscale()
  1405. @gnuplot.plot_2d_curve(path_doppler(), :label, "doppler", :style, "steps")
  1406. end
  1407. def plot_acceleration(reset = true)
  1408. @gnuplot = plot_open
  1409. @gnuplot.reset() if reset
  1410. @gnuplot.set_autoscale()
  1411. @gnuplot.plot_2d_curve(path_acceleration(), :label, "acceleration", :style, "steps")
  1412. end
  1413. def pplot(normalize = true)
  1414. @gnuplot = plot_open
  1415. norm = lambda do |env, nrm|
  1416. unless nrm
  1417. env
  1418. else
  1419. mx = env.each_pair do |x, y| y end.max
  1420. if mx.zero?
  1421. env
  1422. else
  1423. env.each_pair do |x, y| [x, y / mx.to_f] end.flatten
  1424. end
  1425. end
  1426. end
  1427. @gnuplot.reset()
  1428. @gnuplot.size(0, 0, 1, 1)
  1429. @gnuplot.start_multiplot()
  1430. @gnuplot.size(0.0, 0.333, 1.0, 0.667)
  1431. plot_trajectory(:reset, false)
  1432. @gnuplot.size(0.0, 0.0, 1.0, 0.333)
  1433. @gnuplot.plot_2d_curves([norm.call(path_velocity(), normalize),
  1434. norm.call(path_acceleration(), normalize),
  1435. norm.call(path_doppler(), normalize)],
  1436. :labels, ["velocity", "acceleration", "doppler"],
  1437. :styles, ["steps", "steps", "steps"])
  1438. @gnuplot.end_multiplot()
  1439. end
  1440. # Sndplot
  1441. def snd_open(chns = 1)
  1442. if @sndplot.kind_of?(Sndplot)
  1443. @sndplot
  1444. else
  1445. @sndplot = Sndplot.new(chns)
  1446. end
  1447. end
  1448. def snd_close
  1449. @sndplot.close if @sndplot.kind_of?(Sndplot)
  1450. end
  1451. def snd_trajectory(chn = 0, label = "trajectory")
  1452. @sndplot = snd_open
  1453. graph(path_2d_trajectory(), label, false, false, false, false, @sndplot.snd, chn)
  1454. @sndplot
  1455. end
  1456. def snd_velocity(chn = 0, label = "velocity")
  1457. @sndplot = snd_open
  1458. graph(path_velocity(), label, false, false, false, false, @sndplot.snd, chn)
  1459. @sndplot
  1460. end
  1461. def snd_doppler(chn = 0, label = "doppler")
  1462. @sndplot = snd_open
  1463. graph(path_doppler(), label, false, false, false, false, @sndplot.snd, chn)
  1464. @sndplot
  1465. end
  1466. def snd_acceleration(chn = 0, label = "acceleration")
  1467. @sndplot = snd_open
  1468. graph(path_acceleration(), label, false, false, false, false, @sndplot.snd, chn)
  1469. @sndplot
  1470. end
  1471. def snd_plot
  1472. @sndplot = snd_open(4)
  1473. snd_trajectory(0)
  1474. snd_velocity(1)
  1475. snd_acceleration(2)
  1476. snd_doppler(3)
  1477. @sndplot
  1478. end
  1479. private
  1480. def transform_path(*args)
  1481. scaling, translation, rotation, rotation_center, rotation_axis = nil
  1482. optkey(args, binding,
  1483. :scaling,
  1484. :translation,
  1485. :rotation,
  1486. :rotation_center,
  1487. [:rotation_axis, [0.0, 0.0, 1.0]])
  1488. render_path() if @rx.nil?
  1489. if scaling or translation or rotation
  1490. rotation = TWO_PI * (rotation / @one_turn) if rotation
  1491. if rotation_axis and (rotation_axis.length != 3)
  1492. dl_error(rotation_axis, "rotation axis has to have all three coordinates")
  1493. end
  1494. matrix = if rotation
  1495. rotation_matrix(rotation_axis[0], rotation_axis[1], rotation_axis[2], rotation)
  1496. end
  1497. xc = path_x()
  1498. yc = path_y()
  1499. zc = path_z()
  1500. if rotation_center and (rotation_center.length != 3)
  1501. dl_error(rotation_center, "rotation center has to have all three coordinates")
  1502. end
  1503. xtr = []
  1504. ytr = []
  1505. ztr = []
  1506. xc.each_with_index do |x, i|
  1507. y = yc[i]
  1508. z = zc[i]
  1509. xw, yw, zw = x, y, z
  1510. if rotation_center and rotation
  1511. xw -= rotation_center[0]
  1512. yw -= rotation_center[1]
  1513. zw -= rotation_center[2]
  1514. end
  1515. if rotation
  1516. mc = [xw, yw, zw]
  1517. xv, yv, zv = matrix.column_vectors
  1518. xr = xv.to_a.map_with_index do |xx, ii| xx * mc[ii] end.to_a.sum
  1519. yr = yv.to_a.map_with_index do |xx, ii| xx * mc[ii] end.to_a.sum
  1520. zr = zv.to_a.map_with_index do |xx, ii| xx * mc[ii] end.to_a.sum
  1521. xw, yw, zw = xr, yr, zr
  1522. end
  1523. if rotation_center and rotation
  1524. xw += rotation_center[0]
  1525. yw += rotation_center[1]
  1526. zw += rotation_center[2]
  1527. end
  1528. if scaling
  1529. xw *= scaling[0]
  1530. yw *= scaling[1] if scaling[1]
  1531. zw *= scaling[2] if scaling[2]
  1532. end
  1533. if translation
  1534. xw += translation[0]
  1535. yw += translation[1] if translation[1]
  1536. zw += translation[2] if translation[2]
  1537. end
  1538. xtr << xw
  1539. ytr << yw
  1540. ztr << zw
  1541. end
  1542. @tx, @ty, @tz = xtr, ytr, ztr
  1543. else
  1544. @tt = @rt.dup
  1545. @tx = @rx.dup
  1546. @ty = @ry.dup
  1547. @tz = @rz.dup
  1548. end
  1549. end
  1550. def reset_transformation
  1551. @tt = @tx = @ty = @tz = nil
  1552. end
  1553. def reset_rendering
  1554. @rt = @rv = @rx = @ry = @rz = nil
  1555. reset_transformation()
  1556. end
  1557. def parse_cartesian_coordinates(points, d3)
  1558. if array?(points[0])
  1559. x = points.map do |p| p[0] end
  1560. y = points.map do |p| p[1] end
  1561. z = points.map do |p| d3 ? (p[2] or 0.0) : 0.0 end
  1562. v = points.map do |p| d3 ? p[3] : p[2] end
  1563. [x, y, z, v]
  1564. else
  1565. if d3
  1566. x = []
  1567. y = []
  1568. z = []
  1569. 0.step(points.length - 3, 3) do |i|
  1570. x += [points[i]]
  1571. y += [points[i + 1]]
  1572. z += [points[i + 2]]
  1573. end
  1574. [x, y, z, x.map do |i| nil end]
  1575. else
  1576. x = []
  1577. y = []
  1578. 0.step(points.length - 2, 2) do |i|
  1579. x += [points[i]]
  1580. y += [points[i + 1]]
  1581. end
  1582. [x, y, x.map do |i| 0.0 end, x.map do |i| nil end]
  1583. end
  1584. end
  1585. end
  1586. def parse_polar_coordinates(points, d3)
  1587. if array?(points[0])
  1588. x = []
  1589. y = []
  1590. z = []
  1591. v = []
  1592. points.each do |p|
  1593. d = p[0]
  1594. a = p[1]
  1595. e = (d3 ? (p[2] or 0.0) : 0.0)
  1596. evec = cis((e / @one_turn) * TWO_PI)
  1597. dxy = d * evec.real
  1598. avec = cis((a / @one_turn) * TWO_PI)
  1599. z << (d * evec.imag)
  1600. x << (dxy * avec.imag)
  1601. y << (dxy * avec.real)
  1602. v << (d3 ? p[3] : p[2])
  1603. end
  1604. [x, y, z, v]
  1605. else
  1606. if d3
  1607. x = []
  1608. y = []
  1609. z = []
  1610. 0.step(points.length - 1, 3) do |i|
  1611. d, a, e = points[i, 3]
  1612. evec = cis((e / @one_turn) * TWO_PI)
  1613. dxy = (d * evec.real)
  1614. avec = cis((a / @one_turn) * TWO_PI)
  1615. z << (d * evec.imag)
  1616. x << (dxy * avec.imag)
  1617. y << (dxy * avec.real)
  1618. end
  1619. [x, y, z, x.map do |i| nil end]
  1620. else
  1621. x = []
  1622. y = []
  1623. 0.step(points.length - 1, 2) do |i|
  1624. d, a = points[i, 2]
  1625. avec = cis((a / @one_turn) * TWO_PI)
  1626. x << (d * avec.imag)
  1627. y << (d * avec.real)
  1628. end
  1629. [x, y, x.map do |i| 0.0 end, x.map do |i| nil end]
  1630. end
  1631. end
  1632. end
  1633. end
  1634. class Bezier_path < Path
  1635. def initialize(path, *args)
  1636. @path = path
  1637. if (not @path) or (array?(@path) and @path.empty?)
  1638. dl_error("can't define a path with no points in it")
  1639. end
  1640. super()
  1641. d3, polar, error, curvature = nil
  1642. optkey(args, binding,
  1643. [:d3, true],
  1644. [:polar, false],
  1645. [:error, 0.01],
  1646. :curvature)
  1647. @d3 = d3
  1648. @polar = polar
  1649. @error = error
  1650. @curvature = curvature
  1651. @x = @y = @z = @v = @bx = @by = @bz = nil
  1652. # for ac() and a()
  1653. @path_ak_even = nil
  1654. @path_ak_odd = nil
  1655. @path_gtab = nil
  1656. @path_ftab = nil
  1657. end
  1658. private
  1659. def parse_path
  1660. if @polar
  1661. @x, @y, @z, @v = parse_polar_coordinates(@path, @d3)
  1662. else
  1663. @x, @y, @z, @v = parse_cartesian_coordinates(@path, @d3)
  1664. end
  1665. if @v[0] and @v.min < 0.1
  1666. if @v.min < 0.0
  1667. Snd.warning("%s#%s: velocities must be all positive, corrected",
  1668. self.class, get_func_name)
  1669. end
  1670. @v.map! do |x| [0.1, x].max end
  1671. end
  1672. @bx = @by = @bz = nil
  1673. reset_rendering()
  1674. end
  1675. def fit_path
  1676. parse_path() if @x.nil?
  1677. end
  1678. def bezier_point(u, c)
  1679. u1 = 1.0 - u
  1680. cr = make_array(3) do |i| make_array(3) do |j| u1 * c[i][j] + u * c[i][j + 1] end end
  1681. 1.downto(0) do |i|
  1682. 0.upto(i) do |j|
  1683. 3.times do |k| cr[k][j] = u1 * cr[k][j] + u * cr[k][j + 1] end
  1684. end
  1685. end
  1686. [cr[0][0], cr[1][0], cr[2][0]]
  1687. end
  1688. def berny(xl, yl, zl, xh, yh, zh, ul, u, uh, c)
  1689. x, y, z = bezier_point(u, c)
  1690. xn, yn, zn = nearest_point(xl, yl, zl, xh, yh, zh, x, y, z)
  1691. if distance(xn - x, yn - y, zn - z) > @error
  1692. xi, yi, zi = berny(xl, yl, zl, x, y, z, ul, (ul + u) / 2.0, u, c)
  1693. xj, yj, zj = berny(x, y, z, xh, yh, zh, u, (u + uh) / 2.0, uh, c)
  1694. [xi + [x] + xj, yi + [y] + yj, zi + [z] + zj]
  1695. else
  1696. [[], [], []]
  1697. end
  1698. end
  1699. def render_path
  1700. fit_path() if @bx.nil?
  1701. rx = []
  1702. ry = []
  1703. rz = []
  1704. rv = []
  1705. if (not @v[0]) or @v[0].zero?
  1706. @v[0] = 1.0
  1707. @v[-1] = 1.0
  1708. end
  1709. if @x.length == 1
  1710. @rx = @x
  1711. @ry = @y
  1712. @rz = @z
  1713. @rt = [0.0]
  1714. return
  1715. end
  1716. xf_bz = yf_bz = zf_bz = vf_bz = 0.0
  1717. (@v.length - 1).times do |i|
  1718. x_bz = @bx[i]
  1719. y_bz = @by[i]
  1720. z_bz = @bz[i]
  1721. vi_bz, vf_bz = @v[i, 2]
  1722. xi_bz = x_bz[0]
  1723. xf_bz = x_bz[-1]
  1724. yi_bz = y_bz[0]
  1725. yf_bz = y_bz[-1]
  1726. zi_bz = z_bz[0]
  1727. zf_bz = z_bz[-1]
  1728. xs, ys, zs = berny(xi_bz, yi_bz, zi_bz, xf_bz, yf_bz, zf_bz, 0, 0.5, 1, [x_bz, y_bz, z_bz])
  1729. rx += [xi_bz] + xs
  1730. ry += [yi_bz] + ys
  1731. rz += [zi_bz] + zs
  1732. rv += [vi_bz] + xs.map do nil end
  1733. end
  1734. rx << xf_bz
  1735. ry << yf_bz
  1736. rz << zf_bz
  1737. rv << vf_bz
  1738. xseg = [rx[0]]
  1739. yseg = [ry[0]]
  1740. zseg = [rz[0]]
  1741. vi = rv[0]
  1742. ti = 0.0
  1743. times = [ti]
  1744. (1...rx.length).each do |i|
  1745. x = rx[i]
  1746. y = ry[i]
  1747. z = rz[i]
  1748. v = rv[i]
  1749. xseg << x
  1750. yseg << y
  1751. zseg << z
  1752. if v
  1753. sofar = 0.0
  1754. dseg = (0...xseg.length - 1).map do |j|
  1755. xsi, xsf = xseg[j, 2]
  1756. ysi, ysf = yseg[j, 2]
  1757. zsi, zsf = zseg[j, 2]
  1758. sofar += distance(xsf - xsi, ysf - ysi, zsf - zsi)
  1759. end
  1760. df = dseg[-1]
  1761. vf = v
  1762. aa = ((vf - vi) * (vf + vi)) / (df * 4.0)
  1763. tseg = dseg.map do |d|
  1764. ti + (if vi and vi.nonzero? and vf == vi
  1765. d / vi
  1766. elsif aa.nonzero?
  1767. ((vi * vi + 4.0 * aa * d) ** 0.5 - vi) / (2.0 * aa)
  1768. else
  1769. 0.0
  1770. end)
  1771. end
  1772. times += tseg
  1773. xseg = [x]
  1774. yseg = [y]
  1775. zseg = [z]
  1776. vi = v
  1777. ti = tseg[-1]
  1778. end
  1779. end
  1780. @rx = rx
  1781. @ry = ry
  1782. @rz = rz
  1783. tf = times[-1]
  1784. @rt = times.map do |tii| tii / tf end
  1785. reset_transformation()
  1786. end
  1787. # called in Closed_bezier_path#calculate_fit
  1788. def a(k, n)
  1789. if ([Path_maxcoeff * 2.0 + 1, n].min).odd?
  1790. make_a_odd() unless @path_ak_odd
  1791. @path_ak_odd[(n - 3) / 2][k - 1]
  1792. else
  1793. make_a_even() unless @path_ak_even
  1794. @path_ak_even[(n - 4) / 2][k - 1]
  1795. end
  1796. end
  1797. # called in Open_bezier_path#calculate_fit
  1798. def ac(k, n)
  1799. n = [n, Path_maxcoeff].min
  1800. make_a_even() unless @path_ak_even
  1801. @path_ak_even[n - 2][k - 1]
  1802. end
  1803. def make_a_even
  1804. g = lambda do |m|
  1805. @path_gtab = make_array(Path_maxcoeff) unless @path_gtab
  1806. @path_gtab[0] = 1.0
  1807. @path_gtab[1] = -4.0
  1808. (2...Path_maxcoeff).each do |i|
  1809. @path_gtab[i] = -4.0 * @path_gtab[i - 1] - @path_gtab[i - 2]
  1810. end
  1811. @path_gtab[m]
  1812. end
  1813. @path_ak_even = make_array(Path_maxcoeff - 1)
  1814. (1...Path_maxcoeff).each do |m|
  1815. @path_ak_even[m - 1] = make_array(m)
  1816. (1..m).each do |k|
  1817. @path_ak_even[m - 1][k - 1] = (-g.call(m - k) / g.call(m)).to_f
  1818. end
  1819. end
  1820. end
  1821. def make_a_odd
  1822. f = lambda do |m|
  1823. @path_ftab = make_array(Path_maxcoeff) unless @path_ftab
  1824. @path_ftab[0] = 1.0
  1825. @path_ftab[1] = -3.0
  1826. (2...Path_maxcoeff).each do |i|
  1827. @path_ftab[i] = -4.0 * @path_ftab[i - 1] - @path_ftab[i - 2]
  1828. end
  1829. @path_ftab[m]
  1830. end
  1831. @path_ak_odd = make_array(Path_maxcoeff - 1)
  1832. (1...Path_maxcoeff).each do |m|
  1833. @path_ak_odd[m - 1] = make_array(m)
  1834. (1..m).each do |k|
  1835. @path_ak_odd[m - 1][k - 1] = (-f.call(m - k) / f.call(m)).to_f
  1836. end
  1837. end
  1838. end
  1839. end
  1840. class Open_bezier_path < Bezier_path
  1841. def initialize(path, *args)
  1842. super
  1843. initial_direction, final_direction = nil
  1844. optkey(args, binding,
  1845. [:initial_direction, [0.0, 0.0, 0.0]],
  1846. [:final_direction, [0.0, 0.0, 0.0]])
  1847. @initial_direction = initial_direction
  1848. @final_direction = final_direction
  1849. end
  1850. private
  1851. def calculate_fit
  1852. n = @x.length - 1
  1853. m = n - 1
  1854. p = [@x, @y, @z]
  1855. d = make_array(3) do make_array(n + 1, 0.0) end
  1856. d = Matrix[d[0], d[1], d[2]].to_a
  1857. ref = lambda do |z, j, i|
  1858. if i > n
  1859. z[j][i - n]
  1860. elsif i < 0
  1861. z[j][i + n]
  1862. elsif i == n
  1863. z[j][n] - d[j][n]
  1864. elsif i == 0
  1865. z[j][0] + d[j][0]
  1866. else
  1867. z[j][i]
  1868. end
  1869. end
  1870. d[0][0] = (@initial_direction[0] or 0.0)
  1871. d[1][0] = (@initial_direction[1] or 0.0)
  1872. d[2][0] = (@initial_direction[2] or 0.0)
  1873. d[0][n] = (@final_direction[0] or 0.0)
  1874. d[1][n] = (@final_direction[1] or 0.0)
  1875. d[2][n] = (@final_direction[2] or 0.0)
  1876. (1...n).each do |i|
  1877. (1..[Path_maxcoeff - 1, m].min).each do |j|
  1878. 3.times do |k|
  1879. d[k][i] = d[k][i] + ac(j, n) * (ref.call(p, k, i + j) - ref.call(p, k, i - j))
  1880. end
  1881. end
  1882. end
  1883. [n, p, d]
  1884. end
  1885. def fit_path
  1886. parse_path() if @x.nil?
  1887. case points = @x.length
  1888. when 1
  1889. @bx = @by = @bz = nil
  1890. when 2
  1891. x1, x2 = @x[0, 2]
  1892. y1, y2 = @y[0, 2]
  1893. z1, z2 = @z[0, 2]
  1894. @bx = [[x1, x1, x2, x2]]
  1895. @by = [[y1, y1, y2, y2]]
  1896. @bz = [[z1, z1, z2, z2]]
  1897. else
  1898. n, p, d = calculate_fit()
  1899. c = @curvature
  1900. cs = make_array(n)
  1901. if c.kind_of?(NilClass) or (array?(c) and c.empty?)
  1902. n.times do |i| cs[i] = [1.0, 1.0] end
  1903. elsif number?(c)
  1904. n.times do |i| cs[i] = [c, c] end
  1905. elsif array?(c) and c.length == n
  1906. c.each_with_index do |ci, i|
  1907. cs[i] = if array?(ci)
  1908. if ci.length != 2
  1909. dl_error(ci, "curvature sublist must have two elements")
  1910. else
  1911. ci
  1912. end
  1913. else
  1914. [ci, ci]
  1915. end
  1916. end
  1917. else
  1918. dl_error(c, "bad curvature argument to path, need #{n} elements")
  1919. end
  1920. @bx = (0...n).map do |i|
  1921. [p[0][i], p[0][i] + d[0][i] * cs[i][0], p[0][i + 1] - d[0][i + 1] * cs[i][1], p[0][i + 1]]
  1922. end
  1923. @by = (0...n).map do |i|
  1924. [p[1][i], p[1][i] + d[1][i] * cs[i][0], p[1][i + 1] - d[1][i + 1] * cs[i][1], p[1][i + 1]]
  1925. end
  1926. @bz = (0...n).map do |i|
  1927. [p[2][i], p[2][i] + d[2][i] * cs[i][0], p[2][i + 1] - d[2][i + 1] * cs[i][1], p[2][i + 1]]
  1928. end
  1929. end
  1930. reset_rendering()
  1931. end
  1932. end
  1933. class Closed_bezier_path < Bezier_path
  1934. def initialize(path, *args)
  1935. super
  1936. end
  1937. private
  1938. def calculate_fit
  1939. n = @x.length - 1
  1940. m = (n - (n.odd? ? 3 : 4)) / 2
  1941. p = [@x, @y, @z]
  1942. d = make_array(3) do make_array(n, 0.0) end
  1943. ref = lambda do |z, j, i|
  1944. if i > (n - 1)
  1945. z[j][i - n]
  1946. elsif i < 0
  1947. z[j][i + n]
  1948. else
  1949. z[j][i]
  1950. end
  1951. end
  1952. n.times do |i|
  1953. (1..m).each do |j|
  1954. 3.times do |k|
  1955. d[k][i] = d[k][i] + a(j, n) * (ref.call(p, k, i + j) - ref.call(p, k, i - j))
  1956. end
  1957. end
  1958. end
  1959. if @curvature
  1960. n.times do |i|
  1961. curve = @curvature[i]
  1962. d[0][i] *= curve
  1963. d[1][i] *= curve
  1964. d[2][i] *= curve
  1965. end
  1966. end
  1967. [n - 1, p, d]
  1968. end
  1969. def fit_path
  1970. parse_path() if @x.nil?
  1971. if @x.length > 4
  1972. n, p, d = calculate_fit()
  1973. xc = (0...n).map do |i|
  1974. [p[0][i], p[0][i] + d[0][i], p[0][i + 1] - d[0][i + 1], p[0][i + 1]]
  1975. end
  1976. yc = (0...n).map do |i|
  1977. [p[1][i], p[1][i] + d[1][i], p[1][i + 1] - d[1][i + 1], p[1][i + 1]]
  1978. end
  1979. zc = (0...n).map do |i|
  1980. [p[2][i], p[2][i] + d[2][i], p[2][i + 1] - d[2][i + 1], p[2][i + 1]]
  1981. end
  1982. @bx = xc + [[p[0][n], p[0][n] + d[0][n], p[0][0] - d[0][0], p[0][0]]]
  1983. @by = yc + [[p[1][n], p[1][n] + d[1][n], p[1][0] - d[1][0], p[1][0]]]
  1984. @bz = zc + [[p[2][n], p[2][n] + d[2][n], p[2][0] - d[2][0], p[2][0]]]
  1985. else
  1986. xc = []
  1987. yc = []
  1988. zc = []
  1989. (@x.length - 1).times do |i|
  1990. x1, x2 = @x[i, 2]
  1991. y1, y2 = @y[i, 2]
  1992. z1, z2 = @z[i, 2]
  1993. xc << [x1, x1, x2, x2]
  1994. yc << [y1, y1, y2, y2]
  1995. zc << [z1, z1, z2, z2]
  1996. end
  1997. @bx = xc
  1998. @by = yc
  1999. @bz = zc
  2000. end
  2001. reset_rendering()
  2002. end
  2003. end
  2004. class Literal_path < Path
  2005. def initialize(path, *args)
  2006. @path = path
  2007. if (not @path) or (array?(@path) and @path.empty?)
  2008. dl_error("can't define a path with no points in it")
  2009. end
  2010. super()
  2011. d3, polar = nil
  2012. optkey(args, binding,
  2013. [:d3, true],
  2014. [:polar, false])
  2015. @d3 = d3
  2016. @polar = polar
  2017. end
  2018. private
  2019. def render_path
  2020. if @polar
  2021. @rx, @ry, @rz, @rv = parse_polar_coordinates(@path, @d3)
  2022. else
  2023. @rx, @ry, @rz, @rv = parse_cartesian_coordinates(@path, @d3)
  2024. end
  2025. if (not @rv[0]) or @rv[0].zero?
  2026. @rv[0] = 1.0
  2027. @rv[-1] = 1.0
  2028. end
  2029. if @rx.length == 1
  2030. @rt = [0.0]
  2031. return
  2032. end
  2033. rx = @rx
  2034. ry = @ry
  2035. rz = @rz
  2036. rv = @rv
  2037. xseg = [rx[0]]
  2038. yseg = [ry[0]]
  2039. zseg = [rz[0]]
  2040. vi = rv[0]
  2041. ti = 0.0
  2042. times = [ti]
  2043. (1...rx.length).each do |i|
  2044. x = rx[i]
  2045. y = ry[i]
  2046. z = rz[i]
  2047. v = rv[i]
  2048. xseg << x
  2049. yseg << y
  2050. zseg << z
  2051. if v
  2052. sofar = 0.0
  2053. dseg = (0...xseg.length - 1).map do |j|
  2054. xsi, xsf = xseg[j, 2]
  2055. ysi, ysf = yseg[j, 2]
  2056. zsi, zsf = zseg[j, 2]
  2057. sofar += distance(xsf - xsi, ysf - ysi, zsf - zsi)
  2058. end
  2059. df = dseg[-1]
  2060. vf = v
  2061. aa = ((vf - vi) * (vf + vi)) / (df * 4.0)
  2062. tseg = dseg.map do |d|
  2063. ti + (if vi and vi.nonzero? and vf == vi
  2064. d / vi
  2065. elsif aa.nonzero?
  2066. ((vi * vi + 4.0 * aa * d) ** 0.5 - vi) / (2.0 * aa)
  2067. else
  2068. 0.0
  2069. end)
  2070. end
  2071. times += tseg
  2072. xseg = [x]
  2073. yseg = [y]
  2074. zseg = [z]
  2075. vi = v
  2076. ti = tseg[-1]
  2077. end
  2078. end
  2079. tf = times[-1]
  2080. @rt = times.map do |tii| tii / tf end
  2081. reset_transformation()
  2082. end
  2083. end
  2084. class Spiral_path < Literal_path
  2085. def initialize(*args)
  2086. # to fool Literal_path.new
  2087. super([nil])
  2088. start_angle, turns = nil
  2089. optkey(args, binding,
  2090. [:start_angle, 0],
  2091. [:turns, 2])
  2092. @start_angle = start_angle
  2093. @turns = turns
  2094. end
  2095. private
  2096. def render_path
  2097. start = (@start_angle / @one_turn.to_f) * TWO_PI
  2098. total = (@turns.zero? ? TWO_PI : (@turns * TWO_PI))
  2099. step_angle = @one_turn / 100.0
  2100. steps = (total / ((step_angle / @one_turn.to_f) * TWO_PI)).abs
  2101. step = total / (steps.ceil * (step_angle < 0 ? -1 : 1))
  2102. x = []
  2103. y = []
  2104. z = []
  2105. (total / step).round.abs.times do
  2106. xy = cis(start)
  2107. x << (10.0 * xy.imag)
  2108. y << (10.0 * xy.real)
  2109. z << 0.0
  2110. start += step
  2111. end
  2112. sofar = 0.0
  2113. dp = (0...x.length - 1).map do |i|
  2114. xi, xf = x[i, 2]
  2115. yi, yf = y[i, 2]
  2116. zi, zf = z[i, 2]
  2117. sofar += distance(xf - xi, yf - yi, zf - zi)
  2118. end
  2119. td = 0.0
  2120. times = (0...dp.length - 1).map do |i|
  2121. di, df = dp[i, 2]
  2122. td = td + (df - di) / 4.0
  2123. end
  2124. @rx = x
  2125. @ry = y
  2126. @rz = z
  2127. tf = times[-1]
  2128. @rt = times.map do |ti| ti / tf end
  2129. reset_transformation()
  2130. end
  2131. end
  2132. module_function
  2133. def make_dlocsig(start, dur, *args)
  2134. dl = Dlocsig.new
  2135. dl.make_dlocsig(start, dur, *args)
  2136. dl
  2137. end
  2138. def dlocsig(dl, dloc, input)
  2139. dl.dlocsig(dloc, input)
  2140. end
  2141. def make_path(path, *args)
  2142. Open_bezier_path.new(path, *args)
  2143. end
  2144. def make_polar_path(path, *args)
  2145. Open_bezier_path.new(path, :polar, true, *args)
  2146. end
  2147. def make_closed_path(path, *args)
  2148. d3 = nil
  2149. optkey(args, binding, [:d3, true])
  2150. len = d3 ? 3 : 2
  2151. unless path[0][0, len] == path[-1][0, len]
  2152. path += [path[0]]
  2153. end
  2154. Closed_bezier_path.new(path, *args)
  2155. end
  2156. def make_literal_path(path, *args)
  2157. Literal_path.new(path, *args)
  2158. end
  2159. def make_literal_polar_path(path, *args)
  2160. Literal_path.new(path, :polar, true, *args)
  2161. end
  2162. def make_spiral_path(*args)
  2163. Spiral_path.new(*args)
  2164. end
  2165. end
  2166. # example functions (see clm-2/dlocsig/move-sound.ins and
  2167. # clm-2/dlocsig/dlocsig.html)
  2168. class Instrument
  2169. def sinewave(start, dur, freq, amp, path, amp_env = [0, 1, 1, 1], *dlocsig_args)
  2170. os = make_oscil(:frequency, freq)
  2171. en = make_env(:envelope, amp_env, :scaler, amp, :duration, dur)
  2172. run_dlocsig(start, dur, :path, path, *dlocsig_args) do
  2173. env(en) * oscil(os)
  2174. end
  2175. end
  2176. def move(start, file, path, *dlocsig_args)
  2177. dl_error(path, "need a path") unless path.kind_of?(DL::Path)
  2178. dur = ws_duration(file)
  2179. chns = ws_channels(file)
  2180. rds = make_array(chns) do |chn| make_ws_reader(file, :start, start, :channel, chn) end
  2181. run_dlocsig(start, dur, :path, path, *dlocsig_args) do
  2182. rds.map do |rd| ws_readin(rd) end.sum / chns
  2183. end
  2184. end
  2185. add_help(:move_sound,
  2186. "move_sound(path, *args) do ... end
  2187. sound_let-args:
  2188. :channels, 1 # channels to move
  2189. start time in output file:
  2190. :startime, 0
  2191. rest args: make_dlocsig")
  2192. def move_sound(path, *args, &body)
  2193. chns = get_shift_args(args, :channels, 1)
  2194. start = get_shift_args(args, :startime, 0)
  2195. sound_let([:channels, chns, body]) do |to_move|
  2196. if @verbose
  2197. Snd.display("%s: moving sound on %d channel%s", get_func_name, chns, (chns > 1 ? "s" : ""))
  2198. end
  2199. move(0, to_move, path, *args)
  2200. rbm_mix(to_move, :output_frame, seconds2samples(start))
  2201. end
  2202. end
  2203. end
  2204. class With_sound
  2205. def run_dlocsig(start, dur, *dlocsig_args, &body)
  2206. with_sound_info(get_func_name(2), start, dur)
  2207. dl = DL.make_dlocsig(start, dur,
  2208. :clm, @clm,
  2209. :rbm_output, @ws_output,
  2210. :rbm_reverb, @ws_reverb,
  2211. :out_channels, @channels,
  2212. :rev_channels, @reverb_channels,
  2213. *dlocsig_args)
  2214. dl.ws_dlocsig(&body)
  2215. end
  2216. end
  2217. # Dlocsig menu
  2218. #
  2219. # require 'snd-xm'
  2220. #
  2221. # make_snd_menu("Dlocsig") do
  2222. # cascade("Dlocsig (Snd)") do
  2223. # entry(Dlocsig_bezier, "Bezier path (Snd)", true)
  2224. # entry(Dlocsig_spiral, "Spiral path (Snd)", true)
  2225. # end
  2226. # cascade("Dlocsig (CLM)") do
  2227. # entry(Dlocsig_bezier, "Bezier path (CLM)", false)
  2228. # entry(Dlocsig_spiral, "Spiral path (CLM)", false)
  2229. # end
  2230. # end
  2231. if provided? :snd_motif or provided? :snd_gtk
  2232. class Dlocsig_menu
  2233. require "snd-xm"
  2234. include Snd_XM
  2235. require "xm-enved"
  2236. include DL
  2237. def initialize(label, snd_p)
  2238. @label = label
  2239. @snd_p = snd_p
  2240. @dialog = nil
  2241. @out_chans = 4
  2242. @rev_chans = 1
  2243. @path = nil
  2244. @sliders = []
  2245. @init_out_chans = 4
  2246. @output_power = @init_power = 1.5
  2247. @reverb_power = @init_rev_power = 0.5
  2248. @render_using = Amplitude_panning
  2249. end
  2250. private
  2251. def with_sound_target(*comment_args)
  2252. if @render_using == B_format_ambisonics
  2253. set_scale_value(@sliders[0].scale, @out_chans = 4)
  2254. end
  2255. comment_string = if string?($clm_comment) and !$clm_comment.empty?
  2256. format("%s; %s", $clm_comment, format(*comment_args))
  2257. else
  2258. format(*comment_args)
  2259. end
  2260. snd_path, snd_name = File.split(file_name(selected_sound))
  2261. snd_name = snd_name.split("moved-").last
  2262. snd_to_move = format("%s/%s", snd_path, snd_name)
  2263. snd_moved = format("%s/moved-%s", snd_path, snd_name)
  2264. path = @path
  2265. output_power = @output_power
  2266. reverb_power = @reverb_power
  2267. render_using = @render_using
  2268. f = with_sound(:clm, (not @snd_p),
  2269. :output, snd_moved,
  2270. :channels, @out_chans,
  2271. :reverb_channels, @rev_chans,
  2272. :comment, comment_string,
  2273. :info, (not $clm_notehook),
  2274. :statistics, true,
  2275. :play, true) do
  2276. move(0,
  2277. snd_to_move,
  2278. path,
  2279. :output_power, output_power,
  2280. :reverb_power, reverb_power,
  2281. :render_using, render_using)
  2282. end
  2283. Snd.display(f.output.inspect)
  2284. rescue
  2285. Snd.warning("%s#%s: %s", self.class, get_func_name, comment_string)
  2286. end
  2287. def add_with_sound_sliders(parent = @dialog.parent)
  2288. @sliders << @dialog.add_slider("output channels",
  2289. 2, @init_out_chans, 8, 1, :linear, parent) do |w, c, i|
  2290. @out_chans = get_scale_value(w, i).round
  2291. end
  2292. @sliders << @dialog.add_slider("output power",
  2293. 0, @init_power, 10, 100, :linear, parent) do |w, c, i|
  2294. @output_power = get_scale_value(w, i, 100.0)
  2295. end
  2296. @sliders << @dialog.add_slider("reverb power",
  2297. 0, @init_rev_power, 10, 100, :linear, parent) do |w, c, i|
  2298. @reverb_power = get_scale_value(w, i, 100.0)
  2299. end
  2300. end
  2301. def reset_with_sound_sliders(reverb_p = true)
  2302. set_scale_value(@sliders[0].scale, @out_chans = @init_out_chans)
  2303. @output_power = @init_power
  2304. set_scale_value(@sliders[1].scale, @output_power, 100.0)
  2305. @reverb_power = @init_rev_power
  2306. set_scale_value(@sliders[2].scale, @reverb_power, 100.0)
  2307. end
  2308. def add_with_sound_targets
  2309. @dialog.add_target([["amplitude panning", :amplitude, true],
  2310. ["b format ambisonics", :b_format, false],
  2311. ["decoded ambisonics", :decoded, false]]) do |val|
  2312. @render_using = case val
  2313. when :amplitude
  2314. Amplitude_panning
  2315. when :b_format
  2316. set_scale_value(@sliders[0].scale, @out_chans = 4)
  2317. B_format_ambisonics
  2318. when :decoded
  2319. Decoded_ambisonics
  2320. end
  2321. end
  2322. @dialog.add_target([["no reverb", :no_reverb, false],
  2323. ["1 rev chan", :one_rev_chan, true],
  2324. ["4 rev chans", :four_rev_chans, false]]) do |val|
  2325. case val
  2326. when :no_reverb
  2327. @rev_chans = 0
  2328. when :one_rev_chan
  2329. @rev_chans = 1
  2330. when :four_rev_chans
  2331. @rev_chans = 4
  2332. end
  2333. end
  2334. end
  2335. def set_xm_enveds_hooks(*enveds)
  2336. enveds.each do |e|
  2337. e.before_enved_hook.reset_hook! # to prevent running $enved_hook
  2338. e.before_enved_hook.add_hook!("dlocsig-hook") do |pos, x, y, reason|
  2339. if reason == Enved_move_point
  2340. if e.in_range?(x)
  2341. old_x = e.point(pos).first
  2342. e.stretch!(old_x, x)
  2343. e.point(pos, :y, y)
  2344. else
  2345. false
  2346. end
  2347. else
  2348. false
  2349. end
  2350. end
  2351. e.after_enved_hook.add_hook!("dlocsig-hook") do |pos, reason| show_values end
  2352. end
  2353. end
  2354. # comment string
  2355. def dlocsig_strings
  2356. dlstr = ["", :amplitude_panning, :b_format_ambisonics, :decoded_ambisonics]
  2357. format("%s, output_power: %1.2f, reverb_power: %1.2f",
  2358. dlstr[@render_using],
  2359. @output_power,
  2360. @reverb_power)
  2361. end
  2362. def help_cb
  2363. help_dialog(@label,
  2364. "\
  2365. The current sound will be moved through the chosen path. You can set \
  2366. the reverberator via the global with-sound-variable $clm_reverb \
  2367. (#{$clm_reverb.inspect}). If you want four reverb channels, you \
  2368. may try freeverb from freeverb.rb.
  2369. reverb reverb-channels output-channels source
  2370. jc_reverb 1 4 examp.rb
  2371. jl_reverb 1 2 clm-ins.rb
  2372. nrev 1 4 clm-ins.rb
  2373. freeverb 4 > 4 freeverb.rb
  2374. Amplitude-panning: generates amplitude panning between adjacent speakers.
  2375. B-format-ambisonics: generates a four channel first order b-format encoded soundfile.
  2376. Decoded-ambisonics: the ambisonics encoded information is decoded to the number of selected speakers.
  2377. Note: reverb on spiral path generates noise if turns is less than 2.6
  2378. For detailed information see clm-2/dlocsig.html.",
  2379. ["{Libxm}: graphics module",
  2380. "{Ruby}: extension language",
  2381. "{Motif}: Motif extensions via Libxm",
  2382. "{dlocsig}: Fernando Lopez Lezcano's multichannel locator"])
  2383. end
  2384. end
  2385. class Dlocsig_bezier < Dlocsig_menu
  2386. require "xm-enved"
  2387. def initialize(label, snd_p = false)
  2388. super
  2389. @target = :with_sound
  2390. @which_path = :open_bezier_path
  2391. @snd_path = [[-10.0, 10.0, 0.0, 1.0], [0.0, 5.0, 1.0, 1.0], [10.0, 10.0, 0.0, 1.0]]
  2392. @trajectory = nil
  2393. @z_value = nil
  2394. @velocity = nil
  2395. @label_list = []
  2396. end
  2397. def inspect
  2398. str = @snd_path.inspect
  2399. new_str = ""
  2400. [str.length, 20].min.times do |i| new_str << str[i] end
  2401. new_str << "..." if str.length > 20
  2402. format("%s (%s)", @label, new_str)
  2403. end
  2404. def post_dialog
  2405. unless @dialog.kind_of?(Dialog) and widget?(@dialog.dialog)
  2406. init_traj = [0, 1, 0.5, 0.5, 1, 1]
  2407. init_z_traj = [0, 0, 0.5, 0.1, 1, 0]
  2408. init_vel = [0, 0.5, 1, 0.5]
  2409. @dialog = make_dialog(@label,
  2410. :help_cb, lambda do |w, c, i|
  2411. help_cb()
  2412. end, :clear_cb, lambda do |w, c, i|
  2413. create_path
  2414. @path.pplot
  2415. end, :reset_cb, lambda do |w, c, i|
  2416. reset_with_sound_sliders
  2417. @trajectory.envelope = init_traj
  2418. @z_value.envelope = init_z_traj
  2419. @velocity.envelope = init_vel
  2420. show_values
  2421. end) do |w, c, i|
  2422. create_path
  2423. with_sound_target("%s: %s, path: %s", @which_path, dlocsig_strings, @snd_path.inspect)
  2424. end
  2425. if provided? :xm
  2426. frame_args = [RXmNshadowThickness, 4,
  2427. RXmNshadowType, RXmSHADOW_ETCHED_OUT,
  2428. RXmNbackground, basic_color,
  2429. RXmNheight, 170,
  2430. RXmNwidth, 400]
  2431. pane = RXtCreateManagedWidget("pane", RxmPanedWindowWidgetClass, @dialog.parent,
  2432. [RXmNsashHeight, 1, RXmNsashWidth, 1,
  2433. RXmNorientation, RXmHORIZONTAL,
  2434. RXmNbackground, basic_color])
  2435. xepane = RXtCreateManagedWidget("xepane", RxmPanedWindowWidgetClass, pane,
  2436. [RXmNsashHeight, 1, RXmNsashWidth, 1,
  2437. RXmNorientation, RXmVERTICAL,
  2438. RXmNbackground, basic_color])
  2439. trfr = RXtCreateManagedWidget("trfr", RxmFrameWidgetClass, xepane, frame_args)
  2440. zfr = RXtCreateManagedWidget("zfr", RxmFrameWidgetClass, xepane, frame_args)
  2441. vefr = RXtCreateManagedWidget("vefr", RxmFrameWidgetClass, xepane, frame_args)
  2442. vepane = RXtCreateManagedWidget("vpane", RxmPanedWindowWidgetClass, pane,
  2443. [RXmNsashHeight, 1, RXmNsashWidth, 1,
  2444. RXmNseparatorOn, true,
  2445. RXmNorientation, RXmVERTICAL,
  2446. RXmNbackground, basic_color])
  2447. add_with_sound_sliders(vepane)
  2448. rc = RXtCreateManagedWidget("form", RxmRowColumnWidgetClass, vepane,
  2449. [RXmNorientation, RXmVERTICAL,
  2450. RXmNalignment, RXmALIGNMENT_CENTER])
  2451. @label_list = make_array(8) do |i|
  2452. RXtCreateManagedWidget("W" * 30, RxmLabelWidgetClass, rc, [])
  2453. end
  2454. add_with_sound_targets
  2455. @dialog.add_target([["open bezier", :open_bezier_path, true],
  2456. ["closed bezier", :closed_bezier_path, false],
  2457. ["literal", :literal_path, false]]) do |val|
  2458. @which_path = val
  2459. create_path
  2460. end
  2461. activate_dialog(@dialog.dialog)
  2462. @trajectory = make_xenved("x, y", trfr,
  2463. :envelope, init_traj,
  2464. :axis_bounds, [0.0, 1.0, 0.0, 1.0],
  2465. :axis_label, [-10.0, 10.0, 0.0, 10.0])
  2466. @z_value = make_xenved("z", zfr,
  2467. :envelope, init_z_traj,
  2468. :axis_bounds, [0.0, 1.0, 0.0, 1.0],
  2469. :axis_label, [-10.0, 10.0, 0.0, 10.0])
  2470. @velocity = make_xenved("velocity v", vefr,
  2471. :envelope, init_vel,
  2472. :axis_bounds, [0.0, 1.0, 0.05, 1.0],
  2473. :axis_label, [-10.0, 10.0, 0.0, 2.0])
  2474. else
  2475. pane = Rgtk_hbox_new(false, 0)
  2476. Rgtk_box_pack_start(RGTK_BOX(@dialog.parent), pane, false, false, 4)
  2477. Rgtk_widget_show(pane)
  2478. xepane = Rgtk_vbox_new(true, 0)
  2479. Rgtk_box_pack_start(RGTK_BOX(pane), xepane, true, true, 4)
  2480. Rgtk_widget_show(xepane)
  2481. activate_dialog(@dialog.dialog)
  2482. @trajectory = make_xenved("x, y", xepane,
  2483. :envelope, init_traj,
  2484. :axis_bounds, [0.0, 1.0, 0.0, 1.0],
  2485. :axis_label, [-10.0, 10.0, 0.0, 10.0])
  2486. @z_value = make_xenved("z", xepane,
  2487. :envelope, init_z_traj,
  2488. :axis_bounds, [0.0, 1.0, 0.0, 1.0],
  2489. :axis_label, [-10.0, 10.0, 0.0, 10.0])
  2490. @velocity = make_xenved("velocity v", xepane,
  2491. :envelope, init_vel,
  2492. :axis_bounds, [0.0, 1.0, 0.05, 1.0],
  2493. :axis_label, [-10.0, 10.0, 0.0, 2.0])
  2494. vepane = Rgtk_vbox_new(false, 0)
  2495. Rgtk_box_pack_start(RGTK_BOX(pane), vepane, false, false, 4)
  2496. Rgtk_widget_show(vepane)
  2497. add_with_sound_sliders(vepane)
  2498. @label_list = make_array(8) do |i|
  2499. lab = Rgtk_label_new("W" * 30)
  2500. Rgtk_box_pack_start(RGTK_BOX(vepane), lab, false, false, 4)
  2501. Rgtk_widget_show(lab)
  2502. lab
  2503. end
  2504. add_with_sound_targets
  2505. @dialog.add_target([["open bezier", :open_bezier_path, true],
  2506. ["closed bezier", :closed_bezier_path, false],
  2507. ["literal", :literal_path, false]]) do |val|
  2508. @which_path = val
  2509. create_path
  2510. end
  2511. end
  2512. set_xm_enveds_hooks(@trajectory, @z_value, @velocity)
  2513. @dialog.clear_string("Gnuplot")
  2514. @dialog.doit_string((@snd_p ? "With_Snd" : "With_Sound"))
  2515. show_values
  2516. else
  2517. activate_dialog(@dialog.dialog)
  2518. end
  2519. end
  2520. private
  2521. def create_path
  2522. test_path
  2523. @path = case @which_path
  2524. when :open_bezier_path
  2525. make_path(@snd_path)
  2526. when :closed_bezier_path
  2527. make_closed_path(@snd_path)
  2528. when :literal_path
  2529. make_literal_path(@snd_path)
  2530. end
  2531. end
  2532. def test_path
  2533. if @snd_path.length == 2
  2534. Snd.display("%s#%s: path has only two points, one added", self.class, get_func_name)
  2535. @snd_path.insert(1, [0.0, @snd_path[0][1] - 0.1, 0.0, @snd_path[0][3] + 0.1])
  2536. end
  2537. unless @snd_path.detect do |pnt| pnt[1].nonzero? end
  2538. Snd.display("%s#%s: y-values are all zero, changed to mid-point 0.1",
  2539. self.class, get_func_name)
  2540. @snd_path[@snd_path.length / 2][1] = 0.1
  2541. end
  2542. end
  2543. def points_to_path
  2544. @snd_path = []
  2545. @trajectory.each do |x, y|
  2546. z = @z_value.interp(x)
  2547. vel = @velocity.interp(x)
  2548. @snd_path.push([x * 20.0 - 10.0, y * 10.0, z * 10.0, vel * 2.0])
  2549. end
  2550. end
  2551. def show_values
  2552. points_to_path
  2553. @label_list.each_with_index do |w, i|
  2554. if i.zero?
  2555. change_label(w, format("%6s %6s %6s %6s", "x", "y", "z", "v"))
  2556. else
  2557. x, y, z, v = @snd_path[i - 1]
  2558. if x
  2559. change_label(w, format("%s %s %s %s",
  2560. to_f_str(x), to_f_str(y), to_f_str(z), to_f_str(v)))
  2561. else
  2562. change_label(w, "")
  2563. end
  2564. end
  2565. end
  2566. end
  2567. def to_f_str(val)
  2568. "%6s" % ("% 3.1f" % val)
  2569. end
  2570. end
  2571. class Dlocsig_spiral < Dlocsig_menu
  2572. def initialize(label, snd_p = false)
  2573. super
  2574. @start = 0
  2575. @turns = 3.0
  2576. end
  2577. def inspect
  2578. format("%s (%d %1.1f)", @label, @start, @turns)
  2579. end
  2580. def post_dialog
  2581. unless @dialog.kind_of?(Dialog) and widget?(@dialog.dialog)
  2582. sliders = []
  2583. init_start = 0
  2584. init_turns = 3.0
  2585. @dialog = make_dialog(@label,
  2586. :help_cb, lambda do |w, c, i|
  2587. help_cb
  2588. end, :clear_cb, lambda do |w, c, i|
  2589. make_spiral_path(:start_angle, @start, :turns, @turns).pplot
  2590. end, :reset_cb, lambda do |w, c, i|
  2591. reset_with_sound_sliders
  2592. set_scale_value(sliders[0].scale, @start = init_start)
  2593. @turns = init_turns
  2594. set_scale_value(sliders[1].scale, init_turns, 10.0)
  2595. end) do |w, c, i|
  2596. @path = make_spiral_path(:start_angle, @start, :turns, @turns)
  2597. with_sound_target("spiral_path: %s, start: %d, turns: %1.1f",
  2598. dlocsig_strings, @start, @turns)
  2599. end
  2600. add_with_sound_sliders(if provided? :xg
  2601. @dialog.dialog
  2602. else
  2603. @dialog.parent
  2604. end)
  2605. sliders << @dialog.add_slider("start angle", 0, init_start, 360) do |w, c, i|
  2606. @start = get_scale_value(w, i)
  2607. end
  2608. # turns below 2.6 together with reverb create noise
  2609. sliders << @dialog.add_slider("turns", 2.6, init_turns, 10.0, 10) do |w, c, i|
  2610. @turns = get_scale_value(w, i, 10.0)
  2611. end
  2612. add_with_sound_targets
  2613. @dialog.clear_string("Gnuplot")
  2614. @dialog.doit_string((@snd_p ? "With_Snd" : "With_Sound"))
  2615. end
  2616. activate_dialog(@dialog.dialog)
  2617. end
  2618. end
  2619. unless defined? $__private_dlocsig_menu__ and $__private_dlocsig_menu__
  2620. snd_main = make_snd_menu("Dlocsig") do
  2621. cascade("Dlocsig (Snd)") do
  2622. entry(Dlocsig_bezier, "Bezier path (Snd)", true)
  2623. entry(Dlocsig_spiral, "Spiral path (Snd)", true)
  2624. end
  2625. cascade("Dlocsig (CLM)") do
  2626. entry(Dlocsig_bezier, "Bezier path (CLM)", false)
  2627. entry(Dlocsig_spiral, "Spiral path (CLM)", false)
  2628. end
  2629. end
  2630. if provided? :xm
  2631. set_label_sensitive(menu_widgets[Top_menu_bar], "Dlocsig", ((sounds() or []).length > 1))
  2632. else
  2633. set_sensitive(snd_main.menu, ((sounds() or []).length > 1))
  2634. end
  2635. unless $open_hook.member?("dlocsig-menu-hook")
  2636. $open_hook.add_hook!("dlocsig-menu-hook") do |snd|
  2637. if provided? :xm
  2638. set_label_sensitive(menu_widgets[Top_menu_bar], "Dlocsig", true)
  2639. else
  2640. set_sensitive(snd_main.menu, true)
  2641. end
  2642. false
  2643. end
  2644. $close_hook.add_hook!("dlocsig-menu-hook") do |snd|
  2645. if provided? :xm
  2646. set_label_sensitive(menu_widgets[Top_menu_bar], "Dlocsig", ((sounds() or []).length > 1))
  2647. else
  2648. set_sensitive(snd_main.menu, ((sounds() or []).length > 1))
  2649. end
  2650. false
  2651. end
  2652. end
  2653. end
  2654. end
  2655. # JC_REVERB (examp.rb) default options
  2656. #
  2657. # :low_pass, false
  2658. # :volume, 1.0
  2659. # :amp_env, false
  2660. # :delay1, 0.013
  2661. # :delay2, 0.011
  2662. # :delay3, 0.015
  2663. # :delay4, 0.017
  2664. # :double, false
  2665. #
  2666. # $clm_reverb = :jc_reverb_rb
  2667. # $clm_reverb_data = [:low_pass, false, :volume, 1.0, :amp_env, false,
  2668. # :delay1, 0.013, :delay2, 0.011, :delay3, 0.015, :delay4, 0.017]
  2669. # require "clm-ins"
  2670. #
  2671. # JL_REVERB has no options
  2672. #
  2673. # $clm_reverb = :jl_reverb
  2674. # $clm_reverb_data = []
  2675. # NREV default options
  2676. #
  2677. # :reverb_factor, 1.09
  2678. # :lp_coeff, 0.7
  2679. # :volume, 1.0
  2680. #
  2681. # $clm_reverb = :nrev_rb
  2682. # $clm_reverb_data = [:volume, 1.0, :lp_coeff, 0.7]
  2683. # INTERN or N_REV default options (only with_snd)
  2684. #
  2685. # :amount, 0.1
  2686. # :filter, 0.5
  2687. # :feedback, 1.09
  2688. #
  2689. # $clm_reverb = :intern
  2690. # $clm_reverb_data = [:amount, 0.1, :filter, 0.5, :feedback, 1.09]
  2691. # require "freeverb"
  2692. #
  2693. # FREEVERB default options
  2694. #
  2695. # :room_decay, 0.5,
  2696. # :damping, 0.5,
  2697. # :global, 0.3,
  2698. # :predelay, 0.03,
  2699. # :output_gain, 1.0,
  2700. # :output_mixer, nil,
  2701. # :scale_room_decay, 0.28,
  2702. # :offset_room_decay, 0.7,
  2703. # :combtuning, [1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617],
  2704. # :allpasstuning, [556, 441, 341, 225],
  2705. # :scale_damping, 0.4,
  2706. # :stereo_spread, 23,
  2707. #
  2708. # $clm_reverb = :freeverb
  2709. # $clm_reverb_data = [:room_decay, 0.5, :damping, 0.5, :global, 0.3, :predelay, 0.03,
  2710. # :output_gain, 1.0, :output_mixer, nil, :scale_room_decay, 0.7,
  2711. # :scale_damping, 0.4, :stereo_spread, 23]
  2712. =begin
  2713. # (snd-ruby-mode)
  2714. # Examples:
  2715. (with_sound(:channels, 4, :output, "rdloc04.snd") do
  2716. sinewave(0, 1, 440, 0.5, [[-10, 10], [0, 5], [10, 10]].to_path)
  2717. end)
  2718. (with_sound(:channels, 4, :output, "rdlocspiral.snd") do
  2719. move(0, "/usr/gnu/sound/SFiles/bell.snd", DL.make_spiral_path(:start_angle, 180, :turns, 3.5))
  2720. end)
  2721. ([[-10, 10, 0, 0], [0, 5, 0, 1], [10, 10, 0, 0]].to_path(:error, 0.01).plot_velocity)
  2722. (with_sound(:channels, 4, :output, "rdlocmove.snd") do
  2723. move_sound(DL.make_path([[-10, 10], [0.1, 0.1], [10, -10]])) do
  2724. fm_violin_rb(0, 1, 440, 0.1)
  2725. fm_violin_rb(0.3, 2, 1020, 0.05)
  2726. end
  2727. end)
  2728. =end
  2729. # dlocsig.rb ends here