luup 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []