This repository has been archived by the owner on Oct 10, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
iTunes.rb
231 lines (193 loc) · 5.35 KB
/
iTunes.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
require 'win32ole'
#
# Decorator for iTunes' OLE object. This class mainly exists to add
# some convenience methods to what the iTunes API provides. It also prevents
# the OLE stuff from leaking through.
#
# Being a decorator means that all methods that are not implemented here
# are sent to the iTunes OLE object.
#
class ITunes
class PlayerState
STOPPED = 0 # Player is stopped.
PLAYING = 1 # Player is playing.
FASTFORWARD = 2 # Player is fast forwarding.
REWIND = 3 # Player is rewinding.
end
def initialize
@itunes = WIN32OLE.new('iTunes.Application') # the OLE object itself
# Delegate to the OLE object
# from http://blog.jayfields.com/2008/02/ruby-replace-methodmissing-with-dynamic.html
@itunes.public_methods(false).each do |meth|
(class << self; self; end).class_eval do
define_method meth do |*args|
@itunes.send meth, *args
end
end
end
@listeners = [] # event handler registry
setup_events
end
#
# message loop
#
# This method will block until stop_message_loop is called
#
def start_message_loop
@stop_message_loop = false
while !@stop_message_loop do
WIN32OLE_EVENT.message_loop
end
end
def stop_message_loop
@stop_message_loop = true
end
def register(listener)
@listeners << listener
end
def unregister(listener)
@listeners.delete(listener)
end
#
# Logarithmic fade out
#
def fade_out(fade_time = 3, vol_from = @itunes.SoundVolume)
vol_from.downto 1 do |i|
@itunes.SoundVolume = log_norm(i, vol_from)
sleep Float(fade_time) / vol_from
end
@itunes.SoundVolume = 0
end
#
# Logarithmic fade in
#
def fade_in(fade_time = 3, vol_til = 100)
1.upto vol_til do |i|
@itunes.SoundVolume = log_norm(i, vol_til)
sleep Float(fade_time) / vol_til
end
end
def rate_and_fade(r = 0)
@itunes.CurrentTrack.rating = r
crossfade_to_next
end
def toggle_fast_forward
if ITunes::PlayerState::FASTFORWARD == @itunes.PlayerState || ITunes::PlayerState::REWIND == @itunes.PlayerState
@itunes.Resume
else
@itunes.FastForward
end
end
def toggle_rewind
if ITunes::PlayerState::FASTFORWARD == @itunes.PlayerState || ITunes::PlayerState::REWIND == @itunes.PlayerState
@itunes.Resume
else
@itunes.Rewind
end
end
def vol_up(v = 5)
@itunes.SoundVolume = @itunes.SoundVolume + v
end
def vol_down(v = 5)
@itunes.SoundVolume = @itunes.SoundVolume - v
end
def crossfade_to_next
orig_volume = @itunes.SoundVolume
fade_out(1)
@itunes.NextTrack
@itunes.Play if !@itunes.CurrentTrack
# no tracks left?
if !@itunes.CurrentTrack
raise "Playlist empty"
end
if @itunes.CurrentTrack.duration > 120
@itunes.PlayerPosition = 60
else
@itunes.PlayerPosition = @itunes.CurrentTrack.duration / 3
end
fade_in(1, orig_volume)
end
def crossfade_to_previous
orig_volume = @itunes.SoundVolume
fade_out(1)
@itunes.PreviousTrack
@itunes.Play if !@itunes.CurrentTrack
# no tracks left?
if !@itunes.CurrentTrack
raise "Playlist empty"
end
if @itunes.CurrentTrack.duration > 120
@itunes.PlayerPosition = 60
else
@itunes.PlayerPosition = @itunes.CurrentTrack.duration / 3
end
fade_in(1, orig_volume)
end
def toggle_visualizer
@itunes.VisualsEnabled = !@itunes.VisualsEnabled
end
private
#
# return log10(val), normed to norm
#
def log_norm(val, norm)
retval = norm * Math.log10(Float(val) / norm * 10)
retval < 0 ? 0 : retval
end
def setup_events
ev = WIN32OLE_EVENT.new(@itunes, '_IiTunesEvents')
ev.on_event{|*args| @listeners.each{l| l.fallback(*args)}}
ev.on_event("OnDatabaseChangedEvent"){|deleted_ids, changed_ids|
# resolve changed ids to objects
changed = []
changed_ids.each{|record|
changed << @itunes.GetITObjectByID(record[0], record[1], record[2], record[3])
}
@listeners.each{|l|
l.on_database_changed(deleted_ids, changed) if l.respond_to?(:on_database_changed)
}
}
ev.on_event("OnPlayerPlayEvent"){|track|
@listeners.each{|l|
l.on_play(track) if l.respond_to?(:on_play)
}
}
ev.on_event("OnPlayerStopEvent"){|track|
@listeners.each{|l|
l.on_stop(track) if l.respond_to?(:on_stop)
}
}
ev.on_event("OnPlayerPlayingTrackChangedEvent"){|track|
@listeners.each{|l|
l.on_playing_track_changed(track) if l.respond_to?(:on_playing_track_changed)
}
}
ev.on_event("OnCOMCallsDisabledEvent"){|reason|
@listeners.each{|l|
l.on_busy(reason) if l.respond_to?(:on_busy)
}
}
ev.on_event("OnCOMCallsEnabledEvent"){
@listeners.each{|l|
l.on_ready if l.respond_to?(:on_ready)
}
}
ev.on_event("OnAboutToPromptUserToQuitEvent"){
@listeners.each{|l|
l.on_prompting_for_quit if l.respond_to?(:on_prompting_for_quit)
}
stop_message_loop
}
ev.on_event("OnSoundVolumeChangedEvent"){|newVolume|
@listeners.each{|l|
l.on_sound_volume_changed(newVolume) if l.respond_to?(:on_sound_volume_changed)
}
}
ev.on_event("OnQuittingEvent"){
@listeners.each{|l|
l.on_quitting if l.respond_to?(:on_quitting)
}
stop_message_loop
}
end
end