sidtool 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ee52c07a7f399012cf4f38f0be369570784e54086f7bc0c1bb52664b8cbe01b6
4
+ data.tar.gz: e5652d2922c9a8b52df256ac1a45074849c81ff256bfdf672bdbdef3c16113ff
5
+ SHA512:
6
+ metadata.gz: 8695db8b9e68e83c550bf4eba49eafbe4f4eef09c2c409eea568365a708d8d7446da573b854c126c2b88fe84d6711c5448ad8b1441272d47c205ef467ec3480b
7
+ data.tar.gz: 0f828663808f36e37f20866699fe0abd7c015a87d803c19a994a9589318ad5ba34a91ba0cdbe821d0cae058f7639f5e96f650a8fdbad718f932ec0a1eb7f1f2f
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /.idea
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in sidtool.gemspec
6
+ gemspec
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sidtool (0.0.1)
5
+ mos6510 (~> 0.1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.2)
11
+ diff-lcs (1.3)
12
+ libv8 (7.3.492.27.1-x86_64-darwin-18)
13
+ method_source (0.9.2)
14
+ mini_racer (0.2.6)
15
+ libv8 (>= 6.9.411)
16
+ mos6510 (0.1.0)
17
+ mini_racer (~> 0.2.6)
18
+ pry (0.12.2)
19
+ coderay (~> 1.1.0)
20
+ method_source (~> 0.9.0)
21
+ rake (10.5.0)
22
+ rspec (3.8.0)
23
+ rspec-core (~> 3.8.0)
24
+ rspec-expectations (~> 3.8.0)
25
+ rspec-mocks (~> 3.8.0)
26
+ rspec-core (3.8.2)
27
+ rspec-support (~> 3.8.0)
28
+ rspec-expectations (3.8.4)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.8.0)
31
+ rspec-mocks (3.8.1)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.8.0)
34
+ rspec-support (3.8.2)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ bundler (~> 2.0)
41
+ pry (~> 0.12.2)
42
+ rake (~> 10.0)
43
+ rspec (~> 3.0)
44
+ sidtool!
45
+
46
+ BUNDLED WITH
47
+ 2.0.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Ole Friis Østergaard
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.
@@ -0,0 +1,101 @@
1
+ # Sidtool
2
+
3
+ Convert Commodore 64 SID music in the form of `.sid` files into other formats! This is still VERY
4
+ much work in progress... the code is ugly and will probably create horrible results for you :-)
5
+
6
+ Basically, it's a massive hack made for fun and no profit.
7
+
8
+ The vision, though, is to extract the actual information from `.sid` files, which are files storing
9
+ music for the Commodore 64. This sounds like an easy task.
10
+
11
+ `.sid` files contain actual Commodore 64 machine code that writes to registers corresponding to the
12
+ Commodore 64 sound chip, the SID (Sound Interface Device). Which means that in order to play back a
13
+ `.sid` file like it would sound on an actual Commodore 64, you will have to simulate both the
14
+ processor and the sound chip.
15
+
16
+ This project does not attempt to simulate the SID chip to produce authentic sounds - lots of those
17
+ players already exist - but instead has a very simple implementation that produces data that can be
18
+ used to play back the songs. You know, almost as notes.
19
+
20
+ Currently the only output format is a Ruby file which defines a list of synths to play at certain
21
+ points in time. This can be used to play back the music in [Sonic Pi](https://sonic-pi.net).
22
+
23
+ ## Limitations
24
+
25
+ Only a subset of the so-called `PSID` format is supported (a few `.sid` files use the `RSID` format
26
+ which requires a more complete Commodore 64 environment to run), and maybe not all shortcomings of
27
+ the support is handled well.
28
+
29
+ Only PAL (50 frames per second) is supported. No CIA timers or other fanciness is supported.
30
+
31
+ The conversion runs a specified number of frames (default is 1500 - this can be changed on the
32
+ command line). Ideally it should be able to run until the song finishes.
33
+
34
+ For these and other limitations, please consult [the issues](https://github.com/olefriis/sidtool/issues).
35
+
36
+ ## Installation
37
+
38
+ gem install sidtool
39
+
40
+ ## Usage
41
+
42
+ You can find lots of `.sid` files (and a super nice list of players for a wide range of platforms)
43
+ at the [High Voltage SID Collection](https://www.hvsc.c64.org) homepage.
44
+
45
+ Show information, like the author and number of songs in a file:
46
+
47
+ $ sidtool --info <input file>
48
+
49
+ Convert the default song from a file to a Ruby list:
50
+
51
+ $ sidtool --out <output file> <input file>
52
+
53
+ The output can then be used to play back the music, for example in Sonic Pi:
54
+
55
+ ```ruby
56
+ load '<path to your output file from before>'
57
+
58
+ previous_frame = 1
59
+ ::SYNTHS.each do |synth|
60
+ current_frame = synth[0]
61
+ frames_to_sleep = current_frame - previous_frame
62
+ previous_frame = current_frame
63
+ sleep frames_to_sleep/50.0 if frames_to_sleep > 0
64
+
65
+ in_thread do
66
+ use_synth synth[2]
67
+ played_synth = play synth[1], attack: synth[3], decay: synth[4], sustain: synth[5], release: synth[6]
68
+
69
+ this_frame = current_frame
70
+ controls = synth[7]
71
+ controls.each do |c|
72
+ sleep (c[0] - this_frame) / 50.0
73
+ this_frame = c[0]
74
+ control played_synth, note: c[1]
75
+ end
76
+ end
77
+ end
78
+ ```
79
+
80
+ It's a bit hacky, I know. Part of the issue is that Sonic Pi has a limit on the size of the edit buffer,
81
+ so paste the above into the buffer and edit the first line so it loads the (probably rather large)
82
+ output file from `sidtool`.
83
+
84
+ ## Development
85
+
86
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to
87
+ run the tests. You can also run `bin/console` for an interactive prompt that will allow you to
88
+ experiment.
89
+
90
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new
91
+ version, update the version number in `version.rb`, and then run `bundle exec rake release`,
92
+ which will create a git tag for the version, push git commits and tags, and push the `.gem` file
93
+ to [rubygems.org](https://rubygems.org).
94
+
95
+ ## Contributing
96
+
97
+ Bug reports and pull requests are welcome on GitHub at https://github.com/olefriis/sidtool.
98
+
99
+ ## License
100
+
101
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'sidtool'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ require 'sidtool'
3
+ require 'mos6510'
4
+ require 'optparse'
5
+
6
+ params = {}
7
+ OptionParser.new do |parser|
8
+ parser.banner = 'Usage: sidtool [options] <intputfile.sid>'
9
+
10
+ parser.on('-i', '--info', 'Show file information')
11
+ parser.on('-o', '--out FILENAME', 'Output file (Ruby array)')
12
+ parser.on('-s', '--song NUMBER', Integer, 'Song number to process')
13
+ parser.on('-f', '--frames NUMBER', Integer, 'Number of frames to process')
14
+ parser.on_tail('-h', '--help', 'Show this message') do
15
+ puts parser
16
+ exit
17
+ end
18
+ parser.on_tail('--version', 'Show version') do
19
+ puts Sidtool::Version
20
+ exit
21
+ end
22
+ end.parse!(into: params)
23
+
24
+ raise 'Missing input file' if ARGV.empty?
25
+ raise 'Too many arguments' if ARGV.length > 1
26
+ input_file = ARGV.pop
27
+ sid_file = Sidtool::FileReader.read(input_file)
28
+
29
+ output_file = params[:out]
30
+ show_info = !!params[:info]
31
+ raise 'Either provide -i or -o, or I have nothing to do!' unless output_file || show_info
32
+
33
+ song = params[:song] || sid_file.start_song
34
+ raise 'Song must be at least 1' if song < 1
35
+ raise "File only has #{sid_file.songs} songs" if song > sid_file.songs
36
+
37
+ frames = params[:frames] || 1500
38
+
39
+ if show_info
40
+ puts "Read #{sid_file.format} version #{sid_file.version} file."
41
+ puts "Name: #{sid_file.name}"
42
+ puts "Author: #{sid_file.author}"
43
+ puts "Released: #{sid_file.released}"
44
+ puts "Songs: #{sid_file.songs} (start song: #{sid_file.start_song})"
45
+ end
46
+
47
+ if output_file
48
+ load_address = sid_file.data[0] + (sid_file.data[1] << 8)
49
+
50
+ sid = Sidtool::Sid.new
51
+ cpu = Mos6510::Cpu.new(sid: sid)
52
+
53
+ cpu.load(sid_file.data[2..-1], from: load_address)
54
+ cpu.start
55
+
56
+ play_address = sid_file.play_address
57
+ if play_address == 0
58
+ cpu.jsr sid_file.init_address
59
+ play_address = (cpu.peek(0x0315) << 8) + cpu.peek(0x0314)
60
+ STDERR.puts "New play address #{play_address}"
61
+ end
62
+
63
+ cpu.jsr sid_file.init_address, song - 1
64
+
65
+ frames.times do
66
+ cpu.jsr play_address
67
+ sid.finish_frame
68
+ Sidtool::STATE.current_frame += 1
69
+ end
70
+
71
+ sid.stop!
72
+
73
+ STDERR.puts("Processed #{frames} frames")
74
+
75
+ File.open(output_file, 'w') do |file|
76
+ file.puts '::SYNTHS = ['
77
+ Sidtool::STATE.synths.each do |synth|
78
+ file.puts synth.to_a.inspect + ','
79
+ end
80
+ file.puts ']'
81
+ end
82
+ end
@@ -0,0 +1,15 @@
1
+ require 'sidtool/version'
2
+
3
+ module Sidtool
4
+ require 'sidtool/file_reader'
5
+ require 'sidtool/synth'
6
+ require 'sidtool/voice'
7
+ require 'sidtool/sid'
8
+ require 'sidtool/state'
9
+
10
+ # PAL properties
11
+ FRAMES_PER_SECOND = 50.0
12
+ CLOCK_FREQUENCY = 985248.0
13
+
14
+ STATE = State.new
15
+ end
@@ -0,0 +1,70 @@
1
+ module Sidtool
2
+ class FileReader
3
+ attr_reader :format, :version, :init_address, :play_address, :songs, :start_song
4
+ attr_reader :name, :author, :released
5
+ attr_reader :data
6
+
7
+ def self.read(path)
8
+ contents = File.open(path, 'rb', encoding: 'ascii-8bit') { |file| file.read }
9
+
10
+ expected_data_offset = 0x7C
11
+ minimum_file_size = expected_data_offset
12
+
13
+ raise "File is too small - it should be at least #{minimum_file_size} bytes. The file may be corrupt." unless contents.length >= minimum_file_size
14
+
15
+ format = contents[0..3]
16
+ raise "Unknown file format: #{format}. Only PSID is supported." unless format == 'PSID'
17
+
18
+ version = read_word(contents[4..5])
19
+ raise "Invalid version number: #{version}. Only versions 2, 3, and 4 are supported." unless version >= 2 && version <= 4
20
+
21
+ data_offset = read_word(contents[6..7])
22
+ raise "Invalid data offset: #{data_offset}. This has to be #{expected_data_offset}. The file may be corrupt." unless data_offset == expected_data_offset
23
+
24
+ load_address = read_word(contents[8..9])
25
+ raise "Unsupported load address: #{load_address}. Only 0 is supported for now." unless load_address == 0
26
+
27
+ init_address = read_word(contents[10..11])
28
+ play_address = read_word(contents[12..13])
29
+ songs = read_word(contents[14..15])
30
+ start_song = read_word(contents[16..17])
31
+
32
+ name = read_null_terminated_string(contents[22..53])
33
+ author = read_null_terminated_string(contents[54..85])
34
+ released = read_null_terminated_string(contents[86..117])
35
+
36
+ data = read_bytes(contents[data_offset..-1])
37
+
38
+ return self.new(format: format, version: version, init_address: init_address, play_address: play_address,
39
+ songs: songs, start_song: start_song, name: name, author: author, released: released,
40
+ data: data)
41
+ end
42
+
43
+ def initialize(format:, version:, init_address:, play_address:, songs:, start_song:, name:, author:, released:, data:)
44
+ @format = format
45
+ @version = version
46
+ @init_address = init_address
47
+ @play_address = play_address
48
+ @songs = songs
49
+ @start_song = start_song
50
+ @name = name
51
+ @author = author
52
+ @released = released
53
+ @data = data
54
+ end
55
+
56
+ private
57
+ def self.read_word(bytes)
58
+ (bytes[0].ord << 8) + bytes[1].ord
59
+ end
60
+
61
+ def self.read_null_terminated_string(bytes)
62
+ first_null = bytes.index("\0") || 32
63
+ bytes[0..first_null-1]
64
+ end
65
+
66
+ def self.read_bytes(bytes)
67
+ bytes.chars.map(&:ord)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,48 @@
1
+ module Sidtool
2
+ class Sid
3
+ def initialize
4
+ @voices = [Voice.new, Voice.new, Voice.new]
5
+ @synths = []
6
+
7
+ @frequency_low = @frequency_high = 0
8
+ @pulse_low = @pulse_high = 0
9
+ @control_register = 0
10
+ @attack_decay = @sustain_release = 0
11
+ end
12
+
13
+ def poke(register, value)
14
+ if register >= 0 && register <= 6
15
+ voice = @voices[0]
16
+ elsif register >= 7 && register <=13
17
+ voice = @voices[1]
18
+ register -=7
19
+ elsif register >= 14 && register <=20
20
+ voice = @voices[2]
21
+ register -=14
22
+ end
23
+
24
+ case register
25
+ when 0 then voice.frequency_low = value
26
+ when 1 then voice.frequency_high = value
27
+ when 2 then voice.pulse_low = value
28
+ when 3 then voice.pulse_high = value
29
+ when 4 then voice.control_register = value
30
+ when 5 then voice.attack_decay = value
31
+ when 6 then voice.sustain_release = value
32
+ # 7-20 are covered by the mapping above
33
+ when 21 then @cutoff_frequency_low = value
34
+ when 22 then @cutoff_frequency_high = value
35
+ when 23 then @resonance_filter = value
36
+ when 24 then @mode_volume = value
37
+ end
38
+ end
39
+
40
+ def finish_frame
41
+ @voices.each(&:finish_frame)
42
+ end
43
+
44
+ def stop!
45
+ @voices.each(&:stop!)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ module Sidtool
2
+ class State
3
+ attr_accessor :current_frame
4
+ attr_accessor :synths
5
+
6
+ def initialize
7
+ @current_frame = 0
8
+ @synths = []
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ module Sidtool
2
+ class Synth
3
+ attr_writer :waveform
4
+ attr_writer :attack
5
+ attr_writer :decay
6
+ attr_writer :release
7
+
8
+ def initialize(start_frame)
9
+ @start_frame = start_frame
10
+ @controls = []
11
+ end
12
+
13
+ def frequency=(frequency)
14
+ if @frequency
15
+ previous_midi, current_midi = sid_frequency_to_nearest_midi(@frequency), sid_frequency_to_nearest_midi(frequency)
16
+ @controls << [STATE.current_frame, current_midi] if previous_midi != current_midi
17
+ end
18
+ @frequency = frequency
19
+ end
20
+
21
+ def release!
22
+ length_of_attack_decay_sustain = (STATE.current_frame - @start_frame) / FRAMES_PER_SECOND
23
+ if length_of_attack_decay_sustain < @attack
24
+ @attack = length_of_attack_decay_sustain
25
+ @decay, @sustain_length = 0, 0
26
+ elsif length_of_attack_decay_sustain < @attack + @decay
27
+ @decay = length_of_attack_decay_sustain - @attack
28
+ @sustain_length = 0
29
+ else
30
+ @sustain_length = length_of_attack_decay_sustain - @attack - @decay
31
+ end
32
+ end
33
+
34
+ def stop!
35
+ # TODO: Should also cut off any remaining release
36
+ release!
37
+ end
38
+
39
+ def to_a
40
+ tone = sid_frequency_to_nearest_midi(@frequency)
41
+ [@start_frame, tone, @waveform, @attack.round(3), @decay.round(3), @sustain_length, @release.round(3), @controls]
42
+ end
43
+
44
+ private
45
+ def sid_frequency_to_nearest_midi(sid_frequency)
46
+ actual_frequency = sid_frequency_to_actual_frequency(sid_frequency)
47
+ nearest_tone(actual_frequency)
48
+ end
49
+
50
+ def nearest_tone(frequency)
51
+ # Stolen from Sonic Pi
52
+ midi_tone = (12 * (Math.log(frequency * 0.0022727272727) / Math.log(2))) + 69
53
+
54
+ midi_tone.round
55
+ end
56
+
57
+ def sid_frequency_to_actual_frequency(sid_frequency)
58
+ # With a standard 1 MHz clock
59
+ # (sid_frequency * 0.0596).round(2)
60
+ # PAL clock: 985248
61
+ (sid_frequency * (CLOCK_FREQUENCY / 16777216)).round(2)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module Sidtool
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,120 @@
1
+ module Sidtool
2
+ class Voice
3
+ attr_writer :frequency_low
4
+ attr_writer :frequency_high
5
+ attr_writer :pulse_low
6
+ attr_writer :pulse_high
7
+ attr_writer :control_register
8
+ attr_writer :attack_decay
9
+ attr_writer :sustain_release
10
+
11
+ def initialize
12
+ @frequency_low = @frequency_high = 0
13
+ @pulse_low = @pulse_high = 0
14
+ @attack_decay = @sustain_release = 0
15
+ @control_register = 0
16
+ @synth = nil
17
+ end
18
+
19
+ def finish_frame
20
+ if gate
21
+ if frequency > 0
22
+ unless @synth
23
+ @synth = Synth.new(STATE.current_frame)
24
+ STATE.synths << @synth
25
+ end
26
+ @synth.frequency = frequency
27
+ @synth.waveform = waveform
28
+ @synth.attack = attack
29
+ @synth.decay = decay
30
+ @synth.release = release
31
+ end
32
+ else
33
+ @synth&.release!
34
+ @synth = nil
35
+ end
36
+ end
37
+
38
+ def stop!
39
+ @synth&.stop!
40
+ @synth = nil
41
+ end
42
+
43
+ private
44
+ def gate
45
+ @control_register & 1 == 1
46
+ end
47
+
48
+ def frequency
49
+ (@frequency_high << 8) + @frequency_low
50
+ end
51
+
52
+ def waveform
53
+ return :tri if @control_register & 16 != 0
54
+ return :saw if @control_register & 32 != 0
55
+ return :pulse if @control_register & 64 != 0
56
+ return :noise if @control_register & 128 != 0
57
+ STDERR.puts "Unknown waveform: #{@control_register}"
58
+ return :noise
59
+ end
60
+
61
+ def attack
62
+ # Approximated... should be multiplied by 1.000.000 / clock
63
+ convert_attack(@attack_decay >> 4)
64
+ end
65
+
66
+ def decay
67
+ # Approximated... should be multiplied by 1.000.000 / clock
68
+ convert_decay_or_release(@attack_decay & 0xF)
69
+ end
70
+
71
+ def release
72
+ # Approximated... should be multiplied by 1.000.000 / clock
73
+ convert_decay_or_release(@sustain_release >> 4)
74
+ end
75
+
76
+ def convert_attack(attack)
77
+ case attack
78
+ when 0 then 0.002
79
+ when 1 then 0.008
80
+ when 2 then 0.016
81
+ when 3 then 0.024
82
+ when 4 then 0.038
83
+ when 5 then 0.056
84
+ when 6 then 0.068
85
+ when 7 then 0.08
86
+ when 8 then 0.1
87
+ when 9 then 0.25
88
+ when 10 then 0.5
89
+ when 11 then 0.8
90
+ when 12 then 1
91
+ when 13 then 3
92
+ when 14 then 5
93
+ when 15 then 8
94
+ else raise "Unknown value: #{attack}"
95
+ end
96
+ end
97
+
98
+ def convert_decay_or_release(decay_or_release)
99
+ case decay_or_release
100
+ when 0 then 0.006
101
+ when 1 then 0.024
102
+ when 2 then 0.048
103
+ when 3 then 0.072
104
+ when 4 then 0.114
105
+ when 5 then 0.168
106
+ when 6 then 0.204
107
+ when 7 then 0.240
108
+ when 8 then 0.3
109
+ when 9 then 0.75
110
+ when 10 then 1.5
111
+ when 11 then 2.4
112
+ when 12 then 3
113
+ when 13 then 9
114
+ when 14 then 15
115
+ when 15 then 24
116
+ else raise "Unknown value: #{decay_or_release}"
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "sidtool/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'sidtool'
7
+ spec.version = Sidtool::VERSION
8
+ spec.authors = ['Ole Friis Østergaard']
9
+ spec.email = ['olefriis@gmail.com']
10
+
11
+ spec.summary = 'Convert SID tunes to other formats'
12
+ spec.homepage = 'https://github.com/olefriis/sidtool'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.executables = 'sidtool'
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'mos6510', '~> 0.1.0'
22
+ spec.add_development_dependency 'bundler', '~> 2.0'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ spec.add_development_dependency 'pry', '~> 0.12.2'
26
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidtool
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ole Friis Østergaard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mos6510
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.12.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.12.2
83
+ description:
84
+ email:
85
+ - olefriis@gmail.com
86
+ executables:
87
+ - sidtool
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - bin/sidtool
101
+ - lib/sidtool.rb
102
+ - lib/sidtool/file_reader.rb
103
+ - lib/sidtool/sid.rb
104
+ - lib/sidtool/state.rb
105
+ - lib/sidtool/synth.rb
106
+ - lib/sidtool/version.rb
107
+ - lib/sidtool/voice.rb
108
+ - sidtool.gemspec
109
+ homepage: https://github.com/olefriis/sidtool
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.0.1
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Convert SID tunes to other formats
132
+ test_files: []