gswax 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/bin/gswax +4 -0
  2. data/lib/gswax.rb +306 -0
  3. data/lib/gswax/Wax.rb +273 -0
  4. data/lib/gswax/browser.rb +224 -0
  5. data/lib/gswax/images/arm.png +0 -0
  6. data/lib/gswax/images/default.png +0 -0
  7. data/lib/gswax/images/defaultHOVER.png +0 -0
  8. data/lib/gswax/images/dugout.png +0 -0
  9. data/lib/gswax/images/dugoutHOVER.png +0 -0
  10. data/lib/gswax/images/info.png +0 -0
  11. data/lib/gswax/images/infoHOVER.png +0 -0
  12. data/lib/gswax/images/next.png +0 -0
  13. data/lib/gswax/images/nextHOVER.png +0 -0
  14. data/lib/gswax/images/no_cover.png +0 -0
  15. data/lib/gswax/images/pause.png +0 -0
  16. data/lib/gswax/images/pauseHOVER.png +0 -0
  17. data/lib/gswax/images/play.png +0 -0
  18. data/lib/gswax/images/playHOVER.png +0 -0
  19. data/lib/gswax/images/playlist.png +0 -0
  20. data/lib/gswax/images/playlistHOVER.png +0 -0
  21. data/lib/gswax/images/prev.png +0 -0
  22. data/lib/gswax/images/prevHOVER.png +0 -0
  23. data/lib/gswax/images/settings.png +0 -0
  24. data/lib/gswax/images/settingsHOVER.png +0 -0
  25. data/lib/gswax/images/shuffle.png +0 -0
  26. data/lib/gswax/images/shuffleHOVER.png +0 -0
  27. data/lib/gswax/images/stanton.png +0 -0
  28. data/lib/gswax/images/stanton1.png +0 -0
  29. data/lib/gswax/images/vol.png +0 -0
  30. data/lib/gswax/images/volslider.png +0 -0
  31. data/lib/gswax/playlist.rb +182 -0
  32. data/lib/gswax/playlists/default +0 -0
  33. data/lib/gswax/scrollbox.rb +100 -0
  34. data/lib/gswax/settings/settings.yml +16 -0
  35. data/lib/gswax/settings_manager.rb +284 -0
  36. data/lib/gswax/shared.rb +206 -0
  37. data/lib/gswax/turntable.rb +251 -0
  38. data/lib/gswax/version.rb +3 -0
  39. metadata +107 -0
