dtas 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +4 -0
  3. data/Documentation/GNUmakefile +1 -1
  4. data/Documentation/dtas-console.txt +1 -0
  5. data/Documentation/dtas-player_protocol.txt +19 -5
  6. data/Documentation/dtas-splitfx.txt +13 -0
  7. data/Documentation/dtas-tl.txt +16 -0
  8. data/GIT-VERSION-GEN +1 -1
  9. data/GNUmakefile +2 -2
  10. data/INSTALL +3 -3
  11. data/README +4 -0
  12. data/bin/dtas-archive +5 -1
  13. data/bin/dtas-console +13 -6
  14. data/bin/dtas-cueedit +1 -1
  15. data/bin/dtas-mlib +47 -0
  16. data/bin/dtas-readahead +211 -0
  17. data/bin/dtas-sinkedit +1 -1
  18. data/bin/dtas-sourceedit +1 -1
  19. data/bin/dtas-splitfx +15 -6
  20. data/bin/dtas-tl +81 -5
  21. data/dtas.gemspec +2 -2
  22. data/lib/dtas.rb +17 -0
  23. data/lib/dtas/buffer/read_write.rb +21 -19
  24. data/lib/dtas/buffer/splice.rb +1 -2
  25. data/lib/dtas/format.rb +2 -2
  26. data/lib/dtas/mlib.rb +500 -0
  27. data/lib/dtas/mlib/migrations/0001_initial.rb +42 -0
  28. data/lib/dtas/nonblock.rb +24 -0
  29. data/lib/dtas/parse_freq.rb +29 -0
  30. data/lib/dtas/parse_time.rb +5 -2
  31. data/lib/dtas/pipe.rb +2 -1
  32. data/lib/dtas/player.rb +21 -41
  33. data/lib/dtas/player/client_handler.rb +175 -92
  34. data/lib/dtas/process.rb +41 -17
  35. data/lib/dtas/sigevent/pipe.rb +6 -5
  36. data/lib/dtas/sink.rb +1 -1
  37. data/lib/dtas/source/splitfx.rb +14 -0
  38. data/lib/dtas/splitfx.rb +52 -36
  39. data/lib/dtas/track.rb +13 -0
  40. data/lib/dtas/tracklist.rb +148 -43
  41. data/lib/dtas/unix_accepted.rb +49 -32
  42. data/lib/dtas/unix_client.rb +1 -1
  43. data/lib/dtas/unix_server.rb +17 -9
  44. data/lib/dtas/watchable.rb +16 -5
  45. data/test/test_env.rb +16 -0
  46. data/test/test_mlib.rb +31 -0
  47. data/test/test_parse_freq.rb +18 -0
  48. data/test/test_player_client_handler.rb +12 -12
  49. data/test/test_splitfx.rb +0 -29
  50. data/test/test_tracklist.rb +75 -17
  51. data/test/test_unixserver.rb +0 -11
  52. metadata +16 -4
