gswax 0.0.1

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 (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