@@ -0,0 +1,4 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ load "gswax.rb"
4
+
@@ -0,0 +1,306 @@
1
+ =begin
2
+
3
+ gsWax
4
+ v 0.12.01
5
+
6
+ an audio player for folks who miss their vinyl
7
+
8
+ Copyright (C) 2012 Justin Kaiden
9
+
10
+ This program is free software: you can redistribute it and/or modify
11
+ it under the terms of the GNU General Public License as published by
12
+ the Free Software Foundation, either version 3 of the License, or
13
+ (at your option) any later version.
14
+
15
+ This program is distributed in the hope that it will be useful,
16
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ GNU General Public License for more details.
19
+
20
+ You should have received a copy of the GNU General Public License
21
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
22
+ =end
23
+
24
+
25
+
26
+ require 'green_shoes'
27
+ require 'observer'
28
+ require 'cgi'
29
+ require File.join("gswax", "version.rb")
30
+ require File.join("gswax", "shared.rb")
31
+ require File.join("gswax", "Wax")
32
+ require File.join("gswax", "turntable")
33
+ require File.join("gswax", "playlist")
34
+ require File.join("gswax", "browser")
35
+ require File.join("gswax", "scrollbox")
36
+ require File.join("gswax", "settings_manager")
37
+
38
+ module Gswax
39
+
40
+ Settings.read
41
+ scl = Settings.scale
42
+
43
+
44
+ Shoes.app(width: (727 * scl), height: (675 * scl), title: "gsWax") do
45
+
46
+ Shoes::App.class_eval{include Wax}
47
+ init_wax
48
+ wax_pipeline.volume = Settings.volume
49
+
50
+ Gtk::Drag.dest_set(
51
+ self.win,
52
+ Gtk::Drag::DEST_DEFAULT_ALL,
53
+ [["text/plain", 0, 0]],
54
+ Gdk::DragContext::ACTION_COPY
55
+ )
56
+ self.win.signal_connect("drag_data_received"){|w, context, x, y, data, i, t|
57
+ data_drop(data.data, context)
58
+ }
59
+
60
+
61
+ def on_signals(message)
62
+ if message.class == Array
63
+ cmd = message[0].split(":")[-1]
64
+ msg = message[1..-1]
65
+ else
66
+ cmd, msg = nil, nil
67
+ end
68
+
69
+ case message
70
+ when "wax_info"; on_wax_info
71
+ when "eos"; @table.update(Settings.at_bat)
72
+ when "PLAYLIST"; playlist(wax_lineup)
73
+ when "CLEAR_LIST"; clear_list
74
+ when "BROWSER"; browser
75
+ when "SETTINGS"; settings_manager
76
+ when "UPDATE_SETTINGS"; update_settings
77
+ when "TRACK_PROGRESS"; track_progress
78
+ when "PLAY_PAUSE_TRACK"; play_pause_track
79
+ when "PREVIOUS_TRACK"; previous_track
80
+ when "NEXT_TRACK"; next_track
81
+ when "SHUFFLE_TOGGLE"; toggle_wax_shuffle
82
+ when "PLAYLIST_CLOSED"; @browser.delete_observer(@playlist) if @browser; @playlist = nil
83
+ when "BROWSER_CLOSED"; @browser = nil
84
+ when "SETTINGS_CLOSED"; @settings_man = nil
85
+ end
86
+
87
+ case cmd
88
+ when "PLAY_NOW"; play_now(message[-1])
89
+ when "APPEND"; add_to_list(message[1..-1])
90
+ when "PREPEND"; add_to_list(message[1..-1], "prepend")
91
+ when "SAVE_LIST"; Settings.playlist_file = message[1]
92
+ when "LOAD_LIST"; load_playlist(message)
93
+ when "VOLUME"; set_volume(message[-1])
94
+ when "SEEK"; seek(message[-1])
95
+ end
96
+
97
+ end
98
+
99
+ def on_wax_info
100
+ @info_area.set_text(wax_info) if @info_area
101
+ end
102
+
103
+ def add_to_list(tracks, pos = "append")
104
+ flag = true if wax_lineup.empty?
105
+
106
+ if pos == "prepend"
107
+ tracks.reverse.each{|e| wax_lineup.insert(0, e)}
108
+ else
109
+ tracks.each{|e| wax_lineup << e}
110
+ end
111
+
112
+ if flag
113
+ batter_up_wax
114
+ @info_area.set_text("gsWax - ready to rock")
115
+ @table.update(Settings.at_bat)
116
+ end
117
+ end
118
+
119
+ def data_drop(data, context)
120
+ raw = CGI.unescape(data)
121
+ arr = raw.chomp.split("file:#{File::Separator}#{File::Separator}")
122
+ path = arr[-1]
123
+ okfiles = [/.mp3/, /.flac/, /.ogg/, /.wav/]
124
+ selected = []
125
+
126
+ if File.directory?(path)
127
+ Find.find(path){|item|
128
+ okfiles.each{|ok| selected << item if item.downcase =~ ok}
129
+ }
130
+ elsif File.exists?(path)
131
+ okfiles.each{|ok| selected << path if path.downcase =~ ok}
132
+ end
133
+
134
+ add_to_list(selected)
135
+ @playlist.add(selected) if @playlist
136
+
137
+ Gtk::Drag.finish(context, true, false, 0)
138
+ end
139
+
140
+ def browser
141
+ if @browser
142
+ @browser.close_window; @browser = nil
143
+ else
144
+ if Settings.music_dir
145
+ if File.exists?(Settings.music_dir)
146
+ dir = Settings.music_dir
147
+ end
148
+ else
149
+ dir = Settings.brains_dir
150
+ end
151
+ @browser = DirBrowser.new(dir)
152
+ @browser.add_observer(self, :on_signals)
153
+ @browser.add_observer(@playlist, :on_browser_signals) if @playlist
154
+ end
155
+ end
156
+
157
+ def playlist(list)
158
+ if @playlist
159
+ #@playlist.delete_observers
160
+ @browser.delete_observer(@playlist) if @browser
161
+ @playlist.close_window
162
+ @playlist = nil
163
+ else
164
+ @playlist = PlayList.new; @playlist.add(list)
165
+ @playlist.add_observer(self, :on_signals)
166
+ @browser.add_observer(@playlist, :on_browser_signals) if @browser
167
+ end
168
+ end
169
+
170
+ def load_playlist(message)
171
+ stop_wax if wax_state != "stopped"
172
+ f_name = message[1]
173
+ Settings.playlist_file = f_name
174
+ self.wax_batter = 0
175
+ read_wax_lineup
176
+ @table.update(Settings.at_bat)
177
+ @info_area.set_text("gsWax - ready to rock")
178
+ end
179
+
180
+ def clear_list
181
+ stop_wax if wax_state != "stopped"
182
+ wax_lineup.clear
183
+ wax_lupine.clear
184
+ self.wax_batter = 0
185
+ batter_up_wax
186
+ @table.update(Settings.at_bat)
187
+ @table.set_state("stopped")
188
+ @info_area.set_text("gsWax")
189
+ end
190
+
191
+ def settings_manager
192
+ if @settings_man
193
+ @settings_man.close_window; @settings_man = nil
194
+ else
195
+ @settings_man = SettingsManager.new
196
+ @settings_man.add_observer(self, :on_signals)
197
+ end
198
+ end
199
+
200
+ def track_progress
201
+ unless @tracking
202
+ GLib::Timeout.add(100){
203
+ if wax_state == "playing"
204
+ pos_query = Gst::QueryPosition.new(3)#(Gst::Format::Time)
205
+ wax_pipeline.query(pos_query)
206
+ track_pos = pos_query.parse[1] / 10000.0
207
+ arm_pos =(((track_pos * 1.0) / wax_duration) / 1000.0).round(1)
208
+ @table.set_arm_pos(arm_pos)
209
+ @tracking = true
210
+ else
211
+ @tracking = false
212
+ false
213
+ end
214
+ }
215
+ end
216
+ end
217
+
218
+ def play_now(track)
219
+ if wax_lineup.include?(track)
220
+ self.wax_batter = wax_lineup.index(track) unless Settings.shuffle
221
+ self.wax_batter = wax_lupine.index(track) if Settings.shuffle
222
+ batter_up_wax
223
+ stop_wax; play_pause_track
224
+ else
225
+ if wax_lineup.empty?
226
+ wax_lineup << track
227
+ batter_up_wax
228
+ playpause_track
229
+ else
230
+ wax_lineup.insert(wax_batter + 1, track) unless Settings.shuffle
231
+ wax_lupine.insert(wax_batter + 1, track) if Settings.shuffle
232
+ next_track
233
+ end
234
+ end
235
+ @table.update(Settings.at_bat)
236
+ end
237
+
238
+ def play_pause_track
239
+ playpause_wax
240
+ @table.set_state(wax_state)
241
+ track_progress
242
+ end
243
+
244
+ def previous_track
245
+ prev_wax
246
+ @table.update(Settings.at_bat)
247
+ @table.set_state(wax_state)
248
+ end
249
+
250
+ def next_track
251
+ next_wax
252
+ @table.update(Settings.at_bat)
253
+ @table.set_state(wax_state)
254
+ end
255
+
256
+ def set_volume(vol)
257
+ unless vol == Settings.volume
258
+ wax_pipeline.volume = vol
259
+ Settings.volume = vol
260
+ end
261
+ end
262
+
263
+ def seek(percent)
264
+ if wax_duration
265
+ sought = wax_duration * percent
266
+ seek_to_wax((sought * 1000.0).round)
267
+ @table.set_arm_pos(percent)
268
+ end
269
+ end
270
+
271
+ def update_settings
272
+ canvas.children.each{|child| canvas.remove(child)}
273
+ self.win.resize(727 * Settings.scale, 675 * Settings.scale)
274
+ @info_area.resize
275
+ @table.resize
276
+ @table.set_state(wax_state)
277
+ set_table
278
+ @info_area.show_text(wax_info)
279
+ end
280
+
281
+ def set_table
282
+ canvas.put(@info_area.main, 0, 0)
283
+ canvas.put(@table.main, 0, 77.0 * Settings.scale)
284
+ end
285
+
286
+ bga = Settings.bg_color
287
+ bga.each{|v| v = v / 257}
288
+ background(bga)
289
+
290
+ @info_area = ScrollBox.new(wax_info)
291
+ @table = Turntable.new
292
+ @table.add_observer(self, :on_signals)
293
+ @table.update(Settings.at_bat)
294
+ @table.main.signal_connect("destroy"){
295
+ stop_wax
296
+ Settings.line_up = wax_lineup
297
+ Settings.save
298
+ }
299
+
300
+ set_table
301
+
302
+ add_observer(self, :on_signals) # (add self as an observer to Wax)
303
+
304
+ end #Shoes.app
305
+
306
+ end #Module
@@ -0,0 +1,273 @@
1
+ =begin
2
+
3
+ this file is part of: gsWax v. 0.12.01
4
+
5
+ You should have received a copy of the GNU General Public License
6
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
7
+ =end
8
+
9
+
10
+ require 'gst'
11
+
12
+ module Wax
13
+
14
+ include Observable
15
+
16
+ attr_accessor :wax_coverart, :wax_duration, :wax_state, :wax_pipeline,
17
+ :wax_batter, :wax_lineup, :wax_lupine, :wax_info
18
+
19
+ #----------------------
20
+ # INITIALIZE
21
+ #----------------------
22
+
23
+ def init_wax
24
+ Settings.read
25
+ @wax_pipeline = Gst::ElementFactory.make("playbin2")
26
+ @wax_state = "stopped"
27
+ @wax_info = ""
28
+ read_wax_lineup
29
+ end
30
+
31
+ def read_wax_lineup
32
+ @wax_lineup = []
33
+ unless Settings.line_up.empty?
34
+ @wax_lineup = Settings.line_up
35
+ @wax_info = "gsWax - ready to rock"
36
+ else
37
+ @wax_info = "gsWax"
38
+ send_wax_info("wax_info")
39
+ end
40
+ @wax_lineup.compact!
41
+ @wax_lupine = @wax_lineup.shuffle
42
+
43
+ if Settings.shuffle
44
+ if Settings.at_bat
45
+ @wax_batter = @wax_lupine.index(Settings.at_bat)
46
+ else
47
+ @wax_batter = 0
48
+ end
49
+ else
50
+ if Settings.at_bat
51
+ @wax_batter = @wax_lineup.index(Settings.at_bat)
52
+ else
53
+ @wax_batter = 0
54
+ end
55
+ end
56
+
57
+ batter_up_wax
58
+ end
59
+
60
+ def batter_up_wax
61
+ @wax_batter = 0 unless @wax_batter
62
+ @wax_lupine = @wax_lineup.shuffle if @wax_lupine.empty?
63
+ if Settings.shuffle
64
+ Settings.at_bat = @wax_lupine[@wax_batter]
65
+ else
66
+ Settings.at_bat = @wax_lineup[@wax_batter]
67
+ end
68
+ end
69
+
70
+
71
+ #-----------------------------------------------------------
72
+ # PLAY / GET DURATION / GET TAGS
73
+ #-----------------------------------------------------------
74
+
75
+ def play_wax
76
+ if @wax_lineup.empty?
77
+ batter_up_wax
78
+ end
79
+ if Settings.at_bat
80
+ if File.exists?(Settings.at_bat)
81
+ @wax_pipeline.uri= GLib.filename_to_uri(Settings.at_bat)
82
+ bus = @wax_pipeline.bus
83
+ @tagMsg = []
84
+
85
+ bus.add_watch {|bus, message|
86
+ case message.type
87
+ when Gst::Message::ERROR
88
+ p message.parse
89
+ when Gst::Message::EOS
90
+ @wax_state = "stopped"
91
+ next_wax
92
+ send_wax_info("eos")
93
+ when Gst::Message::TAG
94
+ @tagMsg << message.structure.entries
95
+ get_wax_tags
96
+ end
97
+ true
98
+ }
99
+
100
+ @wax_pipeline.play
101
+ @wax_state = "playing"
102
+ get_wax_duration
103
+ end
104
+
105
+ else
106
+ @wax_info = "no tracks"
107
+ send_wax_info("wax_info")
108
+
109
+ end
110
+ end
111
+
112
+ def get_wax_duration
113
+ now = Time.now.sec.to_f
114
+ now = 0.0 if now == 59.0
115
+ @limit = now + 2.0
116
+
117
+ GLib::Timeout.add(100){
118
+ @qd = Gst::QueryDuration.new(3)#(Gst::Format::Type::TIME)
119
+ @wax_pipeline.query(@qd)
120
+ @wax_duration = @qd.parse[1]/1000000000
121
+ if @wax_duration > 0
122
+ send_wax_info("TRACK_PROGRESS")
123
+ false
124
+ elsif
125
+ Time.now.sec.to_f > @limit
126
+ false
127
+ else
128
+ true
129
+ end
130
+ }
131
+ end
132
+
133
+ def get_wax_tags
134
+ @gotTags = false
135
+ @tags = @tagMsg.flatten
136
+
137
+ if @tags.include?("title")
138
+ @title = @tags[@tags.index("title") + 1]; @gotTags = true
139
+ else @title = nil; end
140
+ if @tags.include?("artist")
141
+ @artist = @tags[@tags.index("artist") + 1]; @gotTags = true
142
+ else @artist = nil; end
143
+ if @tags.include?("album")
144
+ @album = @tags[@tags.index("album") + 1]; @gotTags = true
145
+ else @album = nil; end
146
+ if @tags.include?("comments")
147
+ @comments = @tags[@tags.index("comments") + 1]; @gotTags = true
148
+ else @comments = nil; end
149
+ if @tags.include?("track-number")
150
+ @tracknumber = @tags[@tags.index("track-number") + 1]; @gotTags = true
151
+ else @tracknumber = nil; end
152
+ if @tags.include?("genre")
153
+ @genre = @tags[@tags.index("genre") + 1]; @gotTags = true
154
+ else @genre = nil; end
155
+ if @tags.include?("album-artist")
156
+ @albumartist = @tags[@tags.index("album-artist") + 1]; @gotTags = true
157
+ else @albumartist = nil; end
158
+
159
+ split = Settings.title_format.split("#")
160
+ @infoentries = []
161
+ split.each{|i|
162
+ i = @title if i == "title"
163
+ i = @album if i == "album"
164
+ i = @artist if i == "artist"
165
+ i = @genre if i == "genre"
166
+ i = @albumartist if i == "album-artist"
167
+ i = @tracknumber if i == "track-number"
168
+ i = @comments if i == "comments"
169
+ @infoentries << i
170
+ }
171
+
172
+ if @gotTags == false
173
+ @wax_info = File.basename(Settings.at_bat)
174
+ send_wax_info("wax_info")
175
+ else
176
+ @infoentries.compact!
177
+ @wax_info = @infoentries.join
178
+ send_wax_info("wax_info")
179
+ end
180
+
181
+ end #get_wax_tags
182
+
183
+
184
+ #-----------------------
185
+ # TRANSPORT
186
+ #-----------------------
187
+
188
+ def playpause_wax
189
+ if Settings.at_bat
190
+ if @wax_state == "stopped"
191
+ play_wax
192
+ elsif @wax_state == "paused"
193
+ resume_wax
194
+ else
195
+ pause_wax
196
+ end
197
+
198
+ else
199
+ @wax_info = "no tracks"
200
+ send_wax_info("wax_info")
201
+ end
202
+ end
203
+
204
+ def next_wax
205
+ @wax_pipeline.stop
206
+ @wax_batter += 1
207
+ batter_up_wax
208
+ play_wax
209
+ end
210
+
211
+ def prev_wax
212
+ @wax_pipeline.stop
213
+ @wax_batter -= 1
214
+ batter_up_wax
215
+ play_wax
216
+ end
217
+
218
+ def pause_wax
219
+ @wax_pipeline.pause if @wax_pipeline
220
+ @wax_state = "paused"
221
+ end
222
+
223
+ def resume_wax
224
+ @wax_pipeline.play
225
+ @wax_state = "playing"
226
+ end
227
+
228
+ def stop_wax
229
+ @wax_pipeline.stop if @wax_pipeline
230
+ @wax_state = "stopped"
231
+ end
232
+
233
+ def toggle_wax_shuffle
234
+ Settings.shuffle = !Settings.shuffle
235
+ @wax_batter = @wax_lineup.index(Settings.at_bat)
236
+ end
237
+
238
+
239
+ #------------------------------------
240
+ # SEEK / SEND / SAVE
241
+ #------------------------------------
242
+
243
+ def seek_to_wax(position_in_ms)
244
+ if @wax_pipeline
245
+ @wax_pipeline.send_event(Gst::EventSeek.new(1.0,
246
+ Gst::Format::Type::TIME,
247
+ Gst::Seek::FLAG_FLUSH.to_i | Gst::Seek::FLAG_KEY_UNIT.to_i,
248
+ Gst::Seek::TYPE_SET, position_in_ms * 1000000, Gst::Seek::TYPE_NONE, -1))
249
+ end
250
+ end
251
+
252
+
253
+ def send_wax_info(info)
254
+ changed
255
+ notify_observers(info)
256
+ end
257
+
258
+ def xsave_wax_settings(settings)
259
+ settings[0] = @wax_roster
260
+ if @wax_shuffle == true
261
+ settings[1] = "shuffle on"
262
+ else
263
+ settings[1] = "shuffle off"
264
+ end
265
+ settings[2] = Settings.at_bat
266
+
267
+ File.open(@settings_file, "w"){|file|
268
+ settings.each{|entry| file.puts(entry)}
269
+ }
270
+ end
271
+
272
+
273
+ end #module Wax