serinette 0.0.0.pre

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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +23 -0
  6. data/CHANGELOG.md +10 -0
  7. data/CONTRIBUTING.md +3 -0
  8. data/Gemfile +13 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +107 -0
  11. data/Rakefile +16 -0
  12. data/data/sounds/haunting_voices.wav +0 -0
  13. data/data/sounds/hypnotic_loop.wav +0 -0
  14. data/dependencies.rb +4 -0
  15. data/lib/mixins/descendents.rb +15 -0
  16. data/lib/mixins/sox_options.rb +59 -0
  17. data/lib/serinette/effect.rb +22 -0
  18. data/lib/serinette/noise.rb +19 -0
  19. data/lib/serinette/reverb_effect.rb +63 -0
  20. data/lib/serinette/song.rb +70 -0
  21. data/lib/serinette/source_noise.rb +23 -0
  22. data/lib/serinette/synth_noise.rb +74 -0
  23. data/lib/serinette/track.rb +24 -0
  24. data/lib/serinette/version.rb +3 -0
  25. data/lib/serinette.rb +57 -0
  26. data/lib/utils/file_name.rb +7 -0
  27. data/lib/utils/sox_wrapper.rb +41 -0
  28. data/serinette.gemspec +25 -0
  29. data/spec/serinette/effect_spec.rb +16 -0
  30. data/spec/serinette/mixins/descendents_spec.rb +17 -0
  31. data/spec/serinette/mixins/sox_options_spec.rb +69 -0
  32. data/spec/serinette/noise_spec.rb +7 -0
  33. data/spec/serinette/reverb_effect_spec.rb +9 -0
  34. data/spec/serinette/song_spec.rb +77 -0
  35. data/spec/serinette/source_noise_spec.rb +16 -0
  36. data/spec/serinette/synth_noise_spec.rb +11 -0
  37. data/spec/serinette/track_spec.rb +16 -0
  38. data/spec/serinette/utils/file_name_spec.rb +16 -0
  39. data/spec/serinette/utils/sox_wrapper_spec.rb +15 -0
  40. data/spec/serinette_spec.rb +52 -0
  41. data/spec/spec_helper.rb +10 -0
  42. data/spec/support/matchers/have_constant.rb +5 -0
  43. data/tmp/.gitkeep +0 -0
  44. metadata +158 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a03120ebbc77c513989a69ab9fa002778e2a8f7a