@@ -0,0 +1,42 @@
1
+ # Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
2
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
3
+
4
+ Sequel.migration do
5
+ up do
6
+ create_table(:nodes) do
7
+ primary_key :id
8
+ String :name, null: false # encoding: binary, POSIX
9
+ Integer :ctime
10
+ foreign_key :parent_id, :nodes, null: false # parent dir
11
+ # >= 0: tlen of track, -2: ignore, -1: directory
12
+ Integer :tlen, null: false
13
+ unique [ :parent_id, :name ]
14
+ end
15
+
16
+ create_table(:tags) do
17
+ primary_key :id
18
+ String :tag, null: false, unique: true # encoding: US-ASCII
19
+ end
20
+
21
+ create_table(:vals) do
22
+ primary_key :id
23
+ String :val, null: false, unique: true # encoding: UTF-8
24
+ end
25
+
26
+ create_table(:comments) do
27
+ foreign_key :node_id, :nodes, null: false
28
+ foreign_key :tag_id, :tags, null: false
29
+ foreign_key :val_id, :vals, null: false
30
+ primary_key [ :node_id, :tag_id, :val_id ]
31
+ index :node_id
32
+ index [ :tag_id, :val_id ]
33
+ end
34
+ end
35
+
36
+ down do
37
+ drop_table(:nodes)
38
+ drop_table(:tags)
39
+ drop_table(:vals)
40
+ drop_table(:comments)
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
2
+ # License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
3
+
4
+ class DTAS::Nonblock < IO
5
+ if RUBY_VERSION.to_f <= 2.0
6
+ EX = {}.freeze
7
+ def read_nonblock(len, buf = nil, opts = EX)
8
+ super(len, buf)
9
+ rescue IO::WaitReadable
10
+ raise if opts[:exception]
11
+ :wait_readable
12
+ rescue EOFError
13
+ raise if opts[:exception]
14
+ nil
15
+ end
16
+
17
+ def write_nonblock(buf, opts = EX)
18
+ super(buf)
19
+ rescue IO::WaitWritable
20
+ raise if opts[:exception]
21
+ :wait_writable
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
2
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
3
+
4
+ require_relative '../dtas'
5
+ module DTAS::ParseFreq
6
+
7
+ # may return a negative frequency meaning lowpass
8
+ def parse_freq(val, round = true)
9
+ case val
10
+ when String
11
+ val = val.dup
12
+ mult = val.sub!(/k\z/, '') ? 1000 : 1
13
+ val = (val.to_f * mult)
14
+ when Numeric
15
+ val
16
+ else
17
+ raise ArgumentError, "non-numeric value given"
18
+ end
19
+
20
+ case round
21
+ when true, :int
22
+ val.round
23
+ when :float
24
+ val.to_f
25
+ else
26
+ raise ArgumentError, "usage: parse_freq(val, (true|:round))"
27
+ end
28
+ end
29
+ end
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
2
3
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
3
4
  require_relative '../dtas'
4
5
 
5
6
  module DTAS::ParseTime
6
- # convert a string time to seconds, returning a Flot or Integer
7
+ # convert a string time to seconds, returning a Floot or Integer
7
8
  def parse_time(time)
8
9
  case time
10
+ when Numeric
11
+ time
9
12
  when /\A\d+\z/
10
13
  time.to_i
11
14
  when /\A[\d\.]+\z/
@@ -15,7 +18,7 @@ def parse_time(time)
15
18
  rv = hhmmss.sub!(/\.(\d+)\z/, "") ? "0.#$1".to_f : 0
16
19
 
17
20
  # deal with HH:MM:SS
18
- t = hhmmss.split(/:/)
21
+ t = hhmmss.split(':')
19
22
  raise ArgumentError, "Bad time format: #{hhmmss}" if t.size > 3
20
23
 
21
24
  mult = 1
@@ -6,9 +6,10 @@
6
6
  end
7
7
  require_relative '../dtas'
8
8
  require_relative 'writable_iter'
9
+ require_relative 'nonblock'
9
10
 
10
11
  # pipe wrapper for -player sinks
11
- class DTAS::Pipe < IO # :nodoc:
12
+ class DTAS::Pipe < DTAS::Nonblock # :nodoc:
12
13
  include DTAS::WritableIter
13
14
  attr_accessor :sink
14
15
 
@@ -190,7 +190,7 @@ def enq_handler(io, msg)
190
190
  io.emit("OK")
191
191
  end
192
192
 
193
- def do_enq_head(io, msg)
193
+ def dpc_enq_head(io, msg)
194
194
  # check @queue[0] in case we have no sinks
195
195
  if need_to_queue
196
196
  @queue.unshift(msg)
@@ -207,53 +207,33 @@ def client_iter(io, msg)
207
207
  case command
208
208
  when "enq"
209
209
  enq_handler(io, msg[0])
210
- when "enq-head"
211
- do_enq_head(io, msg)
212
210
  when "enq-cmd"
213
211
  enq_handler(io, { "command" => msg[0]})
214
212
  when "pause", "play", "play_pause"
215
213
  play_pause_handler(io, command)
216
- when "seek"
217
- do_seek(io, msg[0])
218
- when "clear"
219
- @queue.clear
220
- wall("clear")
221
- io.emit("OK")
222
- when "rg"
223
- rg_handler(io, msg)
224
- when "cue"
225
- cue_handler(io, msg)
226
- when "skip"
227
- skip_handler(io, msg)
228
- when "sink"
229
- sink_handler(io, msg)
230
- when "current"
231
- current_handler(io, msg)
232
- when "watch"
233
- @watchers[io] = true
234
- io.emit("OK")
235
- when "format"
236
- format_handler(io, msg)
237
- when "env"
238
- env_handler(io, msg)
239
- when "restart"
240
- restart_pipeline
241
- io.emit("OK")
242
- when "source"
243
- source_handler(io, msg)
244
- when "state"
245
- state_file_handler(io, msg)
246
- when "cd"
247
- chdir_handler(io, msg)
248
214
  when "pwd"
