mass 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YmUwMjYwZDZlOWNkOWZiNWJhMmI5YmEzNzcxZDhkNmQwYjI0ZjcwOQ==
5
+ data.tar.gz: !binary |-
6
+ Mjk4YjIxZjM1Yjg5ZTAzNzNjZTE5NTI3MzJiNTY3NDgxOTJiZmUzMg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NjVlZmRmNzdjZTg1MWU0NTU3YmNmODUxNjQ5NGYyNDNjYTg5NDY3YmVlZmYz
10
+ MmNmODk3NjFmM2NlZWJlZWI1NWY3NmIzNWNiMGRhNDVhNWIxNjQwNTJiN2Jl
11
+ ZTg1MWNlOGVmZGQ5ZmJmMGVlZDU1MGFiNjAzMDg3M2M0MDY4NjU=
12
+ data.tar.gz: !binary |-
13
+ ZWFmYjhjODI3ODVkMGIyM2MyOGNmOWM2ZjVmYzdlODExNTAxN2Q3N2VlZjI3
14
+ MDMyYzk3ZjEwYzdiY2U0MDEwNmRmMWE1Y2Y4OTAxMDY0NmYzZGY1Mzg2Mjcy
15
+ OTc5NDVjZDFmMDQ2OWI5MjFkZDAzODlkYWU2YzZlZGVhM2VmZDM=
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ tags
11
+ /vendor/
12
+ .env
13
+ /spec/examples.txt
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ AllCops:
2
+ Exclude:
3
+ - vendor/bundle/**/*
4
+ - bin/**/*
@@ -0,0 +1 @@
1
+ 2.2.2
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ cache: bundler
3
+ deploy:
4
+ provider: rubygems
5
+ api_key:
6
+ secure: idwit0aZMOvvNrRYA74X2mC1YroT50+7XmDv4d+VT+KIHlBm8E4KCbwEO7MQSJTWQq1K3ThQMxWyp6PgeTgz4lZJRfIfndLZdqRKUkcthhMZeUNiQkP0vra9a0PIpDkQxmgSsNU7b7LEc62P2ER4mA3gtQiEsPheUx2LSlkPLhrLLXgBhSCf3RrUN3xUoYtEBWtkGg6Cqbe5f8lEs5h/JzL/brsw1IgU9IpRgQpr5twAAcSw+RxziwMj2/VxVVxHpDcda5nqg2UxPG/D8VjZtC6O1dVaLILDjqKdQnLnBLhqbGcrTWG+FDtWaQ7irXrceDHzfyffKTAtmFZxeVeKQuYBE1rSF2cbxbZw8qTtJyND0XXwEeq+2BmHqvP8717hauao/J7wLVnoaCyf6oMIFSyQY+5Ghi/pUCqQlBcHAAWOMRzv4plkoofIs4wAA//Rn9hHRDafxUTjnR4okfQLt9DgrxI4veqSui4xOknsEHyIfRyRKeVAqWI4tRAAaaOWGYequ3fCgOP086Bm5fEmlLb0KLnjBDgY/NbYVmP0JfYJAVvBFHviTtdgaDEf+46cdc1lN3tbpweS+qjMU6ChrL0xIQx/eF748Zx2m2wElEF2vgbTn70BKRxCbU8Ql8Ewh5v1bWqa7NHq7C8C/2x2YFwFFh6sEWVJHnq5z055gJQ=
7
+ gem: mass
8
+ on:
9
+ tags: true
10
+ repo: tubbo/mass
11
+ addons:
12
+ apt:
13
+ packages:
14
+ - libasound2-dev
15
+ code_climate:
16
+ repo_token:
17
+ secure: loBoOiccqVWqMihGrLjRqalFtZ2LenatY7KSbeIA94pS23lQgxCg2n9oMwpB0nB6Mgk1hBoJjKhrJau9FP50On+HK39Y8AdOPucB5YT9EICRr3/3urAtiwMdbCGGUcRRrklP3DEVvWyOIHltQWwGtTUzQaGuNiZ4AUI/9tQ/Rqtnu11Vy/FtpIG3xjGq9jeyTa69OUOHhNjzlSCywu8t1rWkR1fsRE0bDwkNmsK1OITYAD/DAbAAg4QMnOiwc6ePwxQ+GqRIGkwxdm5xCZBnVwknVXne2dfXcPvMgAdr+0np7jzjPkmkInFlIZkOjnuhRclgYYscbwnLDqLKifDU/xcDnmsaBjl3EXyN/nspx0YWIIgWY61V+NrOhnVrxVHt1tZ0hYmeaXxkmWfW6r7tlt3oKl2hZBGv0CLe7Jt5Kkoq+iv5fBKyDkEzxoNG2A5KTPoiximnWSYsFqwvyF2mes/TJ+4txyec5U3N8j0ufYaZXkIJPoECVRI2NusEMad/ZdRfkXls3+UDrr8ohQNK0REywLyp28ino+I39zvorQjfbsXZ6jMCyVsZn/rxb6T2EyrqNouFBlmGW0DoULloyA7UKmBxgovHsPEUkhzB3B/oDmHFnwhp2wyxrJBu8nOgSyLVpQYWVyFCXmLjoaOWlxLVGy34UF7Z26stDp4FG54=
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mass.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Tom Scott
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,82 @@
1
+ # Mass
2
+
3
+ [![Build Status](https://travis-ci.org/tubbo/mass.svg?branch=master)](https://travis-ci.org/tubbo/mass)
4
+ [![Code Climate](https://codeclimate.com/github/tubbo/mass/badges/gpa.svg)](https://codeclimate.com/github/tubbo/mass)
5
+ [![Test Coverage](https://codeclimate.com/github/tubbo/mass/badges/coverage.svg)](https://codeclimate.com/github/tubbo/mass/coverage)
6
+ [![Inline docs](http://inch-ci.org/github/tubbo/mass.svg?branch=master)](http://inch-ci.org/github/tubbo/mass)
7
+
8
+ Mass is a Ruby framework for building MIDI synthesizer controls.
9
+
10
+ ## Installation
11
+
12
+ On Linux, the **libasound** and **libasound-dev** are required for use.
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'mass'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ $ bundle
24
+ ```
25
+
26
+ Or install it yourself:
27
+
28
+ ```bash
29
+ $ gem install mass
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ To create a new pattern, use the DSL methods that Mass gives you upon
35
+ mixing it into whatever namespace you're dealing with.
36
+
37
+ ```ruby
38
+ require 'mass'
39
+
40
+ include Mass
41
+
42
+ bpm 128
43
+ pattern bars: 4 do
44
+ note 4, pitch: 'c2'
45
+ note 4, pitch: 'c3'
46
+ note 4, pitch: 'c4'
47
+ note 4, pitch: 'c3'
48
+ end
49
+ ```
50
+
51
+ Read the [RDoc documentation][rdoc] for more information on
52
+ each component and what each DSL method does.
53
+
54
+ ## Development
55
+
56
+ Mass was created by [Tom Scott][tubbo] and is published under
57
+ the [MIT License][mit]. All contributions are welcome as long
58
+ as they are submitted via pull request, include tests that
59
+ describe your change and prove what you did works, and do not
60
+ break the CI build.
61
+
62
+ To get started with contributing, clone down this repo and run the
63
+ following command within its root directory:
64
+
65
+ ```bash
66
+ $ bin/setup
67
+ ```
68
+
69
+ If you need to install this gem onto your local machine, run:
70
+
71
+ ```bash
72
+ $ bin/rake install
73
+ ```
74
+
75
+ To release a new version, update the version number in
76
+ **lib/mass/version.rb**, then run the following command to create a Git
77
+ tag for the release, push all commits & tags to the repo, and upload the
78
+ newly built `.gem` file to [RubyGems][gem]:
79
+
80
+ ```bash
81
+ $ bin/rake release
82
+ ```
@@ -0,0 +1,25 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ # Remove the original :release task, we are redefining it below.
6
+ Rake::Task['release'].clear
7
+
8
+ # Run RSpec code examples
9
+ RSpec::Core::RakeTask.new :test
10
+
11
+ desc 'Run RuboCop lint checks'
12
+ RuboCop::RakeTask.new :lint
13
+
14
+ desc 'Run RuboCop lint checks and RSpec code examples'
15
+ task test: %i(lint spec)
16
+
17
+ desc 'Bump the version, tag a commit and deploy to RubyGems with Travis CI'
18
+ task release: %w(build release:guard_clean release:source_control_push) do
19
+ Bundler.ui.confirm(
20
+ 'Please wait for Travis CI to push the working build to RubyGems.'
21
+ )
22
+ end
23
+
24
+ # Run all tests, then build and install the gem on CI.
25
+ task default: %i(test install)
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'cc-tddium-post-worker' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('codeclimate-test-reporter', 'cc-tddium-post-worker')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'coderay' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('coderay', 'coderay')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mass"
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
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'htmldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('diff-lcs', 'htmldiff')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'launchy' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('launchy', 'launchy')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('diff-lcs', 'ldiff')
data/bin/pry ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'pry' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('pry', 'pry')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rubocop' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rubocop', 'rubocop')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ruby-parse' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('parser', 'ruby-parse')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ruby-rewrite' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('parser', 'ruby-rewrite')
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'travis' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('travis', 'travis')
@@ -0,0 +1,51 @@
1
+ require 'unimidi'
2
+
3
+ require 'mass/version'
4
+ require 'mass/pitch'
5
+ require 'mass/note'
6
+ require 'mass/pattern'
7
+
8
+ # A massive synth library.
9
+ module Mass
10
+ class << self
11
+ # The current BPM of this track. Defaults to +100+.
12
+ #
13
+ # @return [Integer]
14
+ def current_bpm
15
+ @current_bpm ||= 100
16
+ end
17
+
18
+ # Change BPM of the track.
19
+ #
20
+ # @example
21
+ # require 'mass'
22
+ # include Mass
23
+ #
24
+ # bpm 128
25
+ #
26
+ def bpm(new_bpm)
27
+ @current_bpm = new_bpm
28
+ end
29
+
30
+ # Create a pattern in Mass.
31
+ #
32
+ # @example
33
+ # require 'mass'
34
+ # include Mass
35
+ #
36
+ # pattern name: 'verse', bars: 1 do
37
+ # note 8, pitch: 'C4'
38
+ # note 8, pitch: 'C3'
39
+ # note 8, pitch: 'A3'
40
+ # note 8, pitch: 'B4'
41
+ # note 8, pitch: 'C4'
42
+ # note 8, pitch: 'Gb2'
43
+ # note 8, pitch: 'C4'
44
+ # rest 8
45
+ # end
46
+ #
47
+ def pattern(**params, &block)
48
+ Pattern.create(**params, &block)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,105 @@
1
+ require 'forwardable'
2
+
3
+ module Mass
4
+ # Represents a single note in the pattern.
5
+ class Note
6
+ extend Forwardable
7
+
8
+ # Dictionary of velocity values from a given +expression+.
9
+ #
10
+ # @type [Hash<Symbol>]
11
+ VELOCITIES = {
12
+ ff: 127,
13
+ mf: 117,
14
+ f: 107,
15
+ mp: 97,
16
+ p: 85,
17
+ pp: 75
18
+ }
19
+
20
+ # Duration value of the note.
21
+ #
22
+ # @attr_reader [Integer]
23
+ attr_reader :duration
24
+
25
+ # Expression value of the note, e.g. ':ff'. This can
26
+ # optionally be given as an Integer for maximum velocity
27
+ # control and is simply output as the velocity.
28
+ #
29
+ # The following expression values are supported:
30
+ #
31
+ # - :ff
32
+ # - :mf
33
+ # - :f
34
+ # - :mp
35
+ # - :p
36
+ # - :pp
37
+ #
38
+ # @attr_reader [Symbol | Integer]
39
+ attr_reader :expression
40
+
41
+ # MIDI output object.
42
+ #
43
+ # @attr_reader [UniMIDI::Output]
44
+ attr_reader :midi
45
+
46
+ # Pitch object used for figuring out the MIDI pitch
47
+ # value.
48
+ #
49
+ # @attr_reader [Mass::Pitch]
50
+ attr_reader :pitch
51
+
52
+ # Hex value for sending to +UniMIDI+ that signals when
53
+ # this note should begin playing.
54
+ #
55
+ # @type [Fixnum]
56
+ ON = 0x90
57
+
58
+ # Hex value for sending to +UniMIDI+ that signals when
59
+ # this note should cease playing.
60
+ #
61
+ # @type [Fixnum]
62
+ OFF = 0x80
63
+
64
+ # @param [Integer] value
65
+ # @param [String] pitch
66
+ # @param [Symbol | Integer] exp - Can be expressed as either
67
+ # @param [UniMIDI::Output] midi
68
+ def initialize(value: 1, pitch: nil, exp: :mp, midi: nil)
69
+ @value = value
70
+ @pitch = Pitch.find pitch
71
+ @expression = exp
72
+ @midi = midi
73
+ end
74
+
75
+ # The given note name.
76
+ #
77
+ # @return [String]
78
+ def_delegator :pitch, :name, :id
79
+
80
+ # This note as expressed in a MIDI pitch value.
81
+ #
82
+ # @return [Integer]
83
+ def_delegator :pitch, :to_i, :to_midi
84
+
85
+ # This note as expressed in a MIDI velocity value.
86
+ #
87
+ # @return [Integer]
88
+ def to_velocity
89
+ VELOCITIES[expression] || expression
90
+ end
91
+
92
+ # The given duration value divided by the BPM of
93
+ # the current song.
94
+ def duration
95
+ value / Mass.current_bpm
96
+ end
97
+
98
+ # Play the current note through the +UniMIDI+ output.
99
+ def play
100
+ midi.puts ON, to_midi, to_velocity if pitch.present?
101
+ sleep duration
102
+ midi.puts OFF, to_midi, to_velocity if pitch.present?
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,74 @@
1
+ module Mass
2
+ # A single pattern written using the +Mass+ DSL. This is the
3
+ # "collection"-style object which holds each +Note+ and plays
4
+ # them in sequence, but has no control over their durations or
5
+ # pitches.
6
+ class Pattern
7
+ attr_reader :name, :bars, :notes
8
+
9
+ # @param [String] name
10
+ # @param [Integer] bars
11
+ # @param block
12
+ def initialize(name: '', bars: 4, &_block)
13
+ @name = name
14
+ @bars = bars
15
+ @notes = []
16
+ yield if block_given?
17
+ end
18
+
19
+ # Create a new pattern and immediately play it.
20
+ #
21
+ # @param [String] name
22
+ # @param [Integer] bars
23
+ # @param block
24
+ def self.create(name: '', bars: 4, repeat: false, &block)
25
+ pattern = new(name: name, bars: bars, &block)
26
+ pattern.play! in_loop: repeat
27
+ pattern
28
+ end
29
+
30
+ # Play the instantiated pattern once, or in a loop if opted
31
+ # into it.
32
+ #
33
+ # @param [Boolean] in_loop - defaults to +false+.
34
+ def play!(in_loop: false)
35
+ _play_once unless in_loop
36
+ loop { _play_once }
37
+ end
38
+
39
+ protected
40
+
41
+ # Part of the DSL, the +note+ method instantiates a
42
+ # new +Note+ object with the given parameters and
43
+ # pushes it into the pattern's notes collection.
44
+ #
45
+ # @private
46
+ # @param [Integer] value - Value of each note
47
+ # @param [String] pitch - String representation
48
+ # of MIDI note, e.g. 'c4'
49
+ # @param [Symbol] expression - Symbolic representation
50
+ # of MIDI velocity, e.g. ':ff'
51
+ def note(value, pitch: nil, expression: nil)
52
+ @notes << Note.new(
53
+ value: value,
54
+ pitch: pitch,
55
+ exp: expression,
56
+ midi: _midi
57
+ )
58
+ end
59
+
60
+ def rest(value)
61
+ note value
62
+ end
63
+
64
+ private
65
+
66
+ def _midi
67
+ @midi ||= UniMIDI::Output.gets
68
+ end
69
+
70
+ def _play_once
71
+ notes.map(&:play)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,95 @@
1
+ module Mass
2
+ # Represents the pitch of a given note, and calculates its
3
+ # correct MIDI value, leaving the +Note+ class more for
4
+ # calculating the proper duration and actually playing out
5
+ # the note.
6
+ class Pitch
7
+ attr_reader :id, :name, :value
8
+
9
+ # A dictionary of MIDI note values that are substituted
10
+ # for a given String note value.
11
+ #
12
+ # @type [Array<String>]
13
+ VALUES = {
14
+ 'C' => 36,
15
+ 'C#' => 37,
16
+ 'Db' => 37,
17
+ 'D' => 38,
18
+ 'D#' => 39,
19
+ 'Eb' => 39,
20
+ 'E' => 40,
21
+ 'F' => 41,
22
+ 'F#' => 42,
23
+ 'Gb' => 42,
24
+ 'G' => 43,
25
+ 'G#' => 44,
26
+ 'Ab' => 44,
27
+ 'A' => 45,
28
+ 'A#' => 46,
29
+ 'Bb' => 46,
30
+ 'B' => 47
31
+ }
32
+
33
+ # A collection of attributes which are required for
34
+ # this object to be considered a valid pitch.
35
+ #
36
+ # @type [Array<Symbol>]
37
+ REQUIRED = %i(name octave value)
38
+
39
+ # @param [String] id - Identifier string of this pitch.
40
+ def initialize(identifier)
41
+ @id = identifier.to_s
42
+ @name = id.gsub(/\d/, '').to_s
43
+ @octave = id.gsub(/#{name}/, '').to_i
44
+ @value = begin
45
+ VALUES[name]
46
+ rescue
47
+ nil
48
+ end
49
+ end
50
+
51
+ # Find a +Pitch+ by its given +id+.
52
+ #
53
+ # @return [Pitch] when the id is valid
54
+ # @throw [Pitch::NotFound] when id is not valid
55
+ def self.find(id)
56
+ pitch = new(id)
57
+ fail NotFound, id unless pitch.valid?
58
+ pitch
59
+ end
60
+
61
+ # Make sure the +name+ and +octave+ attributes have
62
+ # been correctly parsed out of the given +id+.
63
+ #
64
+ # @return [Boolean] whether the object is valid
65
+ def valid?
66
+ required_params.all? do |param|
67
+ !param.nil? && (param != '') && (param != 0)
68
+ end
69
+ end
70
+
71
+ # The MIDI value of this pitch.
72
+ #
73
+ # @return [Integer]
74
+ def to_i
75
+ value + octave_modifier
76
+ end
77
+
78
+ private
79
+
80
+ def required_params
81
+ REQUIRED.map do |param_name|
82
+ send param_name
83
+ end
84
+ end
85
+
86
+ # The number by which the given scalar pitch gets modified to
87
+ # produce the correct MIDI note value for playback.
88
+ #
89
+ # @private
90
+ # @return [Integer]
91
+ def octave_modifier
92
+ 12 * octave
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,10 @@
1
+ module Mass
2
+ class Pitch
3
+ # Thrown when a +Pitch+ cannot be found, e.g. it is not valid.
4
+ class NotFound < RuntimeError
5
+ def initialize(name)
6
+ @message = "Invalid pitch '#{name}'"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # :nodoc:
2
+ module Mass
3
+ # Version number
4
+ #
5
+ # @type [String]
6
+ VERSION = '0.0.1'
7
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mass/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'mass'
8
+ spec.version = Mass::VERSION
9
+ spec.authors = ['Tom Scott']
10
+ spec.email = ['tscott@weblinc.com']
11
+
12
+ spec.summary = 'Synth framework for Ruby.'
13
+ spec.description = "#{spec.summary} Build a cool synth!"
14
+ spec.homepage = 'https://github.com/tubbo/mass'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |file|
18
+ file.match(%r{^(spec)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.10'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'rubocop', '~> 0'
28
+ spec.add_development_dependency 'travis', '~> 1.8'
29
+ spec.add_development_dependency 'codeclimate-test-reporter', '~> 0'
30
+
31
+ spec.add_dependency 'unimidi'
32
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mass
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Scott
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: travis
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.8'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: unimidi
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Synth framework for Ruby. Build a cool synth!
112
+ email:
113
+ - tscott@weblinc.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - .rspec
120
+ - .rubocop.yml
121
+ - .ruby-version
122
+ - .travis.yml
123
+ - CODE_OF_CONDUCT.md
124
+ - Gemfile
125
+ - LICENSE.txt
126
+ - README.md
127
+ - Rakefile
128
+ - bin/cc-tddium-post-worker
129
+ - bin/coderay
130
+ - bin/console
131
+ - bin/htmldiff
132
+ - bin/launchy
133
+ - bin/ldiff
134
+ - bin/pry
135
+ - bin/rake
136
+ - bin/rspec
137
+ - bin/rubocop
138
+ - bin/ruby-parse
139
+ - bin/ruby-rewrite
140
+ - bin/setup
141
+ - bin/travis
142
+ - lib/mass.rb
143
+ - lib/mass/note.rb
144
+ - lib/mass/pattern.rb
145
+ - lib/mass/pitch.rb
146
+ - lib/mass/pitch/not_found.rb
147
+ - lib/mass/version.rb
148
+ - mass.gemspec
149
+ homepage: https://github.com/tubbo/mass
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ! '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.4.5
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Synth framework for Ruby.
173
+ test_files: []