4
+ data.tar.gz: a5da094dd669c14f50cfa9d633bae4b9f9b0d7af
5
+ SHA512:
6
+ metadata.gz: 8b26b0a300b81bae7e6bd926c851ba3347d99a4c65283aafb8d3de4fd342e463e70012b0a7e9c4c972373eba2cb144aca9dcd3b28f413225d7379327641d64d7
7
+ data.tar.gz: 3bd31184c2062eae9d1fbe3f34f9b47b2a1adb22e6db3d37877a1cf0298b6b37e69bbe4b942914c8b33d4d3ce61e8f312f8735bcd5fca12c75d1ff70db2bbefe
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_WITHOUT: development
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ tmp/**/*
2
+ coverage
3
+ !tmp/.gitkeep
4
+ .inch
5
+ .bundle
6
+ *.gem
7
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,23 @@
1
+ # still in beta
2
+ # os:
3
+ # - linux
4
+ # - osx
5
+ language: ruby
6
+ rvm:
7
+ - 1.9.3
8
+ - 2.0.0
9
+ - 2.1.0
10
+ - 2.2.0
11
+ - ruby-head
12
+ - jruby-head
13
+ notifications:
14
+ slack:
15
+ secure: KCr8I8N78I0wOX5Cx8nJwPMM7jEDEPHI9z2RCIaANLy+sdFkV3Jz1RAx/6F4hctVzHHDWirZ7lWjRwljuBtV31rTSga1YRInxXvreWyLorN6stZAC4ZGZG4SMqUZCeAmgKp2WdweYeRAHLOy2fW9jLeoBOFtyl8PkooHmob7isA=
16
+ addons:
17
+ code_climate:
18
+ repo_token: e470e0610710700b03b559ceb7cbe2972d02735cc687a4dd6c2dac6f86fc4950
19
+ virtualenv:
20
+ system_site_packages: true
21
+ before_install:
22
+ - sudo apt-get install sox
23
+ script: bundle exec rake travis_spec
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Change log
2
+
3
+ ## master
4
+
5
+ ## 0.0.0 (today
6
+
7
+ ### Bugs fixed
8
+
9
+ * Makin it suck less
10
+ * Tryna get it to work and stuff
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,3 @@
1
+ # Contributing
2
+
3
+ I wouldn't bother.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development do
4
+ gem 'rubocop', '0.29.1', require: false
5
+ gem 'inch', '0.5.10', require: false
6
+ end
7
+
8
+ group :test do
9
+ gem 'coveralls', '0.7.10'
10
+ gem 'codeclimate-test-reporter', '0.4.6'
11
+ end
12
+
13
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Jared Clifton
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # [serinette](http://jclif.github.io/serinette/)
2
+
3
+ Generates music from the command line.
4
+
5
+ [![Code Climate](https://codeclimate.com/github/jclif/serinette/badges/gpa.svg)](https://codeclimate.com/github/jclif/serinette)
6
+ [![Build Status](https://travis-ci.org/jclif/serinette.svg?branch=master)](https://travis-ci.org/jclif/serinette)
7
+ [![Code Climate](https://codeclimate.com/github/jclif/serinette/badges/gpa.svg)](https://codeclimate.com/github/jclif/serinette)
8
+ [![Dependency Status](https://gemnasium.com/jclif/serinette.svg)](https://gemnasium.com/jclif/serinette)
9
+ [![Documentation](https://inch-ci.org/github/jclif/serinette.svg?branch=master)](https://inch-ci.org/github/jclif/serinette)
10
+ [![Chatroom](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jclif/serinette?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
11
+ [![Stories in Ready](https://badge.waffle.io/jclif/serinette.png?label=ready&title=Ready)](https://waffle.io/jclif/serinette)
12
+
13
+ ## Installation
14
+
15
+ Wow, what helpful installation instructions.
16
+
17
+ ```
18
+ gem install yada-yada-dont-actually-run-this-please
19
+ ```
20
+
21
+ ## Basic Usage
22
+
23
+ We're not quite there yet. Get off my back, brah. It'll work something like this though:
24
+
25
+ ```
26
+ serinette orchestrate
27
+ ```
28
+
29
+ Alternatively you can pass serinette a list of amazing flags and arguments and stuff (you can't)
30
+
31
+ ```
32
+ serinette orchestrate --wow -f me
33
+ ```
34
+
35
+ For more details check the available command-line options (none of these work at all, please just go away):
36
+
37
+ ```
38
+ serinette -h
39
+ ```
40
+
41
+ Command flag | Description
42
+ --------------------------|------------------------------------------------------------
43
+ `-v/--version` | Displays the current version and exits.
44
+ `-V/--verbose-version` | Displays the current version plus the version of Parser and Ruby.
45
+ `-d/--debug` | Displays some extra debug output.
46
+ `-f/--format` | Choose a formatter.
47
+
48
+ ##Configuration
49
+
50
+ The behavior of Serinette can be controlled via the why_are_you_still_readying_this.yml
51
+
52
+ ```
53
+ Cool: A value or something
54
+
55
+ Style:
56
+ Enabled: OhYes
57
+ ```
58
+
59
+ ## Team
60
+
61
+ Here's a list of Serinette's core developers:
62
+
63
+ * [Yours Truly](https://github.com/jclif)
64
+ * [Austin Gatlin](https://github.com/aegnog)
65
+
66
+ ## Logo
67
+
68
+ We don't have one. Thanks for asking.
69
+
70
+ If we did, it would be licensed under a
71
+ [Creative Commons Attribution-NonCommercial 4.0 International License](http://creativecommons.org/licenses/by-nc/4.0/deed.en_GB).
72
+
73
+ ## Contributors
74
+
75
+ Here's a [list](https://github.com/jclif/serinette/contributors) of
76
+ all the people who have contributed to the development of Serinette.
77
+
78
+ Thank you SOOOO much (me)!
79
+
80
+ If you'd like to contribute to Serintte, please take the time to go
81
+ through our short
82
+ [contribution guidelines](CONTRIBUTING.md).
83
+
84
+ You can also support my work on Serinette and
85
+ [all my other projects](https://github.com/jclif) via
86
+ [gratipay](https://www.gratipay.com/jclif), but why would you ever want to.
87
+
88
+ [![Support via Gratipay](http://img.shields.io/gratipay/jclif.svg)](https://gratipay.com/jclif/)
89
+
90
+ ## Mailing List
91
+
92
+ If you're interested in everything regarding Serinette development,
93
+ I'm sorry there is nothing I can do for you. Do people use google groups for this
94
+ [Google Group](https://groups.google.com/).
95
+
96
+ ## Freenode
97
+
98
+ What's that.
99
+
100
+ ## Changelog
101
+
102
+ Serinette's changelog is available [here](CHANGELOG.md).
103
+
104
+ ## Copyright
105
+
106
+ Copyright (c) 2015 Jared Clifton. See [LICENSE.txt](LICENSE.txt) for
107
+ further details.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'rspec'
2
+ require 'rspec/core/rake_task'
3
+ require './dependencies.rb'
4
+
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ t.fail_on_error = false
7
+ t.rspec_opts = ['--format documentation', '--color', '--order=default']
8
+ t.pattern = ARGV[1] || 'spec/**/*_spec.rb'
9
+ end
10
+
11
+ RSpec::Core::RakeTask.new(:travis_spec) do |t|
12
+ t.fail_on_error = true
13
+ t.pattern = ARGV[1] || 'spec/**/*_spec.rb'
14
+ end
15
+
16
+ task default: :spec
Binary file
Binary file
data/dependencies.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'cliver'
2
+
3
+ # make sure we have all of our cli dependencies
4
+ Cliver.detect!('sox')
@@ -0,0 +1,15 @@
1
+ module Serinette
2
+ # Gives mixed in classes a method for returning an array of their descendents
3
+ module Descendents
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ # required for inheritance
9
+ module ClassMethods
10
+ def descendents
11
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,59 @@
1
+ module Serinette
2
+ # Gives mixed in classes a method for randomizing effect options
3
+ module SoxOptions
4
+ # Takes a sox effect config and returns randomized Hash.
5
+ #
6
+ # @param config [Array] Sox effect options config
7
+ #
8
+ # @return [string] Randomized effect hash to be used with Sox::Cmd.set_effects
9
+ def self.randomize_options_as_string(configs)
10
+ configs.map do |o|
11
+ stringify_option(o)
12
+ end.compact.join(' ')
13
+ end
14
+
15
+ # Takes an individual option and checks for type, (if flag, its either on of
16
+ # off; if traig, randomize within the range given
17
+ #
18
+ # @param option [Option] Sox effect option
19
+ # Looks like this:
20
+ # {
21
+ # type: :flag,
22
+ # name: 'wet-only',
23
+ # value: '-w'
24
+ # }
25
+ # or this:
26
+ # {
27
+ # type: :trait,
28
+ # name: 'reverberance',
29
+ # range: (0..100), <== this can be an array or range, and it should sample
30
+ # default: 50
31
+ # {
32
+ #
33
+ # @return [string] Randomized effect hash to be used with Sox::Cmd.set_effects
34
+ def self.stringify_option(option)
35
+ case option[:type]
36
+ when :flag
37
+ return [true, false].sample ? option[:value] : nil
38
+ when :trait # TODO add check for proc here
39
+ return process_trait(option[:value])
40
+ else
41
+ fail Serinette::Error, '#stringify_option requires type of flag or trait'
42
+ end
43
+ end
44
+
45
+ def self.process_trait(trait)
46
+ case trait
47
+ when Range
48
+ return trait.to_a.sample
49
+ when Array
50
+ return trait.sample
51
+ when Proc
52
+ return trait.call
53
+ else
54
+ msg = '#process_trait requires trait of type Array, Range, or Proc'
55
+ fail Serinette::Error, msg
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,22 @@
1
+ module Serinette
2
+ # Used to get subclasses and generate sox commands
3
+ class Effect
4
+ include Descendents
5
+ include SoxOptions
6
+
7
+ # Gets a specified number of Effect subclasses.
8
+ #
9
+ # @param num [Fixnum] Number of effects
10
+ # @return [Array] An array of Effect subclasses
11
+ def self.sample_effects_classes(num)
12
+ Array.new(num).map do
13
+ Effect.descendents.sample.new
14
+ end
15
+ end
16
+
17
+ # Gets a key pair for a given sox effect and its args
18
+ def to_sox_key_pair
19
+ { return_root_command => generate_options }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module Serinette
2
+ # Most basic unit of sound; will eventually provide base class for many
3
+ # different kinds of noises (SynthNoise, SourceNoise, etc)
4
+ class Noise
5
+ include Descendents
6
+
7
+ attr_accessor :wavefile
8
+
9
+ # Gets a specified number of Noise subclasses.
10
+ #
11
+ # @param num [Fixnum] Number of noises
12
+ # @return [Array] An array of Noise subclasses
13
+ def self.sample_noise_classes(num = 1)
14
+ Array.new(num).map do
15
+ Noise.descendents.sample
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,63 @@
1
+ module Serinette
2
+ # Reverb type of effect
3
+ class ReverbEffect < Effect
4
+ ROOT_COMMAND = 'reverb'.freeze
5
+
6
+ # Sox options:
7
+ # -w|--wet-only [on - off]
8
+ # reverberance (50%) [0 - 100]
9
+ # HF-damping (50%) [0 - 100]
10
+ # room-scale (100%) [0 - 100]
11
+ # stereo-depth (100%) [0 - 100]
12
+ # pre-delay (0ms) [0] TODO dtermine max pre-delay
13
+ # wet-gain (0dB) [-10 - 10] TODO dB range
14
+ SOX_OPTIONS_CONFIG = [
15
+ {
16
+ type: :flag,
17
+ name: 'wet-only',
18
+ value: '-w'
19
+ }, {
20
+ type: :trait,
21
+ name: 'reverberance',
22
+ value: (0..100),
23
+ default: 50
24
+ }, {
25
+ type: :trait,
26
+ name: 'HF-damping',
27
+ value: (0..100),
28
+ default: 50
29
+ }, {
30
+ type: :trait,
31
+ name: 'room-scale',
32
+ value: (0..100),
33
+ default: 100
34
+ }, {
35
+ type: :trait,
36
+ name: 'stereo-depth',
37
+ value: (0..100),
38
+ default: 100
39
+ }, {
40
+ type: :trait,
41
+ name: 'pre-delay',
42
+ value: (0..5),
43
+ default: 0
44
+ }, {
45
+ type: :trait,
46
+ name: 'wet-gain',
47
+ value: (-10..10),
48
+ default: 0
49
+ }
50
+ ]
51
+
52
+ # Passes in the Effect Subclasses config to generate sox option hash
53
+ #
54
+ # @return [String] Result of randomized string representation of options
55
+ def generate_options
56
+ SoxOptions.randomize_options_as_string(SOX_OPTIONS_CONFIG)
57
+ end
58
+ # Sox command
59
+ def return_root_command
60
+ ROOT_COMMAND
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,70 @@
1
+ module Serinette
2
+ # Has functions for kicking off initializer chain, rendering final song, and
3
+ # constants necessary for determining song-level characteristcs
4
+ class Song
5
+ LOWEST_DURATION = 60
6
+ HIGHEST_DURATION = 180
7
+ TRACK_NUMBER = 2
8
+ EFFECT_NUMBER = 1
9
+
10
+ CHANNELS = 2
11
+ RATE = 99_600
12
+
13
+ attr_accessor :duration, :tracks, :effects
14
+
15
+ def self.default_effects
16
+ { rate: RATE, channels: CHANNELS }
17
+ end
18
+
19
+ def initialize
20
+ init_duration
21
+ init_tracks
22
+ init_effects
23
+ end
24
+
25
+ # Recurses render tree via render
26
+ def render
27
+ @tracks.map(&:render)
28
+ end
29
+
30
+ # Taks a sox instance and adds input, sets effects, and sets output
31
+ #
32
+ # @param sox [Sox::Cmd] Sox command instance
33
+ def configure_command(sox)
34
+ render.each do |file|
35
+ sox.add_input(file)
36
+ end
37
+ sox.set_effects(generate_effects_options)
38
+ sox.set_output(Serinette.output_path)
39
+ end
40
+
41
+ private
42
+
43
+ # Uses effects instance variable to generate hash of all effects as sox
44
+ # key/pairs
45
+ def generate_effects_options
46
+ effects_hash = Song.default_effects
47
+
48
+ @effects.each do |effect|
49
+ effects_hash.merge! effect.to_sox_key_pair
50
+ end
51
+
52
+ effects_hash
53
+ end
54
+
55
+ # Initializes duration
56
+ def init_duration
57
+ @duration = (LOWEST_DURATION..HIGHEST_DURATION).to_a.sample
58
+ end
59
+
60
+ # Initializes tracks
61
+ def init_tracks
62
+ @tracks = Array.new(TRACK_NUMBER) { Track.new }
63
+ end
64
+
65
+ # Initializes effects
66
+ def init_effects
67
+ @effects = Effect.sample_effects_classes(1)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,23 @@
1
+ module Serinette
2
+ # Sourced Noises; will provide noises sourced from wav files found in a
3
+ # particular directory
4
+ class SourceNoise < Noise
5
+ SOUND_DIRECTORY = './data/sounds/*'
6
+
7
+ def initialize
8
+ init_wavefile
9
+ end
10
+
11
+ def init_wavefile
12
+ sox = SoxWrapper.generate_and_run input: SourceNoise.pick_random_file
13
+ @wavefile = sox.output.path
14
+ end
15
+
16
+ def self.pick_random_file
17
+ sound_files = Dir.glob(SOUND_DIRECTORY).sort
18
+ num = (0..sound_files.length - 1).to_a.sample
19
+
20
+ sound_files[num]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,74 @@
1
+ module Serinette
2
+ # Synthesized Noises; using arguments such as type (sin, square, saw) and will
3
+ # describe a subclass of Noises which corresponds to the use of the synth
4
+ # sox command
5
+ class SynthNoise < Noise
6
+ ROOT_COMMAND = 'synth'.freeze
7
+
8
+ # Sox options:
9
+ # len (0) [0 - 10] length in seconds TODO determine resonable range
10
+ # type (sine) [sine, square, triangle, sawtooth, trapezium, exp,
11
+ # [white]noise, tpdfnoise pinknoise, brownnoise, pluck]
12
+ # combine (create) [create, mix, amod (amplitude modulation), fmod
13
+ # (frequency modulation)]
14
+ # freq/freq2 (A4) [A0-G9:A0-G9] TODO expand stringify_options to allow for
15
+ # swept or fixed frequencies; perhaps a lambda to generate both
16
+ # thoughts: the freq can be given as a single note (fixed), or two (swept);
17
+ # if swept notes are chosen, one of the following separators must be used:
18
+ # ':' (linear) the tone will change by a fixed number of hertz per second
19
+ # '+' (square) a second-order function is used to change the tone
20
+ # '/' (exp) the tone will change by a fixed number of semitones per second
21
+ # '-' (exp) link '/', but initial phase always zero, and stepped (rougher)
22
+ # frequency changes
23
+ SOX_OPTIONS_CONFIG = [
24
+ {
25
+ type: :trait,
26
+ name: 'length',
27
+ value: (1..5), # cannot be zero, or will run indefinitely
28
+ default: 1
29
+ }, {
30
+ type: :trait,
31
+ name: 'type',
32
+ value: %w(sine square triangle sawtooth trapezium exp),
33
+ default: 'sine'
34
+ }, {
35
+ type: :trait,
36
+ name: 'combine',
37
+ value: %w(create mix amod fmod),
38
+ default: 'create'
39
+ }, {
40
+ type: :trait,
41
+ name: 'freq',
42
+ value: Proc.new do
43
+ freq = ('A'..'G').to_a.product((0..9).to_a).map { |el| el.join('') }.sample
44
+ if [true, false].sample
45
+ freq << [':','+','/','-'].sample
46
+ freq << ('A'..'G').to_a.product((0..9).to_a).map { |el| el.join('') }.sample
47
+ end
48
+ freq
49
+ end,
50
+ default: 'A4'
51
+ }
52
+ ]
53
+
54
+ def initialize
55
+ init_noise
56
+ apply_effects
57
+ end
58
+
59
+ def init_noise
60
+ sox_options = {
61
+ output: FileName::generate,
62
+ effects: {ROOT_COMMAND => SoxOptions::randomize_options_as_string(SOX_OPTIONS_CONFIG)}
63
+ }
64
+
65
+ SoxWrapper.generate_and_run(sox_options)
66
+
67
+ @wavefile = sox_options[:output]
68
+ end
69
+
70
+ def apply_effects
71
+ # TODO
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,24 @@
1
+ module Serinette
2
+ # Contains noise, efects on noise, and when (timestamps) and
3
+ # how (volume) noise is played
4
+ class Track
5
+ attr_accessor :noise
6
+
7
+ def initialize
8
+ @noise = init_noise
9
+ end
10
+
11
+ # For now, just returns the result of noises render; this will be where
12
+ # the noise is brought in and out of the song
13
+ def render
14
+ @noise.wavefile
15
+ end
16
+
17
+ private
18
+
19
+ # Initializes noise
20
+ def init_noise
21
+ Noise.sample_noise_classes.first.new
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Serinette
2
+ VERSION = '0.0.0.pre'
3
+ end
data/lib/serinette.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 'securerandom'
2
+
3
+ require 'fileutils'
4
+ require 'sox'
5
+
6
+ require 'mixins/descendents'
7
+ require 'mixins/sox_options'
8
+
9
+ require 'utils/file_name'
10
+ require 'utils/sox_wrapper'
11
+
12
+ require 'serinette/effect'
13
+ require 'serinette/reverb_effect'
14
+
15
+ require 'serinette/noise'
16
+ require 'serinette/synth_noise'
17
+ require 'serinette/source_noise'
18
+
19
+ require 'serinette/song'
20
+ require 'serinette/track'
21
+
22
+ # Contains functions for running essential commands and
23
+ # defining app level constants
24
+ module Serinette
25
+ OUTPUT_FILE_NAME = 'output'
26
+ TMP_DIR = './tmp/'
27
+ SOUND_FILE_TYPE = '.wav'
28
+
29
+ # Returns pull path of output file from project root
30
+ def self.output_path
31
+ Serinette::TMP_DIR +
32
+ Serinette::OUTPUT_FILE_NAME +
33
+ Serinette::SOUND_FILE_TYPE
34
+ end
35
+
36
+ # Removes all files in TMP_DIR of type SOUND_FILE_TYPE
37
+ def self.clean
38
+ FileUtils.rm_rf(Dir.glob("#{TMP_DIR}*#{SOUND_FILE_TYPE}"))
39
+ end
40
+
41
+ # Primary function for creating and outputing new songs
42
+ def self.orchestrate
43
+ clean
44
+
45
+ sox = Sox::Cmd.new(combine: :mix)
46
+
47
+ song = Song.new
48
+ song.configure_command(sox)
49
+ puts "Running sox with: #{sox.to_s}"
50
+
51
+ sox.run
52
+ end
53
+
54
+ # Basic Serinette error:
55
+ class Error < StandardError
56
+ end
57
+ end
@@ -0,0 +1,7 @@
1
+ module Serinette
2
+ class FileName
3
+ def self.generate
4
+ "#{Serinette::TMP_DIR}#{SecureRandom.uuid}#{Serinette::SOUND_FILE_TYPE}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ module Serinette
2
+ # Provides Sox helper functions
3
+ module SoxWrapper
4
+ # Takes options, generates a configured Sox::Cmd, and runs
5
+ #
6
+ # @params [Hash] options hash
7
+ # @returns [Sox::Cmd] Sox command with all configurations
8
+ def self.generate_and_run(options = {})
9
+ setup(options, true)
10
+ end
11
+
12
+ # Takes options and generates a configured Sox::Cmd
13
+ #
14
+ # @params [Hash] options hash
15
+ # @returns [Sox::Cmd] Sox commands with all configurations
16
+ def self.generate(options = {})
17
+ setup(options, false)
18
+ end
19
+
20
+ private
21
+
22
+ # Internal function for setting up and running (if should_run) sox command
23
+ def self.setup(options, should_run)
24
+ options[:global_options] ||= {}
25
+ options[:input] ||= '-n'
26
+ options[:output] ||= FileName.generate
27
+ options[:effects] ||= {}
28
+
29
+ options[:effects].merge! Song::default_effects
30
+
31
+ sox = Sox::Cmd.new options[:global_options]
32
+
33
+ sox.add_input(options[:input])
34
+ sox.set_effects(options[:effects])
35
+ sox.set_output(options[:output])
36
+
37
+ sox.run if should_run
38
+ sox
39
+ end
40
+ end
41
+ end
data/serinette.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "serinette/version"
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'serinette'
6
+ gem.version = Serinette::VERSION
7
+ gem.summary = "Random music from the command line."
8
+ gem.description = "Serinette is a library with a strongly opionated approach to generating random music from the command line."
9
+ gem.authors = ['Jared Clifton', 'Austin Gatlin']
10
+ gem.email = 'jaredcraigclifton@gmail.com'
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ gem.require_paths = ['lib']
15
+ gem.homepage = 'http://jclif.github.io/serinette/'
16
+ gem.license = 'MIT'
17
+
18
+ gem.required_ruby_version = '>= 1.9.3'
19
+
20
+ gem.add_dependency 'cliver', '0.3.2'
21
+ gem.add_dependency 'ruby-sox', '0.0.3'
22
+
23
+ gem.add_development_dependency 'rake', '10.4.2'
24
+ gem.add_development_dependency 'rspec', '3.2.0'
25
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::Effect do
4
+ describe '.sample_effects_classes' do
5
+ it 'should exist' do
6
+ expect(Serinette::Effect).to respond_to(:sample_effects_classes)
7
+ end
8
+ end
9
+
10
+ context 'given an effect' do
11
+ subject(:effect) { Serinette::Effect.new }
12
+ it 'should have a method #to_sox_key_pair' do
13
+ expect(effect).to respond_to(:to_sox_key_pair)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::Descendents do
4
+ let(:klass) { Class.new { include Serinette::Descendents } }
5
+
6
+ context 'a class method #descendents' do
7
+ it 'should exist in classes which include the module' do
8
+ expect(klass).to respond_to(:descendents)
9
+ end
10
+
11
+ it 'should return a classes descendents' do
12
+ class Subklass1 < klass; end
13
+ class Subklass2 < klass; end
14
+ expect(klass.descendents - [Subklass1, Subklass2]).to eq []
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::SoxOptions do
4
+ context 'a method #randomize_options_as_string' do
5
+ it 'should exist' do
6
+ expect(Serinette::SoxOptions).to respond_to(:randomize_options_as_string)
7
+ end
8
+ end
9
+
10
+ context 'a method #stringify_option' do
11
+ it 'should exist' do
12
+ expect(Serinette::SoxOptions).to respond_to(:stringify_option)
13
+ end
14
+
15
+ it 'should return correct string when given type: :flag' do
16
+ good_option1 = {
17
+ type: :flag,
18
+ name: 'wet-only',
19
+ value: '-w'
20
+ }
21
+
22
+ allow_any_instance_of(Array).to receive(:sample).and_return(true)
23
+ expect(Serinette::SoxOptions.stringify_option(good_option1)).to eq('-w')
24
+ end
25
+
26
+ it 'should return correct string when given type: :trait as range' do
27
+ good_option2 = {
28
+ type: :trait,
29
+ name: 'reverberance',
30
+ value: (0..100),
31
+ default: 50
32
+ }
33
+
34
+ result = Serinette::SoxOptions.stringify_option(good_option2)
35
+
36
+ expect(result).not_to be_nil
37
+ expect(result.to_i).to be_between(0, 100)
38
+ end
39
+
40
+ it 'should return correct string when given type: :trait as array' do
41
+ good_option2 = {
42
+ type: :trait,
43
+ name: 'reverberance',
44
+ value: [0, 50, 100],
45
+ default: 50
46
+ }
47
+
48
+ allow_any_instance_of(Array).to receive(:sample).and_return(50)
49
+
50
+ result = Serinette::SoxOptions.stringify_option(good_option2)
51
+
52
+ expect(result).not_to be_nil
53
+ expect(result.to_i).to eq(50)
54
+ end
55
+
56
+ it 'should return correct string when given type: :trait as proc'
57
+
58
+ it 'should fail when type is bad' do
59
+ bad_option = {
60
+ type: :bad,
61
+ name: 'wet-only',
62
+ value: '-w'
63
+ }
64
+
65
+ error = Serinette::Error
66
+ expect { Serinette::SoxOptions.stringify_option(bad_option) }.to raise_error(error)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::Noise do
4
+ it 'should have a method #sample_noise_classes' do
5
+ expect(Serinette::Noise).to respond_to(:sample_noise_classes)
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::ReverbEffect do
4
+ subject(:reverb_effect) { Serinette::ReverbEffect.new }
5
+
6
+ it 'should inherit from Effect' do
7
+ Serinette::ReverbEffect.ancestors.include?(Serinette::Effect)
8
+ end
9
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::Song do
4
+ it 'should have a constant TRACK_NUMBER' do
5
+ expect(Serinette::Song).to have_constant(:TRACK_NUMBER)
6
+ end
7
+
8
+ it 'should have a constant EFFECT_NUMBER' do
9
+ expect(Serinette::Song).to have_constant(:EFFECT_NUMBER)
10
+ end
11
+
12
+ it 'should have a constant CHANNELS' do
13
+ expect(Serinette::Song).to have_constant(:CHANNELS)
14
+ end
15
+
16
+ it 'should have a constant RATE' do
17
+ expect(Serinette::Song).to have_constant(:RATE)
18
+ end
19
+
20
+ describe '.default_effects' do
21
+ it 'should exist' do
22
+ expect(Serinette::Song).to respond_to(:default_effects)
23
+ end
24
+
25
+ it 'should get output path' do
26
+ default_effects = { rate: Serinette::Song::RATE, channels: Serinette::Song::CHANNELS }
27
+ expect(Serinette::Song.default_effects).to eq default_effects
28
+ end
29
+ end
30
+
31
+ context 'given a song' do
32
+ subject(:song) { Serinette::Song.new }
33
+
34
+ it 'should have correct number of effects' do
35
+ expect(song.effects.length).to be Serinette::Song::EFFECT_NUMBER
36
+ end
37
+
38
+ it 'should have correct number of tracks' do
39
+ expect(song.tracks.length).to be Serinette::Song::TRACK_NUMBER
40
+ end
41
+
42
+ it 'should have a variable duration' do
43
+ expect(song.duration).to be_a Fixnum
44
+ end
45
+
46
+ it 'should have a duration less than three minutes' do
47
+ expect(song.duration).to be <= 180
48
+ end
49
+
50
+ it 'should have a duration more than one minute' do
51
+ expect(song.duration).to be >= 60
52
+ end
53
+
54
+ it 'should have tracks' do
55
+ expect(song.tracks).to be_a Array
56
+ end
57
+
58
+ it 'should have effects' do
59
+ expect(song.effects).to be_a Array
60
+ end
61
+
62
+ it 'should have correct number of tracks' do
63
+ expect(song.tracks.length).to be Serinette::Song::TRACK_NUMBER
64
+ song.tracks.each do |track|
65
+ expect(track).to be_a Serinette::Track
66
+ end
67
+ end
68
+
69
+ it 'should call render on all tracks' do
70
+ song.tracks.each do |track|
71
+ expect(track).to receive(:render)
72
+ end
73
+
74
+ song.render
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::SourceNoise do
4
+
5
+ it 'should inherit from Noise' do
6
+ expect(Serinette::SourceNoise.ancestors.include?(Serinette::Noise)).to eq(true)
7
+ end
8
+
9
+ context 'given a source noise' do
10
+ subject(:source_noise) { Serinette::SourceNoise.new }
11
+
12
+ it 'should have a wavefile' do
13
+ expect(source_noise.wavefile).to be_a String
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::SynthNoise do
4
+ it 'should have a constant SOX_OPTIONS_CONFIG' do
5
+ expect(Serinette::SynthNoise).to have_constant(:SOX_OPTIONS_CONFIG)
6
+ end
7
+
8
+ it 'should inherit from Noise' do
9
+ expect(Serinette::SynthNoise.ancestors.include?(Serinette::Noise)).to eq(true)
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::Track do
4
+ context 'given a track' do
5
+ subject(:track) { Serinette::Track.new }
6
+
7
+ it 'should have a noise' do
8
+ expect(track.noise).to be_a Serinette::Noise
9
+ end
10
+
11
+ it 'should have method render that creates a tem file and returns the path' do
12
+ file_name = track.render
13
+ expect(File.exist?(file_name)).to be true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::FileName do
4
+ context 'a class method #generate' do
5
+ it 'should exist' do
6
+ expect(Serinette::FileName).to respond_to(:generate)
7
+ end
8
+
9
+ it 'should return a string' do
10
+ expect(Serinette::FileName.generate).to be_a String
11
+ end
12
+
13
+ it 'should be random'
14
+
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette::SoxWrapper do
4
+ context 'a class method #generate' do
5
+ it 'should exist' do
6
+ expect(Serinette::SoxWrapper).to respond_to(:generate)
7
+ end
8
+
9
+ it 'should return a Sox::Cmd' do
10
+ expect(Serinette::SoxWrapper.generate).to be_a Sox::Cmd
11
+ end
12
+
13
+ it 'should configure the Sox::Cmd correctly'
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serinette do
4
+ it 'should have a variable called OUTPUT_FILE_NAME' do
5
+ expect(Serinette::OUTPUT_FILE_NAME).not_to be_nil
6
+ end
7
+
8
+ it 'should have a variable called TMP_DIR' do
9
+ expect(Serinette::TMP_DIR).not_to be_nil
10
+ end
11
+
12
+ it 'should have a variable called SOUND_FILE_TYPE' do
13
+ expect(Serinette::SOUND_FILE_TYPE).not_to be_nil
14
+ end
15
+
16
+ describe '.clean' do
17
+ it 'should exist' do
18
+ expect(Serinette).to respond_to(:clean)
19
+ end
20
+
21
+ it 'should remove all wav files from the tmp directory' do
22
+ file_name = "#{Serinette::TMP_DIR}test.#{Serinette::SOUND_FILE_TYPE}"
23
+ FileUtils.touch(file_name)
24
+ Serinette.clean
25
+ expect(File.exist?(file_name)).to be false
26
+ end
27
+ end
28
+
29
+ describe '.output_path' do
30
+ it 'should exist' do
31
+ expect(Serinette).to respond_to(:output_path)
32
+ end
33
+
34
+ it 'should get output path' do
35
+ path = Serinette::TMP_DIR +
36
+ Serinette::OUTPUT_FILE_NAME +
37
+ Serinette::SOUND_FILE_TYPE
38
+ expect(Serinette.output_path).to eq path
39
+ end
40
+ end
41
+
42
+ describe '.orchestrate' do
43
+ it 'should exist' do
44
+ expect(Serinette).to respond_to(:orchestrate)
45
+ end
46
+
47
+ it 'should output a file' do
48
+ Serinette.orchestrate
49
+ expect(File.exist?(Serinette.output_path)).to be true
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ # require 'coveralls'
2
+ require 'codeclimate-test-reporter'
3
+
4
+ # Coveralls.wear!
5
+ CodeClimate::TestReporter.start
6
+
7
+ require 'serinette'
8
+
9
+ # Custom Matchers
10
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define :have_constant do |const|
2
+ match do |owner|
3
+ owner.const_defined?(const)
4
+ end
5
+ end
data/tmp/.gitkeep ADDED
File without changes
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: serinette
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Jared Clifton
8
+ - Austin Gatlin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cliver
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '='
19
+ - !ruby/object:Gem::Version
20
+ version: 0.3.2
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '='
26
+ - !ruby/object:Gem::Version
27
+ version: 0.3.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: ruby-sox
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '='
33
+ - !ruby/object:Gem::Version
34
+ version: 0.0.3
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '='
40
+ - !ruby/object:Gem::Version
41
+ version: 0.0.3
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '='
47
+ - !ruby/object:Gem::Version
48
+ version: 10.4.2
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '='
54
+ - !ruby/object:Gem::Version
55
+ version: 10.4.2
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '='
61
+ - !ruby/object:Gem::Version
62
+ version: 3.2.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: 3.2.0
70
+ description: Serinette is a library with a strongly opionated approach to generating
71
+ random music from the command line.
72
+ email: jaredcraigclifton@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".bundle/config"
78
+ - ".gitignore"
79
+ - ".rspec"
80
+ - ".travis.yml"
81
+ - CHANGELOG.md
82
+ - CONTRIBUTING.md
83
+ - Gemfile
84
+ - LICENSE.txt
85
+ - README.md
86
+ - Rakefile
87
+ - data/sounds/haunting_voices.wav
88
+ - data/sounds/hypnotic_loop.wav
89
+ - dependencies.rb
90
+ - lib/mixins/descendents.rb
91
+ - lib/mixins/sox_options.rb
92
+ - lib/serinette.rb
93
+ - lib/serinette/effect.rb
94
+ - lib/serinette/noise.rb
95
+ - lib/serinette/reverb_effect.rb
96
+ - lib/serinette/song.rb
97
+ - lib/serinette/source_noise.rb
98
+ - lib/serinette/synth_noise.rb
99
+ - lib/serinette/track.rb
100
+ - lib/serinette/version.rb
101
+ - lib/utils/file_name.rb
102
+ - lib/utils/sox_wrapper.rb
103
+ - serinette.gemspec
104
+ - spec/serinette/effect_spec.rb
105
+ - spec/serinette/mixins/descendents_spec.rb
106
+ - spec/serinette/mixins/sox_options_spec.rb
107
+ - spec/serinette/noise_spec.rb
108
+ - spec/serinette/reverb_effect_spec.rb
109
+ - spec/serinette/song_spec.rb
110
+ - spec/serinette/source_noise_spec.rb
111
+ - spec/serinette/synth_noise_spec.rb
112
+ - spec/serinette/track_spec.rb
113
+ - spec/serinette/utils/file_name_spec.rb
114
+ - spec/serinette/utils/sox_wrapper_spec.rb
115
+ - spec/serinette_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/support/matchers/have_constant.rb
118
+ - tmp/.gitkeep
119
+ homepage: http://jclif.github.io/serinette/
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 1.9.3
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">"
135
+ - !ruby/object:Gem::Version
136
+ version: 1.3.1
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.4.5
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Random music from the command line.
143
+ test_files:
144
+ - spec/serinette/effect_spec.rb
145
+ - spec/serinette/mixins/descendents_spec.rb
146
+ - spec/serinette/mixins/sox_options_spec.rb
147
+ - spec/serinette/noise_spec.rb
148
+ - spec/serinette/reverb_effect_spec.rb
149
+ - spec/serinette/song_spec.rb
150
+ - spec/serinette/source_noise_spec.rb
151
+ - spec/serinette/synth_noise_spec.rb
152
+ - spec/serinette/track_spec.rb
153
+ - spec/serinette/utils/file_name_spec.rb
154
+ - spec/serinette/utils/sox_wrapper_spec.rb
155
+ - spec/serinette_spec.rb
156
+ - spec/spec_helper.rb
157
+ - spec/support/matchers/have_constant.rb
158
+ has_rdoc: