serinette 0.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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: