luup 0.0.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 (3) hide show
  1. checksums.yaml +7 -0
  2. data/bin/luup +174 -0
  3. metadata +64 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 74c26b80c698a923f1276a1b2d5dafe19374d23e88be9af99db98a64bc1ebfbc
4
+ data.tar.gz: 62abcf8d3457fc26f30d8ccaf778253d0e341b3ae15cdf2e5a70b975d9755ea3
5
+ SHA512:
6
+ metadata.gz: af94e453210387c5b3641e99a6f39e3998419a2611ed9a5e12f7771adaaa1c5ecaec12b30a9b7bd7212ddfa0c55f83dc081586854c6722e8b1e9ec442a6a74bf
7
+ data.tar.gz: b63e1d8eb593720cb9b2eed6c8f60d50725e0588db6b26d10e225c0dcd32567c54315d3f666f0745c71a54ff3e9447cbb6a6bca4e58ff1c7ce232d55d89526c3
data/bin/luup ADDED
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+ require 'open3'
3
+ require 'shellwords'
4
+ require 'io/console'
5
+ require 'pathname'
6
+ require 'pulseaudio_simple_ffi'
7
+ require 'pp'
8
+ require 'readline'
9
+
10
+ Pkg='luup'
11
+ Ver='0.0.0'
12
+ ConfigDir="#{Dir.home}/.config/#{Pkg}"
13
+ Dir.mkdir(ConfigDir) unless File.directory?(ConfigDir)
14
+
15
+ Channels,Rate,WordSize=2,44100,4
16
+ FrameSize=Channels*WordSize # "frame" is all channels of a sample
17
+ BytesPerSec=Rate*FrameSize
18
+ FfmpegFmt="-ac #{Channels} -ar #{Rate} -f f#{WordSize*8}le"
19
+ Quantum=24.0 # 24 FPS = cinematic
20
+
21
+ def frame_round(x) = ( ( x.to_i / FrameSize ).to_i * FrameSize ).to_i
22
+
23
+ ChunkSize=frame_round(BytesPerSec/Quantum/2)
24
+
25
+ def readline_hooked insert,prompt
26
+ Readline.pre_input_hook = -> do
27
+ Readline.insert_text insert
28
+ Readline.redisplay
29
+ Readline.pre_input_hook=nil
30
+ end
31
+ Readline.readline prompt
32
+ end
33
+
34
+ class Luup
35
+ attr_reader :data,:pulse,:start_time,:len,:off,:out,:mode
36
+
37
+ attr_reader :len_f,:off_f,:dis_f,:num_f
38
+ attr_reader :len_byte,:off_byte,:start_byte,:curr_byte,:end_byte,:out_byte
39
+
40
+ def initialize file,len_p,off_p='0',dis_p='0',num_p='1'
41
+ fn=Shellwords.escape(file)
42
+ print "decoding #{file}..."
43
+ @data,st = Open3.capture2("ffmpeg -loglevel -8 -i #{fn} #{FfmpegFmt} -",binmode:true)
44
+
45
+ self.num=num_p
46
+ self.len=len_p
47
+
48
+ self.dis=dis_p
49
+ self.off=off_p ? off_p : `aubioquiet #{fn} | head -1 | cut -d\\ -f2`
50
+
51
+ @start_time=Time.now.to_f
52
+ @pulse=PulseAudioSimpleFFI::PulseAudioSimpleO.new(Pkg,file,channels:Channels,rate:Rate,device:@device)
53
+ @mode=:play
54
+ @curr_byte=start_byte
55
+ @out_byte=0
56
+
57
+ @out=Thread.new{
58
+ until :quit == @mode
59
+ if @curr_byte-1==end_byte
60
+ @curr_byte=start_byte
61
+ elsif frame_round( @curr_byte + ChunkSize - end_byte+1)>0
62
+ extra_byte=frame_round( @curr_byte + ChunkSize - end_byte + 1)
63
+ extra_end_byte=frame_round(start_byte+extra_byte)-1
64
+ #puts "#{curr_byte} #{end_byte} #{start_byte} #{extra_byte} #{extra_end_byte} #{chunk_size}"
65
+ @pulse.write data[@curr_byte .. end_byte]
66
+ @pulse.write data[start_byte .. extra_end_byte]
67
+ @out_byte+=(end_byte-@curr_byte+1)+(extra_end_byte-start_byte+1)
68
+ @curr_byte=extra_end_byte+1
69
+ else
70
+ @pulse.write data[@curr_byte .. @curr_byte+ChunkSize-1]
71
+ @curr_byte+=ChunkSize
72
+ @out_byte+=ChunkSize
73
+ end
74
+ wrote_t,spent_t=out_byte.to_f/BytesPerSec,Time.now.to_f-@start_time
75
+ #puts "#{wrote_t} in #{spent_t}"
76
+ sleep (wrote_t-spent_t)/Quantum if (wrote_t>spent_t)
77
+ end
78
+ @pulse.close
79
+ }
80
+ end
81
+
82
+ def len= x
83
+ @len=x
84
+ @len_f=x.to_f
85
+ @len_byte=frame_round(@len_f*BytesPerSec*@num_f)
86
+ end
87
+
88
+ def off= x
89
+ @off=x
90
+ @off_f=x.to_f
91
+ @off_byte=(@off_f*Rate).to_i*FrameSize + frame_round(@dis_f*@len_byte/@num_f)
92
+ end
93
+
94
+ def dis= x
95
+ @dis=x
96
+ @dis_f=x.to_f
97
+ self.off=@off if @off
98
+ end
99
+
100
+ def num= x
101
+ @num=x
102
+ @num_f=x.to_f
103
+ self.len=@len if @len
104
+ end
105
+
106
+ def start_byte
107
+ @off_byte
108
+ end
109
+
110
+ def end_byte
111
+ @off_byte + @len_byte - 1
112
+ end
113
+
114
+ def main
115
+ until :quit == @mode
116
+ d=end_byte-start_byte
117
+ c=@curr_byte-start_byte
118
+ n=(c*256/d).to_i
119
+ #msg="%0.02f" % (curr_byte.to_f/BytesPerSec)
120
+ msg="%02X" % n
121
+ print "\e[2K\r#{msg} l:#{@len} o:#{@off} n:#{@num} d:#{@dis}> "
122
+ case ch=IO.console.raw{|c|c.read_nonblock(1) rescue ''}.downcase
123
+ when 'q'
124
+ @mode=:quit
125
+ puts 'quit!'
126
+ when 'o'
127
+ new_off=readline_hooked(@off,"o:")
128
+ if(new_off.to_f!=0)
129
+ self.off=new_off
130
+ @curr_byte=start_byte
131
+ end
132
+ when 'l'
133
+ new_len=readline_hooked(@len,"l:")
134
+ if(new_len.to_f>=5 and new_len.to_f<10)
135
+ self.len=new_len
136
+ @curr_byte=start_byte
137
+ end
138
+ when 'n'
139
+ new_num=readline_hooked(@num,"n:")
140
+ if(new_num.to_f>0)
141
+ self.num=new_num
142
+ @curr_byte=start_byte
143
+ end
144
+ when 'd'
145
+ s=-(@off_f/@len_f)
146
+ l=@data.length/BytesPerSec
147
+ e=(l-@off_f)/@len_f
148
+ new_dis=readline_hooked(@dis,"d=#{"%0.03f"%(s+0.0005)}-#{"%0.03f"%(e-0.0005)}:")
149
+ if(new_dis.to_f>=s && new_dis.to_f<=e)
150
+ self.dis=new_dis
151
+ @curr_byte=start_byte
152
+ end
153
+ when ''
154
+ sleep 1/Quantum
155
+ # else
156
+ # puts 'wtf'
157
+ #print (help="#{ch} < #{list_i>0?"[P]rev ":""}[R]estart [S]eek [N]ext [Q]uit")
158
+ end
159
+ end
160
+ puts "\r ] #{Pkg} ✝ v#{Ver} [ "
161
+ @out.join
162
+ end
163
+ end
164
+
165
+ if ARGV.length==0
166
+ puts "Usage: #{Pkg} file"
167
+ exit 1
168
+ end
169
+
170
+ main=Luup.new(file=Pathname(ARGV[0]).realpath.to_s,'7.7948','33.125')
171
+ data=main.data
172
+ o=main.pulse
173
+ main.main
174
+
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: luup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin Cook
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pulseaudio_simple_ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.0.1
33
+ description: luup - a dj tool that loops music and finetunes beatgrids
34
+ email: root@baryon.it
35
+ executables:
36
+ - luup
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - bin/luup
41
+ homepage: https://github.com/Canar/luup
42
+ licenses: []
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '3.0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements:
59
+ - ffmpeg
60
+ rubygems_version: 3.3.15
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: luup - a dj tool that loops music and finetunes beatgrids
64
+ test_files: []