249
215
  io.emit(Dir.pwd)
250
- when "tl"
251
- tl_handler(io, msg)
252
- when "trim"
253
- trim_handler(io, msg)
216
+ else
217
+ m = "dpc_#{command.tr('-', '_')}"
218
+ __send__(m, io, msg) if respond_to?(m)
254
219
  end
255
220
  end
256
221
 
222
+ def dpc_clear(io, msg)
223
+ @queue.clear
224
+ wall('clear')
225
+ io.emit('OK')
226
+ end
227
+
228
+ def dpc_queue(io, msg)
229
+ 'cat' == msg[0] and io.emit(@queue.to_yaml)
230
+ end
231
+
232
+ def dpc_watch(io, _)
233
+ @watchers[io] = true
234
+ io.emit('OK')
235
+ end
236
+
257
237
  def event_loop_iter
258
238
  @srv.run_once do |io, msg| # readability handler, request/response
259
239
  case io
@@ -307,7 +287,7 @@ def sink_death(sink, status)
307
287
  return unless sink.active
308
288
 
309
289
  if (@current || @queue[0]) && !@paused
310
- # we get here if source/sinks are all killed in restart_pipeline
290
+ # we get here if source/sinks are all killed in dpc_restart
311
291
  __sink_activate(sink)
312
292
  next_source(_next) unless @current
313
293
  end
@@ -426,7 +406,7 @@ def next_source(source_spec)
426
406
 
427
407
  dst = @sink_buf
428
408
  pending.dst_assoc(dst)
429
- pending.src_spawn(@format, @rg, out: dst.wr, in: "/dev/null")
409
+ pending.src_spawn(@format, @rg, out: dst.wr, in: DTAS.null)
430
410
 
431
411
  # watch and restart on modifications
432
412
  pending.respond_to?(:watch_begin) and
@@ -117,7 +117,7 @@ def __sink_snapshot(sink)
117
117
  end
118
118
 
119
119
  # returns a wait_ctl arg
120
- def sink_handler(io, msg)
120
+ def dpc_sink(io, msg)
121
121
  name = msg[1]
122
122
  case msg[0]
123
123
  when "ls"
@@ -240,7 +240,7 @@ def current_expect_samples(in_samples) # @current.samples
240
240
  out_samples(in_samples, @current.format, @format)
241
241
  end
242
242
 
243
- def rg_handler(io, msg)
243
+ def dpc_rg(io, msg)
244
244
  return io.emit(@rg.to_hsh.to_yaml) if msg.empty?
245
245
  before = @rg.to_hsh
246
246
  msg.each do |kv|
@@ -277,7 +277,7 @@ def active_sinks
277
277
 
278
278
  # show current info about what's playing
279
279
  # returns non-blocking iterator retval
280
- def current_handler(io, msg)
280
+ def dpc_current(io, msg)
281
281
  tmp = {}
282
282
  if @current
283
283
  tmp["current"] = s = @current.to_hsh
@@ -312,6 +312,7 @@ def current_handler(io, msg)
312
312
  h
313
313
  end
314
314
  end
315
+ tmp['tracklist'] = @tl.to_hsh(false)
315
316
  io.emit(tmp.to_yaml)
316
317
  end
317
318
 
@@ -321,7 +322,7 @@ def __buf_reset(buf) # buf is always @sink_buf for now
321
322
  @srv.wait_ctl(buf, :wait_readable)
322
323
  end
323
324
 
324
- def skip_handler(io, msg)
325
+ def dpc_skip(io, msg)
325
326
  __current_drop
326
327
  wall("skip")
327
328
  io.emit("OK")
@@ -371,7 +372,8 @@ def seek_internal(cur, offset)
371
372
  end
372
373
  end
373
374
 
374
- def do_seek(io, offset)
375
+ def dpc_seek(io, msg)
376
+ offset = msg[0]
375
377
  if @current
376
378
  if @current.respond_to?(:infile)
377
379
  begin
