vorbis 0.1.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 +7 -0
- data/CHANGELOG.md +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +118 -0
- data/Rakefile +8 -0
- data/lib/vorbis/block.rb +40 -0
- data/lib/vorbis/clearable.rb +32 -0
- data/lib/vorbis/comment.rb +36 -0
- data/lib/vorbis/dsp_state.rb +40 -0
- data/lib/vorbis/encoder.rb +120 -0
- data/lib/vorbis/info.rb +38 -0
- data/lib/vorbis/native.rb +142 -0
- data/lib/vorbis/native_enc.rb +15 -0
- data/lib/vorbis/version.rb +5 -0
- data/lib/vorbis.rb +19 -0
- metadata +85 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 51ab935f6596d5bcc9629adcf457ad8920827419c2c777879afbc2704988114f
|
|
4
|
+
data.tar.gz: 24969f2e03d1ae9cbd7a72aaaf614c100ad87b69d3a283977794ca6defb7b994
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6ff9ac4e6a9eea530ba7182e365a3dc3bf2a638653cef18c132cd2895bbad7fd325ba59b7497f9617c6893eed1a3f1f93d9e2e3b68cb094da0decf689be06c62
|
|
7
|
+
data.tar.gz: 22279394f5b9fc644b0342537a29ff7dc12e65f00674b6538b7b010e396704d1684e375e6c2ffe568fe3d1e5b51bfabd71a3d0aa26666902f93073f5b7525868
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# vorbis-ruby
|
|
2
|
+
|
|
3
|
+
Ruby FFI bindings for libvorbis and libvorbisenc. Provides Vorbis audio codec encoding functionality.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### System Requirements
|
|
8
|
+
|
|
9
|
+
libvorbis and libvorbisenc must be installed on your system.
|
|
10
|
+
|
|
11
|
+
macOS:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
brew install libvorbis
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Debian / Ubuntu:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
sudo apt-get install libvorbis-dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Fedora / RHEL:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
sudo dnf install libvorbis-devel
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Gem Installation
|
|
30
|
+
|
|
31
|
+
Add to your Gemfile:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
gem "vorbis-ruby"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or install directly:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
gem install vorbis-ruby
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
require "vorbis"
|
|
47
|
+
|
|
48
|
+
File.open("output.ogg", "wb") do |f|
|
|
49
|
+
encoder = Vorbis::Encoder.new(
|
|
50
|
+
channels: 2,
|
|
51
|
+
rate: 44100,
|
|
52
|
+
quality: 0.4,
|
|
53
|
+
comments: { "ARTIST" => "Test", "TITLE" => "Hello" }
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
encoder.write_headers { |data| f.write(data) }
|
|
57
|
+
|
|
58
|
+
# PCM data as per-channel float arrays (-1.0 to 1.0)
|
|
59
|
+
samples = [Array.new(1024, 0.0), Array.new(1024, 0.0)]
|
|
60
|
+
encoder.encode(samples) { |data| f.write(data) }
|
|
61
|
+
|
|
62
|
+
encoder.finish { |data| f.write(data) }
|
|
63
|
+
encoder.close
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## API Reference
|
|
68
|
+
|
|
69
|
+
### `Vorbis::Encoder`
|
|
70
|
+
|
|
71
|
+
High-level encoder that manages all Vorbis resources internally.
|
|
72
|
+
|
|
73
|
+
- `initialize(channels:, rate:, quality: 0.4, comments: {})` — Create encoder with VBR quality (-0.1 to 1.0)
|
|
74
|
+
- `write_headers { |data| }` — Yield OGG header pages
|
|
75
|
+
- `encode(samples) { |data| }` — Encode PCM samples (array of per-channel float arrays) and yield OGG pages
|
|
76
|
+
- `finish { |data| }` — Signal end-of-stream and yield remaining OGG pages
|
|
77
|
+
- `close` — Release all resources
|
|
78
|
+
|
|
79
|
+
### `Vorbis::Info`
|
|
80
|
+
|
|
81
|
+
Low-level wrapper for `vorbis_info`.
|
|
82
|
+
|
|
83
|
+
- `encode_init_vbr(channels:, rate:, quality:)` — Set up VBR encoding
|
|
84
|
+
- `encode_init(channels:, rate:, nominal_bitrate:, max_bitrate: -1, min_bitrate: -1)` — Set up CBR/ABR encoding
|
|
85
|
+
- `channels`, `rate`, `bitrate_nominal` — Accessors
|
|
86
|
+
- `clear` — Release resources
|
|
87
|
+
|
|
88
|
+
### `Vorbis::Comment`
|
|
89
|
+
|
|
90
|
+
Low-level wrapper for `vorbis_comment`.
|
|
91
|
+
|
|
92
|
+
- `add_tag(tag, value)` — Add a comment tag
|
|
93
|
+
- `query(tag, index = 0)` — Query a tag value
|
|
94
|
+
- `query_count(tag)` — Count tags with a given name
|
|
95
|
+
- `vendor` — Get the vendor string
|
|
96
|
+
- `clear` — Release resources
|
|
97
|
+
|
|
98
|
+
### `Vorbis::DspState`
|
|
99
|
+
|
|
100
|
+
Low-level wrapper for `vorbis_dsp_state`.
|
|
101
|
+
|
|
102
|
+
- `headerout(comment)` — Generate 3 header packets
|
|
103
|
+
- `analysis_buffer(samples)` — Get per-channel write buffers
|
|
104
|
+
- `wrote(samples)` — Notify samples written (0 for EOS)
|
|
105
|
+
- `clear` — Release resources
|
|
106
|
+
|
|
107
|
+
### `Vorbis::Block`
|
|
108
|
+
|
|
109
|
+
Low-level wrapper for `vorbis_block`.
|
|
110
|
+
|
|
111
|
+
- `blockout` — Extract a block from DSP state
|
|
112
|
+
- `analysis_and_addblock` — Analyze block and add to bitrate management
|
|
113
|
+
- `flush_packet` — Flush a packet from bitrate management
|
|
114
|
+
- `clear` — Release resources
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/lib/vorbis/block.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
class Block
|
|
5
|
+
include Clearable
|
|
6
|
+
|
|
7
|
+
attr_reader :native
|
|
8
|
+
|
|
9
|
+
def initialize(dsp_state)
|
|
10
|
+
@dsp_state = dsp_state
|
|
11
|
+
@ptr = FFI::MemoryPointer.new(Native::VorbisBlock.size)
|
|
12
|
+
@native = Native::VorbisBlock.new(@ptr)
|
|
13
|
+
result = Native.vorbis_block_init(dsp_state.native, @ptr)
|
|
14
|
+
raise InitError, "vorbis_block_init failed with status #{result}" unless result == 0
|
|
15
|
+
|
|
16
|
+
@pkt_ptr = FFI::MemoryPointer.new(Ogg::Native::OggPacket.size)
|
|
17
|
+
setup_clearable(Native.method(:vorbis_block_clear))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def blockout
|
|
21
|
+
result = Native.vorbis_analysis_blockout(@dsp_state.native, @ptr)
|
|
22
|
+
result == 1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def analysis_and_addblock
|
|
26
|
+
result = Native.vorbis_analysis(@ptr, nil)
|
|
27
|
+
raise EncoderError, "vorbis_analysis failed with status #{result}" unless result == 0
|
|
28
|
+
|
|
29
|
+
result = Native.vorbis_bitrate_addblock(@ptr)
|
|
30
|
+
raise EncoderError, "vorbis_bitrate_addblock failed with status #{result}" unless result == 0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def flush_packet
|
|
34
|
+
result = Native.vorbis_bitrate_flushpacket(@dsp_state.native, @pkt_ptr)
|
|
35
|
+
return nil unless result == 1
|
|
36
|
+
|
|
37
|
+
Native.packet_from_native(@pkt_ptr)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
module Clearable
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def finalizer_for(ptr, clear_state, clear_fn)
|
|
11
|
+
proc { clear_fn.call(ptr) unless clear_state[0] }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def setup_clearable(clear_fn)
|
|
16
|
+
@clear_state = [false]
|
|
17
|
+
@_native_clear = clear_fn
|
|
18
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer_for(@ptr, @clear_state, clear_fn))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def clear
|
|
22
|
+
return if cleared?
|
|
23
|
+
|
|
24
|
+
@_native_clear.call(@ptr)
|
|
25
|
+
@clear_state[0] = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def cleared?
|
|
29
|
+
@clear_state[0]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
class Comment
|
|
5
|
+
include Clearable
|
|
6
|
+
|
|
7
|
+
attr_reader :native
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@ptr = FFI::MemoryPointer.new(Native::VorbisComment.size)
|
|
11
|
+
@native = Native::VorbisComment.new(@ptr)
|
|
12
|
+
Native.vorbis_comment_init(@ptr)
|
|
13
|
+
setup_clearable(Native.method(:vorbis_comment_clear))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_tag(tag, value)
|
|
17
|
+
Native.vorbis_comment_add_tag(@ptr, tag, value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def query(tag, index = 0)
|
|
21
|
+
result = Native.vorbis_comment_query(@ptr, tag, index)
|
|
22
|
+
result.null? ? nil : result.read_string
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def query_count(tag)
|
|
26
|
+
Native.vorbis_comment_query_count(@ptr, tag)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def vendor
|
|
30
|
+
vendor_ptr = @native[:vendor]
|
|
31
|
+
return nil if vendor_ptr.null?
|
|
32
|
+
|
|
33
|
+
vendor_ptr.read_string
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
class DspState
|
|
5
|
+
include Clearable
|
|
6
|
+
|
|
7
|
+
attr_reader :native
|
|
8
|
+
|
|
9
|
+
def initialize(info)
|
|
10
|
+
@info = info
|
|
11
|
+
@ptr = FFI::MemoryPointer.new(Native::VorbisDspState.size)
|
|
12
|
+
@native = Native::VorbisDspState.new(@ptr)
|
|
13
|
+
result = Native.vorbis_analysis_init(@ptr, info.native)
|
|
14
|
+
raise InitError, "vorbis_analysis_init failed with status #{result}" unless result == 0
|
|
15
|
+
|
|
16
|
+
setup_clearable(Native.method(:vorbis_dsp_clear))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def headerout(comment)
|
|
20
|
+
op_ptr = FFI::MemoryPointer.new(Ogg::Native::OggPacket.size)
|
|
21
|
+
op_comm_ptr = FFI::MemoryPointer.new(Ogg::Native::OggPacket.size)
|
|
22
|
+
op_code_ptr = FFI::MemoryPointer.new(Ogg::Native::OggPacket.size)
|
|
23
|
+
|
|
24
|
+
result = Native.vorbis_analysis_headerout(@ptr, comment.native, op_ptr, op_comm_ptr, op_code_ptr)
|
|
25
|
+
raise EncoderError, "vorbis_analysis_headerout failed with status #{result}" unless result == 0
|
|
26
|
+
|
|
27
|
+
[op_ptr, op_comm_ptr, op_code_ptr].map { |pkt_ptr| Native.packet_from_native(pkt_ptr) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def analysis_buffer(samples)
|
|
31
|
+
buffer_ptr = Native.vorbis_analysis_buffer(@ptr, samples)
|
|
32
|
+
buffer_ptr.read_array_of_pointer(@info.channels)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def wrote(samples)
|
|
36
|
+
result = Native.vorbis_analysis_wrote(@ptr, samples)
|
|
37
|
+
raise EncoderError, "vorbis_analysis_wrote failed with status #{result}" unless result == 0
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
class Encoder
|
|
5
|
+
def initialize(channels:, rate:, quality: 0.4, comments: {})
|
|
6
|
+
@closed = false
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
@info = Vorbis::Info.new
|
|
10
|
+
@info.encode_init_vbr(channels: channels, rate: rate, quality: quality)
|
|
11
|
+
|
|
12
|
+
@comment = Vorbis::Comment.new
|
|
13
|
+
comments.each { |tag, value| @comment.add_tag(tag.to_s, value.to_s) }
|
|
14
|
+
|
|
15
|
+
@dsp_state = Vorbis::DspState.new(@info)
|
|
16
|
+
@block = Vorbis::Block.new(@dsp_state)
|
|
17
|
+
@stream = Ogg::StreamState.new(rand(0xFFFFFF))
|
|
18
|
+
rescue StandardError
|
|
19
|
+
close
|
|
20
|
+
raise
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def write_headers
|
|
25
|
+
ensure_open!
|
|
26
|
+
|
|
27
|
+
packets = @dsp_state.headerout(@comment)
|
|
28
|
+
packets.each { |pkt| @stream.packetin(pkt) }
|
|
29
|
+
|
|
30
|
+
while (page = @stream.flush)
|
|
31
|
+
yield page.to_s
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def encode(samples)
|
|
36
|
+
ensure_open!
|
|
37
|
+
validate_samples!(samples)
|
|
38
|
+
|
|
39
|
+
num_samples = samples.first.size
|
|
40
|
+
return if num_samples.zero?
|
|
41
|
+
|
|
42
|
+
buffer = @dsp_state.analysis_buffer(num_samples)
|
|
43
|
+
|
|
44
|
+
samples.each_with_index do |channel_data, ch|
|
|
45
|
+
buffer[ch].write_array_of_float(channel_data)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
@dsp_state.wrote(num_samples)
|
|
49
|
+
flush_blocks { |data| yield data }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def finish
|
|
53
|
+
ensure_open!
|
|
54
|
+
|
|
55
|
+
@dsp_state.wrote(0)
|
|
56
|
+
flush_blocks { |data| yield data }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def close
|
|
60
|
+
return if @closed
|
|
61
|
+
|
|
62
|
+
@block&.clear
|
|
63
|
+
@dsp_state&.clear
|
|
64
|
+
@comment&.clear
|
|
65
|
+
@info&.clear
|
|
66
|
+
@stream&.clear
|
|
67
|
+
ensure
|
|
68
|
+
@closed = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def closed?
|
|
72
|
+
@closed
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def ensure_open!
|
|
78
|
+
raise EncoderError, "encoder is closed" if @closed
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def validate_samples!(samples)
|
|
82
|
+
unless samples.is_a?(Array)
|
|
83
|
+
raise ArgumentError, "samples must be an array of per-channel sample arrays"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
expected_channels = @info.channels
|
|
87
|
+
unless samples.size == expected_channels
|
|
88
|
+
raise ArgumentError, "expected #{expected_channels} channels, got #{samples.size}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
num_samples = nil
|
|
92
|
+
|
|
93
|
+
samples.each_with_index do |channel_data, channel_index|
|
|
94
|
+
unless channel_data.is_a?(Array)
|
|
95
|
+
raise ArgumentError, "channel #{channel_index} must be an array of numeric samples"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
num_samples ||= channel_data.size
|
|
99
|
+
unless channel_data.size == num_samples
|
|
100
|
+
raise ArgumentError, "all channels must have the same number of samples"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def flush_blocks
|
|
106
|
+
while @block.blockout
|
|
107
|
+
@block.analysis_and_addblock
|
|
108
|
+
|
|
109
|
+
while (packet = @block.flush_packet)
|
|
110
|
+
@stream.packetin(packet)
|
|
111
|
+
|
|
112
|
+
while (page = @stream.pageout)
|
|
113
|
+
yield page.to_s
|
|
114
|
+
break if page.eos?
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
data/lib/vorbis/info.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
class Info
|
|
5
|
+
include Clearable
|
|
6
|
+
|
|
7
|
+
attr_reader :native
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@ptr = FFI::MemoryPointer.new(Native::VorbisInfo.size)
|
|
11
|
+
@native = Native::VorbisInfo.new(@ptr)
|
|
12
|
+
Native.vorbis_info_init(@ptr)
|
|
13
|
+
setup_clearable(Native.method(:vorbis_info_clear))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def encode_init_vbr(channels:, rate:, quality: 0.4)
|
|
17
|
+
result = NativeEnc.vorbis_encode_init_vbr(@ptr, channels, rate, quality)
|
|
18
|
+
raise InitError, "vorbis_encode_init_vbr failed with status #{result}" unless result == 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def encode_init(channels:, rate:, max_bitrate: -1, nominal_bitrate:, min_bitrate: -1)
|
|
22
|
+
result = NativeEnc.vorbis_encode_init(@ptr, channels, rate, max_bitrate, nominal_bitrate, min_bitrate)
|
|
23
|
+
raise InitError, "vorbis_encode_init failed with status #{result}" unless result == 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def channels
|
|
27
|
+
@native[:channels]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def rate
|
|
31
|
+
@native[:rate]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def bitrate_nominal
|
|
35
|
+
@native[:bitrate_nominal]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
module Native
|
|
5
|
+
extend FFI::Library
|
|
6
|
+
ffi_lib ["libvorbis.so.0", "libvorbis.0.dylib", "libvorbis", "vorbis"]
|
|
7
|
+
|
|
8
|
+
# --- Structs ---
|
|
9
|
+
|
|
10
|
+
class VorbisInfo < FFI::Struct
|
|
11
|
+
layout :version, :int,
|
|
12
|
+
:channels, :int,
|
|
13
|
+
:rate, :long,
|
|
14
|
+
:bitrate_upper, :long,
|
|
15
|
+
:bitrate_nominal, :long,
|
|
16
|
+
:bitrate_lower, :long,
|
|
17
|
+
:bitrate_window, :long,
|
|
18
|
+
:codec_setup, :pointer
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class VorbisComment < FFI::Struct
|
|
22
|
+
layout :user_comments, :pointer,
|
|
23
|
+
:comment_lengths, :pointer,
|
|
24
|
+
:comments, :int,
|
|
25
|
+
:vendor, :pointer
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class VorbisDspState < FFI::Struct
|
|
29
|
+
layout :analysisp, :int,
|
|
30
|
+
:vi, :pointer,
|
|
31
|
+
:pcm, :pointer,
|
|
32
|
+
:pcmret, :pointer,
|
|
33
|
+
:pcm_storage, :int,
|
|
34
|
+
:pcm_current, :int,
|
|
35
|
+
:pcm_returned, :int,
|
|
36
|
+
:preextrapolate, :int,
|
|
37
|
+
:eofflag, :int,
|
|
38
|
+
:lW, :long,
|
|
39
|
+
:W, :long,
|
|
40
|
+
:nW, :long,
|
|
41
|
+
:centerW, :long,
|
|
42
|
+
:granulepos, :int64,
|
|
43
|
+
:sequence, :int64,
|
|
44
|
+
:glue_bits, :int64,
|
|
45
|
+
:time_bits, :int64,
|
|
46
|
+
:floor_bits, :int64,
|
|
47
|
+
:res_bits, :int64,
|
|
48
|
+
:backend_state, :pointer
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class VorbisBlock < FFI::Struct
|
|
52
|
+
layout :pcm, :pointer,
|
|
53
|
+
# oggpack_buffer opb (inlined)
|
|
54
|
+
:opb_endbyte, :long,
|
|
55
|
+
:opb_endbit, :int,
|
|
56
|
+
:opb_buffer, :pointer,
|
|
57
|
+
:opb_ptr, :pointer,
|
|
58
|
+
:opb_storage, :long,
|
|
59
|
+
# remaining fields
|
|
60
|
+
:lW, :long,
|
|
61
|
+
:W, :long,
|
|
62
|
+
:nW, :long,
|
|
63
|
+
:pcmend, :int,
|
|
64
|
+
:mode, :int,
|
|
65
|
+
:eofflag, :int,
|
|
66
|
+
:granulepos, :int64,
|
|
67
|
+
:sequence, :int64,
|
|
68
|
+
:vd, :pointer,
|
|
69
|
+
:localstore, :pointer,
|
|
70
|
+
:localtop, :long,
|
|
71
|
+
:localalloc, :long,
|
|
72
|
+
:totaluse, :long,
|
|
73
|
+
:reap, :pointer,
|
|
74
|
+
:glue_bits, :long,
|
|
75
|
+
:time_bits, :long,
|
|
76
|
+
:floor_bits, :long,
|
|
77
|
+
:res_bits, :long,
|
|
78
|
+
:internal, :pointer
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# --- Info API ---
|
|
82
|
+
|
|
83
|
+
attach_function :vorbis_info_init, [:pointer], :void
|
|
84
|
+
attach_function :vorbis_info_clear, [:pointer], :void
|
|
85
|
+
attach_function :vorbis_info_blocksize, [:pointer, :int], :int
|
|
86
|
+
|
|
87
|
+
# --- Comment API ---
|
|
88
|
+
|
|
89
|
+
attach_function :vorbis_comment_init, [:pointer], :void
|
|
90
|
+
attach_function :vorbis_comment_clear, [:pointer], :void
|
|
91
|
+
attach_function :vorbis_comment_add, [:pointer, :string], :void
|
|
92
|
+
attach_function :vorbis_comment_add_tag, [:pointer, :string, :string], :void
|
|
93
|
+
attach_function :vorbis_comment_query, [:pointer, :string, :int], :pointer
|
|
94
|
+
attach_function :vorbis_comment_query_count, [:pointer, :string], :int
|
|
95
|
+
|
|
96
|
+
# --- Synthesis/Analysis API ---
|
|
97
|
+
|
|
98
|
+
attach_function :vorbis_analysis_init, [:pointer, :pointer], :int
|
|
99
|
+
attach_function :vorbis_analysis_headerout, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
|
100
|
+
attach_function :vorbis_analysis_buffer, [:pointer, :int], :pointer
|
|
101
|
+
attach_function :vorbis_analysis_wrote, [:pointer, :int], :int
|
|
102
|
+
attach_function :vorbis_analysis_blockout, [:pointer, :pointer], :int
|
|
103
|
+
attach_function :vorbis_analysis, [:pointer, :pointer], :int
|
|
104
|
+
attach_function :vorbis_bitrate_addblock, [:pointer], :int
|
|
105
|
+
attach_function :vorbis_bitrate_flushpacket, [:pointer, :pointer], :int
|
|
106
|
+
|
|
107
|
+
# --- Synthesis API ---
|
|
108
|
+
|
|
109
|
+
attach_function :vorbis_synthesis_headerin, [:pointer, :pointer, :pointer], :int
|
|
110
|
+
attach_function :vorbis_synthesis_init, [:pointer, :pointer], :int
|
|
111
|
+
attach_function :vorbis_synthesis_restart, [:pointer], :int
|
|
112
|
+
attach_function :vorbis_synthesis, [:pointer, :pointer], :int
|
|
113
|
+
attach_function :vorbis_synthesis_trackonly, [:pointer, :pointer], :int
|
|
114
|
+
attach_function :vorbis_synthesis_blockin, [:pointer, :pointer], :int
|
|
115
|
+
attach_function :vorbis_synthesis_pcmout, [:pointer, :pointer], :int
|
|
116
|
+
attach_function :vorbis_synthesis_lapout, [:pointer, :pointer], :int
|
|
117
|
+
attach_function :vorbis_synthesis_read, [:pointer, :int], :int
|
|
118
|
+
|
|
119
|
+
# --- Block API ---
|
|
120
|
+
|
|
121
|
+
attach_function :vorbis_block_init, [:pointer, :pointer], :int
|
|
122
|
+
attach_function :vorbis_block_clear, [:pointer], :int
|
|
123
|
+
|
|
124
|
+
# --- DSP Cleanup ---
|
|
125
|
+
|
|
126
|
+
attach_function :vorbis_dsp_clear, [:pointer], :void
|
|
127
|
+
|
|
128
|
+
# --- Helpers ---
|
|
129
|
+
|
|
130
|
+
def self.packet_from_native(pkt_ptr)
|
|
131
|
+
native_pkt = Ogg::Native::OggPacket.new(pkt_ptr)
|
|
132
|
+
data = native_pkt[:packet].read_bytes(native_pkt[:bytes])
|
|
133
|
+
Ogg::Packet.new(
|
|
134
|
+
data: data,
|
|
135
|
+
bos: native_pkt[:b_o_s] != 0,
|
|
136
|
+
eos: native_pkt[:e_o_s] != 0,
|
|
137
|
+
granulepos: native_pkt[:granulepos],
|
|
138
|
+
packetno: native_pkt[:packetno]
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vorbis
|
|
4
|
+
module NativeEnc
|
|
5
|
+
extend FFI::Library
|
|
6
|
+
ffi_lib ["libvorbisenc.so.2", "libvorbisenc.2.dylib", "libvorbisenc", "vorbisenc"]
|
|
7
|
+
|
|
8
|
+
attach_function :vorbis_encode_init, [:pointer, :long, :long, :long, :long, :long], :int
|
|
9
|
+
attach_function :vorbis_encode_init_vbr, [:pointer, :long, :long, :float], :int
|
|
10
|
+
attach_function :vorbis_encode_setup_init, [:pointer], :int
|
|
11
|
+
attach_function :vorbis_encode_setup_managed, [:pointer, :long, :long, :long, :long, :long], :int
|
|
12
|
+
attach_function :vorbis_encode_setup_vbr, [:pointer, :long, :long, :float], :int
|
|
13
|
+
attach_function :vorbis_encode_ctl, [:pointer, :int, :pointer], :int
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/vorbis.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
require "ogg"
|
|
5
|
+
require_relative "vorbis/version"
|
|
6
|
+
require_relative "vorbis/native"
|
|
7
|
+
require_relative "vorbis/native_enc"
|
|
8
|
+
require_relative "vorbis/clearable"
|
|
9
|
+
require_relative "vorbis/info"
|
|
10
|
+
require_relative "vorbis/comment"
|
|
11
|
+
require_relative "vorbis/dsp_state"
|
|
12
|
+
require_relative "vorbis/block"
|
|
13
|
+
require_relative "vorbis/encoder"
|
|
14
|
+
|
|
15
|
+
module Vorbis
|
|
16
|
+
class Error < StandardError; end
|
|
17
|
+
class EncoderError < Error; end
|
|
18
|
+
class InitError < Error; end
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: vorbis
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yudai Takada
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ffi
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.15'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.15'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ogg-ruby
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.1'
|
|
40
|
+
description: A Ruby FFI binding library for libvorbis and libvorbisenc, providing
|
|
41
|
+
Vorbis audio codec encoding functionality.
|
|
42
|
+
email:
|
|
43
|
+
- t.yudai92@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- CHANGELOG.md
|
|
49
|
+
- LICENSE.txt
|
|
50
|
+
- README.md
|
|
51
|
+
- Rakefile
|
|
52
|
+
- lib/vorbis.rb
|
|
53
|
+
- lib/vorbis/block.rb
|
|
54
|
+
- lib/vorbis/clearable.rb
|
|
55
|
+
- lib/vorbis/comment.rb
|
|
56
|
+
- lib/vorbis/dsp_state.rb
|
|
57
|
+
- lib/vorbis/encoder.rb
|
|
58
|
+
- lib/vorbis/info.rb
|
|
59
|
+
- lib/vorbis/native.rb
|
|
60
|
+
- lib/vorbis/native_enc.rb
|
|
61
|
+
- lib/vorbis/version.rb
|
|
62
|
+
homepage: https://github.com/ydah/vorbis-ruby
|
|
63
|
+
licenses:
|
|
64
|
+
- MIT
|
|
65
|
+
metadata:
|
|
66
|
+
homepage_uri: https://github.com/ydah/vorbis-ruby
|
|
67
|
+
source_code_uri: https://github.com/ydah/vorbis-ruby
|
|
68
|
+
rdoc_options: []
|
|
69
|
+
require_paths:
|
|
70
|
+
- lib
|
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 3.1.0
|
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '0'
|
|
81
|
+
requirements: []
|
|
82
|
+
rubygems_version: 4.0.6
|
|
83
|
+
specification_version: 4
|
|
84
|
+
summary: Ruby FFI bindings for libvorbis and libvorbisenc
|
|
85
|
+
test_files: []
|