rockbox_ffi 0.1.2 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20f77ffe0b56e69c1749a47007c36837cd2b1a17a1c9cf36fd76a3ce42411af8
4
- data.tar.gz: 620692c18aada83e7aa9b1d1e282f159fd804a9db0e2303677f825a04367fa81
3
+ metadata.gz: 78ae6470117f6b52f565285e04c0d65742da1296fc3b336ad6cf3c246266c5ee
4
+ data.tar.gz: 11281048c1996c24ff83ec3ceb1f5d66c8529527f2f0f91e3498836eb4fc39ad
5
5
  SHA512:
6
- metadata.gz: 2217930492eb1d545fa86c0ffad14b90ec9144712320d03c46e595aba2fdc31d28ed1d256040d165a78c967465b3645b146117a40d45ce57ac06269d2e6da079
7
- data.tar.gz: c3bed2b5f9238be0d072d46a82b5c6e697ea61dc041915389c8db7a139b3cb2d3805caff51e76571de703f3333653c5dbed0ca27f37ef7af0a4a9d9a2ca8e093
6
+ metadata.gz: dcc042306ae5c1ee6afe35a951a0f3ac2da9ad78b33605009b04f755ef23f253efb061f2ddb64afef34760b0a5642b1bc77ddbcfb95db8cc99b1d1198a41feb7
7
+ data.tar.gz: 9bf8d8eacd74b7cdef21bfe8bd80f810c58ae742e752f9a12709bd9df296f8a9b502ab014b5335fec522800f6c232dedf6796f168c5a8e07a3ddbea2f36cdb96
data/README.md CHANGED
@@ -95,7 +95,7 @@ ruby -Ilib examples/play.rb [path] # play a file through the output device
95
95
  ## Interactive console
96
96
 
97
97
  ```sh
98
- ./bin/console # or: rake console
98
+ bundle exec rake console # or: ./bin/console
99
99
  ```
100
100
 
101
101
  Drops into IRB with `RockboxFFI` loaded and `FIXTURE` pointing at a sample
@@ -108,6 +108,20 @@ p.set_queue([FIXTURE]); p.play
108
108
  p.status[:state] # => "playing"
109
109
  ```
110
110
 
111
+ The console bundles `irb` + `reline`, so **Tab autocompletion** and **syntax
112
+ highlighting** work out of the box — start typing `RockboxFFI::` and press
113
+ `Tab`. Both are on by default; toggle per-session with `irb --noautocomplete`,
114
+ or persist preferences in `~/.irbrc`:
115
+
116
+ ```ruby
117
+ IRB.conf[:USE_AUTOCOMPLETE] = true
118
+ IRB.conf[:USE_COLORIZE] = true
119
+ ```
120
+
121
+ > Run the console under a modern Ruby (3.x/4.x, e.g. Homebrew's
122
+ > `/opt/homebrew/opt/ruby`). macOS system Ruby 2.6 can't build `fiddle`'s
123
+ > native extension, so `bundle install` fails there.
124
+
111
125
  ## Test
112
126
 
113
127
  ```sh
@@ -32,6 +32,19 @@ module RockboxFFI
32
32
  MIX = 1
33
33
  end
34
34
 
35
+ # Where inserted tracks land in the queue (Player#insert / #import_m3u).
36
+ # INDEX (7) uses the explicit +index+ argument.
37
+ module InsertPosition
38
+ PREPEND = 0
39
+ INSERT = 1
40
+ INSERT_NEXT = 2
41
+ INSERT_LAST = 3
42
+ INSERT_SHUFFLED = 4
43
+ INSERT_LAST_SHUFFLED = 5
44
+ REPLACE = 6
45
+ INDEX = 7
46
+ end
47
+
35
48
  module ChannelConfig
36
49
  STEREO = 0
37
50
  MONO = 1
@@ -101,9 +101,12 @@ module RockboxFFI
101
101
  # ---- player -------------------------------------------------------
102
102
  extern "void* rb_player_new()"
103
103
  extern "void* rb_player_new_with_config(uint32_t, float, float, int32_t, float, bool, int32_t, uint32_t, uint32_t, uint32_t, uint32_t, int32_t)"
104
+ extern "void* rb_player_new_with_config_ex(uint32_t, float, float, int32_t, float, bool, int32_t, uint32_t, uint32_t, uint32_t, uint32_t, int32_t, void*, uint32_t)"
104
105
  extern "void rb_player_free(void*)"
105
106
  extern "void rb_player_set_queue_json(void*, void*)"
106
107
  extern "void rb_player_enqueue(void*, void*)"
108
+ extern "void rb_player_insert_json(void*, void*, int32_t, size_t)"
109
+ extern "void* rb_player_queue_json(void*)"
107
110
  extern "void rb_player_play(void*)"
108
111
  extern "void rb_player_pause(void*)"
109
112
  extern "void rb_player_toggle(void*)"
@@ -118,6 +121,20 @@ module RockboxFFI
118
121
  extern "float rb_player_volume(void*)"
119
122
  extern "uint32_t rb_player_sample_rate(void*)"
120
123
  extern "void* rb_player_status_json(void*)"
124
+
125
+ # ---- resume -------------------------------------------------------
126
+ extern "void* rb_player_resume(void*)"
127
+ extern "void rb_player_save_resume(void*)"
128
+ extern "void rb_player_clear_resume(void*)"
129
+ extern "void* rb_load_resume_json(void*)"
130
+
131
+ # ---- m3u / m3u8 playlists -----------------------------------------
132
+ extern "void* rb_player_import_m3u(void*, void*, int32_t, size_t)"
133
+ extern "void* rb_player_load_m3u(void*, void*)"
134
+ extern "int32_t rb_player_export_m3u(void*, void*)"
135
+ extern "void* rb_m3u_read_json(void*)"
136
+ extern "int32_t rb_m3u_write_json(void*, void*)"
137
+ extern "bool rb_is_url(void*)"
121
138
  end
122
139
 
123
140
  # true/false → 1/0 for the ABI's `bool` (declared as int above).
@@ -24,7 +24,9 @@ module RockboxFFI
24
24
  fade_out_duration_ms: 2000,
25
25
  fade_in_delay_ms: 0,
26
26
  fade_in_duration_ms: 2000,
27
- mix_mode: MixMode::CROSSFADE
27
+ mix_mode: MixMode::CROSSFADE,
28
+ resume_file: nil, # an .m3u8 to auto-persist queue + position to
29
+ resume_save_interval_ms: 0 # 0 => 5 s default
28
30
  }.freeze
29
31
 
30
32
  # Open a Player; if a block is given, close it automatically afterwards.
@@ -47,16 +49,19 @@ module RockboxFFI
47
49
  end
48
50
 
49
51
  # Create a player with configuration overrides (see DEFAULT_CONFIG keys).
50
- # sample_rate: 0 means the device default.
52
+ # sample_rate: 0 means the device default. Passing +resume_file:+ enables
53
+ # auto-persisting the queue + exact position to that .m3u8 file.
51
54
  def initialize(**opts)
52
55
  c = DEFAULT_CONFIG.merge(opts)
53
- ptr = Lib.rb_player_new_with_config(
56
+ resume_file = c[:resume_file].nil? ? nil : c[:resume_file].to_s
57
+ ptr = Lib.rb_player_new_with_config_ex(
54
58
  Integer(c[:sample_rate]), Float(c[:buffer_seconds]), Float(c[:volume]),
55
59
  Integer(c[:replaygain_mode]), Float(c[:replaygain_preamp_db]),
56
60
  RockboxFFI.b(c[:replaygain_prevent_clipping]), Integer(c[:crossfade_mode]),
57
61
  Integer(c[:fade_out_delay_ms]), Integer(c[:fade_out_duration_ms]),
58
62
  Integer(c[:fade_in_delay_ms]), Integer(c[:fade_in_duration_ms]),
59
- Integer(c[:mix_mode])
63
+ Integer(c[:mix_mode]), resume_file,
64
+ Integer(c[:resume_save_interval_ms])
60
65
  )
61
66
  init_ptr(ptr)
62
67
  end
@@ -88,6 +93,23 @@ module RockboxFFI
88
93
  Lib.rb_player_enqueue(@ptr, path.to_s)
89
94
  end
90
95
 
96
+ # Insert +paths+ (a path/URL or Array of them) into the queue at
97
+ # +position+ (see InsertPosition). +index+ is only used when position is
98
+ # InsertPosition::INDEX (7).
99
+ def insert(paths, position, index = 0)
100
+ Lib.rb_player_insert_json(
101
+ @ptr, JSON.generate(Array(paths).map(&:to_s)), Integer(position), Integer(index)
102
+ )
103
+ end
104
+
105
+ # The current queue as an Array of String paths/URLs.
106
+ def queue
107
+ s = RockboxFFI.take_string(Lib.rb_player_queue_json(@ptr))
108
+ return [] if s.nil?
109
+
110
+ JSON.parse(s)
111
+ end
112
+
91
113
  # -- transport --------------------------------------------------------
92
114
  def play
93
115
  Lib.rb_player_play(@ptr)
@@ -157,6 +179,51 @@ module RockboxFFI
157
179
  JSON.parse(s, symbolize_names: true)
158
180
  end
159
181
 
182
+ # -- resume -----------------------------------------------------------
183
+ # Restore the queue + exact position from the resume file (does NOT
184
+ # auto-play). Returns a Hash {tracks:, index:, elapsed_ms:} or nil.
185
+ def resume
186
+ s = RockboxFFI.take_string(Lib.rb_player_resume(@ptr))
187
+ return nil if s.nil?
188
+
189
+ JSON.parse(s, symbolize_names: true)
190
+ end
191
+
192
+ # Force-persist the current queue + position to the resume file now.
193
+ def save_resume
194
+ Lib.rb_player_save_resume(@ptr)
195
+ end
196
+
197
+ # Delete the resume file.
198
+ def clear_resume
199
+ Lib.rb_player_clear_resume(@ptr)
200
+ end
201
+
202
+ # -- m3u / m3u8 playlists ---------------------------------------------
203
+ # Import a playlist file into the queue at +position+ (see InsertPosition;
204
+ # +index+ only used for INDEX). Returns the imported paths as an Array.
205
+ def import_m3u(path, position, index = 0)
206
+ s = RockboxFFI.take_string(
207
+ Lib.rb_player_import_m3u(@ptr, path.to_s, Integer(position), Integer(index))
208
+ )
209
+ return [] if s.nil?
210
+
211
+ JSON.parse(s)
212
+ end
213
+
214
+ # Replace the queue with a playlist file. Returns the loaded paths as an Array.
215
+ def load_m3u(path)
216
+ s = RockboxFFI.take_string(Lib.rb_player_load_m3u(@ptr, path.to_s))
217
+ return [] if s.nil?
218
+
219
+ JSON.parse(s)
220
+ end
221
+
222
+ # Export the current queue to an .m3u8 (atomic). Returns true on success.
223
+ def export_m3u(path)
224
+ Lib.rb_player_export_m3u(@ptr, path.to_s).zero?
225
+ end
226
+
160
227
  private
161
228
 
162
229
  def init_ptr(ptr)
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module RockboxFFI
4
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
5
3
  end
data/lib/rockbox_ffi.rb CHANGED
@@ -12,9 +12,41 @@ require "rockbox_ffi/metadata"
12
12
  require "rockbox_ffi/dsp"
13
13
  require "rockbox_ffi/player"
14
14
 
15
+ require "json"
16
+
15
17
  module RockboxFFI
16
18
  # ABI major version of the loaded library (bumped on breaking changes).
17
19
  def self.abi_version
18
20
  Lib.rb_ffi_abi_version
19
21
  end
22
+
23
+ # Peek at a resume file without a Player. Returns a Hash
24
+ # {tracks:, index:, elapsed_ms:} or nil if the file is absent/invalid.
25
+ def self.load_resume(path)
26
+ s = take_string(Lib.rb_load_resume_json(path.to_s))
27
+ return nil if s.nil?
28
+
29
+ JSON.parse(s, symbolize_names: true)
30
+ end
31
+
32
+ # Parse a playlist (.m3u / .m3u8) into an Array of Hashes
33
+ # {path:, duration_ms:, title:}. Returns nil on failure.
34
+ def self.m3u_read(path)
35
+ s = take_string(Lib.rb_m3u_read_json(path.to_s))
36
+ return nil if s.nil?
37
+
38
+ JSON.parse(s, symbolize_names: true)
39
+ end
40
+
41
+ # Write +paths+ (a path/URL or Array of them) as an .m3u8. Returns true on
42
+ # success.
43
+ def self.m3u_write(path, paths)
44
+ Lib.rb_m3u_write_json(path.to_s, JSON.generate(Array(paths).map(&:to_s))).zero?
45
+ end
46
+
47
+ # Whether +s+ looks like an http(s):// URL.
48
+ def self.is_url?(s)
49
+ r = Lib.rb_is_url(s.to_s)
50
+ r == true || r == 1
51
+ end
20
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rockbox_ffi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tsiry Sandratraina
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-09 00:00:00.000000000 Z
11
+ date: 2026-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fiddle