@@ -408,7 +410,12 @@ def restart_pipeline
408
410
  stop_sinks
409
411
  end
410
412
 
411
- def format_handler(io, msg)
413
+ def dpc_restart(io, _)
414
+ restart_pipeline
415
+ io.emit('OK')
416
+ end
417
+
418
+ def dpc_format(io, msg)
412
419
  new_fmt = @format.dup
413
420
  msg.each do |kv|
414
421
  k, v = kv.split(/=/, 2)
@@ -443,7 +450,7 @@ def format_handler(io, msg)
443
450
  io.emit("OK")
444
451
  end
445
452
 
446
- def env_handler(io, msg)
453
+ def dpc_env(io, msg)
447
454
  if msg.empty?
448
455
  # this may fail for large envs due to SEQPACKET size restrictions
449
456
  # do we care?
@@ -465,7 +472,7 @@ def env_handler(io, msg)
465
472
  io.emit("OK")
466
473
  end
467
474
 
468
- def source_handler(io, msg)
475
+ def dpc_source(io, msg)
469
476
  map = @source_map
470
477
  op = msg.shift
471
478
  case op
@@ -509,7 +516,7 @@ def source_handler(io, msg)
509
516
  end
510
517
  end
511
518
 
512
- def chdir_handler(io, msg)
519
+ def dpc_cd(io, msg)
513
520
  msg.size == 1 or return io.emit("ERR usage: cd DIRNAME")
514
521
  begin
515
522
  Dir.chdir(msg[0])
@@ -520,9 +527,27 @@ def chdir_handler(io, msg)
520
527
  io.emit("OK")
521
528
  end
522
529
 
523
- def state_file_handler(io, msg)
530
+ def state_file_dump_async(io, sf)
531
+ on_death = lambda { |_| @srv.wait_ctl(io, :wait_readable) }
532
+ pid = fork do
533
+ begin
534
+ begin
535
+ sf.dump(self)
536
+ res = 'OK'
537
+ rescue => e
538
+ res = "ERR dumping to #{xs(sf.path)} #{e.message}"
539
+ end
540
+ io.to_io.send(res, Socket::MSG_EOR)
541
+ ensure
542
+ exit!(0)
543
+ end
544
+ end
545
+ DTAS::Process::PIDS[pid] = on_death
546
+ end
547
+
548
+ def dpc_state(io, msg)
524
549
  case msg.shift
525
- when "dump"
550
+ when 'dump'
526
551
  dest = msg.shift
527
552
  if dest
528
553
  sf = DTAS::StateFile.new(dest, false)
@@ -532,13 +557,9 @@ def state_file_handler(io, msg)
532
557
  else
533
558
  return io.emit("ERR no state file configured")
534
559
  end
535
- begin
536
- sf.dump(self)
537
- rescue => e
538
- return io.emit("ERR dumping to #{xs(dest)} #{e.message}")
539
- end
560
+ state_file_dump_async(io, sf)
561
+ :ignore
540
562
  end
541
- io.emit("OK")
542
563
  end
543
564
 
544
565
  def _tl_skip
@@ -546,84 +567,144 @@ def _tl_skip
546
567
  __current_drop
547
568
  end
548
569
 
549
- def tl_handler(io, msg)
570
+ def dpc_tl(io, msg)
571
+ sub = msg.shift
572
+ m = "_dpc_tl_#{sub.tr('-', '_')}"
573
+ __send__(m, io, msg) if respond_to?(m)
574
+ end
575
+
576
+ def _dpc_tl_add(io, msg)
577
+ path = msg.shift
578
+ after_track_id = msg.shift
579
+ after_track_id = after_track_id.to_i if after_track_id
580
+ case set_as_current = msg.shift
581
+ when 'true' then set_as_current = true
582
+ when 'false', nil then set_as_current = false
583
+ else
584
+ return io.emit('ERR tl add PATH [after_track_id] [true|false]')
585
+ end
586
+ begin
587
+ track_id = @tl.add_track(path, after_track_id, set_as_current)
588
+ return io.emit('ERR FULL') unless track_id
589
+ rescue ArgumentError => e
590
+ return io.emit("ERR #{e.message}")
591
+ end
592
+
593
+ _tl_skip if set_as_current # if @current is playing, it will restart soon
594
+
595
+ # start playing if we're currently idle
596
+ next_source(_next) unless need_to_queue
597
+ io.emit(track_id.to_s)
598
+ end
599
+
600
+ def _dpc_tl_repeat(io, msg)
601
+ prev = @tl.repeat.to_s
550
602
  case msg.shift
551
- when "add"
552
- path = msg.shift
553
- after_track_id = msg.shift
554
- after_track_id = after_track_id.to_i if after_track_id
555
- case set_as_current = msg.shift
556
- when "true" then set_as_current = true
557
- when "false", nil then set_as_current = false
558
- else
559
- return io.emit("ERR tl add PATH [after_track_id] [true|false]")
560
- end
561
- begin
562
- track_id = @tl.add_track(path, after_track_id, set_as_current)
563
- rescue ArgumentError => e
564
- return io.emit("ERR #{e.message}")
565
- end
603
+ when 'true' then @tl.repeat = true
604
+ when 'false' then @tl.repeat = false
605
+ when '1' then @tl.repeat = 1
606
+ when nil
607
+ end
608
+ io.emit("tl repeat #{prev}")
609
+ end
566
610
 
567
- _tl_skip if set_as_current # if @current is playing, it will restart soon
611
+ def _dpc_tl_shuffle(io, msg)
612
+ prev = (!!@tl.shuffle).to_s
613
+ v = msg.shift
614
+ case v
615
+ when 'debug' then return io.emit(@tl.shuffle.to_yaml) # TODO: remove
616
+ when nil
617
+ else
618
+ set_bool(io, 'tl shuffle', v) { |b| @tl.shuffle = b }
619
+ end
620
+ io.emit("tl shuffle #{prev}")
621
+ end
568
622
 
569
- # start playing if we're currently idle
570
- next_source(_next) unless need_to_queue
571
- io.emit("#{track_id}")
572
- when "repeat"
573
- case msg.shift
574
- when "true" then @tl.repeat = true
575
- when "false" then @tl.repeat = false
576
- when "1" then @tl.repeat = 1
577
- when nil
578
- return io.emit("repeat #{@tl.repeat.to_s}")
579
- end
580
- io.emit("OK")
581
- when "remove"
582
- track_id = msg.shift or return io.emit("ERR track_id not specified")
583
- track_id = track_id.to_i
584
- @tl.remove_track(track_id) or return io.emit("MISSING")
585
-
586
- # skip if we're removing the currently playing track
587
- if @current && @current.respond_to?(:infile) &&
588
- @current.infile.object_id == track_id
589
- _tl_skip
590
- end
591
- # drop it from the queue, too, in case it just got requeued or paused
592
- @queue.delete_if { |t| Array === t && t[0].object_id == track_id }
593
- io.emit("OK")
594
- when "get"
595
- res = @tl.get_tracks(msg.map!(&:to_i))
596
- res.map! { |tid, file| "#{tid}=#{file ? Shellwords.escape(file) : ''}" }
597
- io.emit("#{res.size} #{res.join(' ')}")
598
- when "tracks"
599
- tracks = @tl.tracks
600
- io.emit("#{tracks.size} " << tracks.map!(&:to_s).join(' '))
601
- when "goto"
602
- track_id = msg.shift or return io.emit("ERR track_id not specified")
603
- offset = msg.shift # may be nil
604
- if @tl.go_to(track_id.to_i, offset)
605
- _tl_skip
606
- next_source(_next) unless need_to_queue
607
- io.emit("OK")
608
- else
609
- io.emit("MISSING")
610
- end
611
- when "current"
612
- path = @tl.cur_track
613
- io.emit(path ? path : "NONE")
614
- when "current-id"
615
- path = @tl.cur_track
616
- io.emit(path ? path.object_id.to_s : "NONE")
617
- when "next"
623
+ def _dpc_tl_max(io, msg)
624
+ prev = @tl.max
625
+ case msg.shift
626
+ when nil
627
+ when %r{\A(\d[\d_]*)\z} then @tl.max = $1.to_i
628
+ else
629
+ return io.emit('ERR tl max must a non-negative integer')
630
+ end
631
+ io.emit("tl max #{prev}")
632
+ end
633
+
634
+ def _dpc_tl_remove(io, msg)
635
+ track_id = msg.shift or return io.emit('ERR track_id not specified')
636
+ track_id = track_id.to_i
637
+ path = @tl.remove_track(track_id) or return io.emit('MISSING')
638
+ rm = path.object_id
639
+
640
+ # skip if we're removing the currently playing track
641
+ if @current && @current.respond_to?(:infile) &&
642
+ @current.infile.object_id == rm
618
643
  _tl_skip
619
- io.emit("OK")
620
- when "prev"
621
- @tl.previous!
644
+ end
645
+ # drop it from the queue, too, in case it just got requeued or paused
646
+ @queue.delete_if { |t| Array === t && t[0].object_id == rm }
647
+ io.emit(path)
648
+ end
649
+
650
+ def _dpc_tl_get(io, msg)
651
+ res = @tl.get_tracks(msg.map!(&:to_i))
652
+ res.map! { |tid, file| "#{tid}=#{file ? Shellwords.escape(file) : ''}" }
653
+ io.emit("#{res.size} #{res.join(' ')}")
654
+ end
655
+
656
+ def _dpc_tl_tracks(io, msg)
657
+ tracks = @tl.tracks
658
+ io.emit("#{tracks.size} " << tracks.map!(&:to_s).join(' '))
659
+ end
660
+
661
+ def _dpc_tl_goto(io, msg)
662
+ track_id = msg.shift or return io.emit('ERR track_id not specified')
663
+ offset = msg.shift # may be nil
664
+ if @tl.go_to(track_id.to_i, offset)
622
665
  _tl_skip
623
- io.emit("OK")
666
+ next_source(_next) unless need_to_queue
667
+ io.emit('OK')
668
+ else
669
+ io.emit('MISSING')
624
670
  end
625
671
  end
626
672
 
673
+ def _dpc_tl_current(io, msg)
674
+ track = @tl.cur_track
675
+ io.emit(track ? track.to_path : 'NONE')
676
+ end
677
+
678
+ def _dpc_tl_current_id(io, msg)
679
+ track = @tl.cur_track
680
+ io.emit(track ? track.track_id.to_s : 'NONE')
681
+ end
682
+
683
+ def _dpc_tl_next(io, msg)
684
+ _tl_skip
685
+ io.emit('OK')
686
+ end
687
+
688
+ def _dpc_tl_prev(io, msg)
689
+ @tl.previous!
690
+ _tl_skip
691
+ io.emit('OK')
692
+ end
693
+
694
+ def _dpc_tl_clear(io, msg)
695
+ @tl.clear
696
+ _tl_skip
697
+ io.emit('OK')
698
+ end
699
+
700
+ def _dpc_tl_swap(io, msg)
701
+ usage = 'ERR usage: "tl swap TRACK_ID_A TRACK_ID_B"'
702
+ a_id = msg.shift or return io.emit(usage)
703
+ b_id = msg.shift or return io.emit(usage)
704
+ @tl.swap(a_id.to_i, b_id.to_i) or return io.emit('MISSING')
705
+ io.emit('OK')
706
+ end
707
+
627
708
  def __bp_prev_next(io, msg, cur, bp)
628
709
  case type = msg[1]
629
710
  when nil, "track"
@@ -663,7 +744,7 @@ def __bp_prev_next(io, msg, cur, bp)
663
744
  io.emit("OK")
664
745
  end
665
746
 
666
- def cue_handler(io, msg)
747
+ def dpc_cue(io, msg)
667
748
  cur = @current
668
749
  if cur.respond_to?(:cuebreakpoints)
669
750
  bp = cur.cuebreakpoints
@@ -684,10 +765,10 @@ def cue_handler(io, msg)
684
765
  end
685
766
  end
686
767
 
687
- def trim_handler(io, msg)
768
+ def dpc_trim(io, msg)
769
+ t = @trim
688
770
  case msg.size
689
- when 0
690
- io.emit({ 'trim' => @trim }.to_yaml)
771
+ when 0 # OK
691
772
  when 1, 2
692
773
  case msg[0]
693
774
  when 'off'
@@ -706,8 +787,10 @@ def trim_handler(io, msg)
706
787
  end
707
788
  end
708
789
  __current_requeue
709
- io.emit('OK')
790
+ else
791
+ return io.emit('ERR usage: trim [off|TBEG [TLEN]]')
710
792
  end
793
+ io.emit(t ? t.map(&:to_s).join(' ') : 'off')
711
794
  end
712
795
  end
713
796
  # :startdoc: