pocket_miku 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +84 -8
- data/lib/pocket_miku.rb +14 -132
- data/lib/pocket_miku/base.rb +2 -0
- data/lib/pocket_miku/chartable.rb +1 -1
- data/lib/pocket_miku/device.rb +99 -0
- data/lib/pocket_miku/exception.rb +1 -1
- data/lib/pocket_miku/literal.rb +11 -0
- data/lib/pocket_miku/note.rb +123 -0
- data/lib/pocket_miku/packet_factory.rb +15 -0
- data/lib/pocket_miku/score.rb +96 -0
- data/lib/pocket_miku/version.rb +2 -2
- data/spec/device_spec.rb +34 -0
- data/spec/note_spec.rb +86 -0
- data/spec/packet_factory_spec.rb +25 -0
- data/spec/score_spec.rb +40 -0
- metadata +28 -24
- data/spec/pocket_miku_spec.rb +0 -66
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 224a1798a6222b6fdfcdd075c49fc64f7b4371cd
|
4
|
+
data.tar.gz: bfc29cc865f43426d18aa17c434c9b5e9386e823
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0480a64f8a7b28ae31ee6a6499ce0ba3de4a980398c90659f043a8fa0798b97808c98fe944d8066e9f79d7dcd72c17863368ddafb33944095da0d6f3851e12df
|
7
|
+
data.tar.gz: 17bcbffc83eaac450af442fa9beb127d9aa0ca03ca26b0407842f19d516450ec81b4ad75981c039d46cefef094521c44bb84fd38b31cd2819dc90dd3da3c625f
|
data/README.md
CHANGED
@@ -26,21 +26,97 @@ Or install it yourself as:
|
|
26
26
|
|
27
27
|
```ruby
|
28
28
|
require 'pocket_miku'
|
29
|
-
PocketMiku.
|
30
|
-
|
31
|
-
|
32
|
-
ぼ(82,127)
|
33
|
-
sleep 0.12
|
34
|
-
stop
|
29
|
+
PocketMiku.sing '/dev/midi2' do
|
30
|
+
tempo 240
|
31
|
+
ふぁ 75; ぼ 82
|
35
32
|
end
|
36
33
|
```
|
37
34
|
|
38
|
-
|
35
|
+
*ふぁ (音階)* などと書くと、その高さで「ふぁ」と発音します。ポケット・ミク付属の『ユーザーズマニュアル』の裏に掲載されている『ポケット・ミク デフォルト文字テーブル』に書いてある文字が全て使用できます。
|
39
36
|
なお、「ん」は「N\」のエイリアスです。ほかの「ん」を使用する時は、同備考欄の文字(ダブルクォートは不要)を指定してください。
|
37
|
+
### 休符
|
38
|
+
「っ」です。引数は数値一つで、「長さ」(後述)です。
|
39
|
+
|
40
|
+
### ノートの追加パラメータ
|
41
|
+
音毎に追加パラメータを設定できます。追加パラメータを利用する場合、例えば *あ 60* を *あ key:60* と書き換え、
|
42
|
+
その後に *, 追加パラメータ名: 値* と書きます。
|
43
|
+
|
44
|
+
#### 長さ
|
45
|
+
音の長さを指定するには、 *length: [長さ]* と指定します。
|
46
|
+
長さは、32分音符が1で、長さが倍になると倍になります。又、以下の定数も用意されています。
|
47
|
+
|
48
|
+
|定数|意味|
|
49
|
+
|:-:|:-|
|
50
|
+
|PocketMiku::Note1|全音符|
|
51
|
+
|PocketMiku::Note2|2分音符|
|
52
|
+
|PocketMiku::Note4|4分音符|
|
53
|
+
|PocketMiku::Note8|8分音符|
|
54
|
+
|PocketMiku::Note16|16分音符|
|
55
|
+
|PocketMiku::Note32|32分音符|
|
56
|
+
|
57
|
+
以下のサンプルでは見やすさのために変数を使ってます。
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
PocketMiku.sing '/dev/midi2' do
|
61
|
+
f8 = PocketMiku::Note8
|
62
|
+
f16 = PocketMiku::Note16
|
63
|
+
|
64
|
+
ま key: 79, length: f8; っ f16; る key: 79, length: f16
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
#### ベロシティ
|
69
|
+
音の強さは、 *velocity: [0..127]* のように指定します。0から127まで、数字が大きいほうが音が大きくなります。
|
70
|
+
さっきのサンプルにベロシティを追加してみました。
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
PocketMiku.sing '/dev/midi2' do
|
74
|
+
f8 = PocketMiku::Note8
|
75
|
+
f16 = PocketMiku::Note16
|
76
|
+
|
77
|
+
ま key: 79, length: f8; っ f16; る key: 79, length: f16, velocity: 80
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
### 楽譜パラメータ
|
82
|
+
|
83
|
+
#### デフォルト値
|
84
|
+
*default.velocity = 100* などと書くと、これ以降の音符のデフォルト値が設定できます。
|
85
|
+
|
86
|
+
|定義|意味|
|
87
|
+
|:-|:-|
|
88
|
+
|default.key = |音の高さ|
|
89
|
+
|default.length|長さ|
|
90
|
+
|default.velocity|ベロシティ|
|
91
|
+
|
92
|
+
#### テンポ
|
93
|
+
*tempo 100* などと書くと、これ以降のテンポを変更できます。曲のテンポを設定する時は最初に書いてください。
|
94
|
+
テンポの数字は、1分間に4分音符がいくつ入るか、です。
|
95
|
+
|
96
|
+
先ほどのサンプルにテンポをつけてみました
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
PocketMiku.sing '/dev/midi2' do
|
100
|
+
tempo 80
|
101
|
+
f8 = PocketMiku::Note8
|
102
|
+
f16 = PocketMiku::Note16
|
103
|
+
|
104
|
+
ま key: 79, length: f8; っ f16; る key: 79, length: f16, velocity: 80
|
105
|
+
た key: 79, length: f8; っ f16; け key: 79, length: f16, velocity: 80
|
106
|
+
え key: 79, length: f8; っ f16; べ key: 77, length: f16, velocity: 80
|
107
|
+
す key: 79, length: f8; っ f16; に key: 77, length: f16, velocity: 80
|
108
|
+
お key: 79, length: f8; っ f16; し key: 79, length: f16, velocity: 80
|
109
|
+
お key: 74, length: f8; っ f16; い key: 74, length: f16, velocity: 80
|
110
|
+
け key: 74, length: f8 * 1.5
|
111
|
+
っ f16 + PocketMiku::Note4;
|
112
|
+
end
|
113
|
+
```
|
40
114
|
|
41
115
|
## そのうち
|
42
116
|
|
43
|
-
-
|
117
|
+
- 音の同期処理本当にどうしよう
|
118
|
+
- ピッチベンド実装したい
|
119
|
+
- 発音中に音のパラメータを操作したい(だんだん高く、だんだん弱く等)
|
44
120
|
|
45
121
|
## Contributing
|
46
122
|
|
data/lib/pocket_miku.rb
CHANGED
@@ -1,141 +1,23 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require "pocket_miku/version"
|
3
|
-
require "pocket_miku/
|
3
|
+
require "pocket_miku/literal"
|
4
4
|
require "pocket_miku/exception"
|
5
|
+
require "pocket_miku/packet_factory"
|
6
|
+
require "pocket_miku/base"
|
7
|
+
require "pocket_miku/chartable"
|
8
|
+
require "pocket_miku/note"
|
9
|
+
require "pocket_miku/score"
|
10
|
+
require "pocket_miku/device"
|
5
11
|
require 'stringio'
|
12
|
+
require 'rational'
|
6
13
|
|
7
|
-
|
8
|
-
|
9
|
-
attr_reader :sound, :key, :velocity
|
10
|
-
|
11
|
-
# ==== Args
|
12
|
-
# [device]
|
13
|
-
# - String :: MIDIデバイスファイル名
|
14
|
-
# - IO,StringIO :: 出力するIOオブジェクト
|
15
|
-
# ==== Exception
|
16
|
-
# PocketMiku::ArgumentError
|
17
|
-
# ファイル device が見つからず、ストリームとしても利用できない場合
|
18
|
-
def initialize(device)
|
19
|
-
@key = 60
|
20
|
-
@velocity = 100
|
21
|
-
@io = case device
|
22
|
-
when IO,StringIO
|
23
|
-
device
|
24
|
-
when String
|
25
|
-
open(device, 'w')
|
26
|
-
else
|
27
|
-
raise PocketMiku::ArgumentError, "device should give IO or String. but give `#{device.class}'"
|
28
|
-
end
|
29
|
-
if block_given?
|
30
|
-
begin
|
31
|
-
self.instance_eval(&Proc.new)
|
32
|
-
ensure
|
33
|
-
@io.close
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
14
|
+
module PocketMiku
|
15
|
+
extend self
|
37
16
|
|
38
|
-
#
|
17
|
+
# ポケットミクに歌わせるためのショートカットメソッド
|
39
18
|
# ==== Args
|
40
|
-
# [
|
41
|
-
|
42
|
-
|
43
|
-
# ==== Exception
|
44
|
-
# PocketMiku::InvalidByteError
|
45
|
-
# packetの中に、1byte(0..255)に収まらない数値がある場合
|
46
|
-
# ==== Return
|
47
|
-
# self
|
48
|
-
def send(packet)
|
49
|
-
@io << pack(packet)
|
50
|
-
@io.flush
|
19
|
+
# [device] String|IO デバイスファイルの場所
|
20
|
+
def sing(device)
|
21
|
+
PocketMiku::Device.new(device, &Proc.new)
|
51
22
|
end
|
52
|
-
|
53
|
-
# ポケットミクに発音させる情報をセットする
|
54
|
-
# ==== Args
|
55
|
-
# [key] Integer 音程
|
56
|
-
# [velocity] Integer 音の強さ
|
57
|
-
# [sound] Integer|Symbol 文字テーブルの文字コード(Integer)か文字(Symbol)
|
58
|
-
# ==== Return
|
59
|
-
# self
|
60
|
-
def set(options)
|
61
|
-
@key = byte_check(options[:key], "invalid key `%d'".freeze) if options[:key]
|
62
|
-
@velocity = byte_check(options[:velocity], "invalid velocity `%d'".freeze) if options[:velocity]
|
63
|
-
self.sound = options[:sound] if options[:sound]
|
64
|
-
self
|
65
|
-
end
|
66
|
-
|
67
|
-
# ポケットミクに発音させる文字テーブル情報をセットする
|
68
|
-
# ==== Args
|
69
|
-
# [new] Integer|Symbol セットする文字テーブルの文字コード(Integer)か文字(Symbol)
|
70
|
-
# ==== Exceptions
|
71
|
-
# PocketMiku::CharMappingError
|
72
|
-
# newが文字テーブルに存在しない場合
|
73
|
-
# PocketMiku::InvalidByteError
|
74
|
-
# newが1byte(0..255)に収まらない数値である場合
|
75
|
-
# ==== Return
|
76
|
-
# 新しい sound の値。Symbolをセットしても必ず数値になる。
|
77
|
-
def sound=(new)
|
78
|
-
case new
|
79
|
-
when Fixnum
|
80
|
-
@sound = byte_check(new, "invalid sound `%d'".freeze)
|
81
|
-
when -> _ {CharTable.include? _}
|
82
|
-
@sound = CharTable[new]
|
83
|
-
else
|
84
|
-
raise CharMappingError, "unknown sound `#{new}'"
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# 設定されている情報でサウンドを再生開始する
|
89
|
-
# ==== Exception
|
90
|
-
# PocketMiku::InvalidByteError
|
91
|
-
# packetの中に、1byte(0..255)に収まらない数値がある場合
|
92
|
-
def play
|
93
|
-
send([0xF0, 0x43, 0x79, 0x09, 0x11, 0x0A, 0, @sound, 0xF7])
|
94
|
-
send([0x90, @key, @velocity])
|
95
|
-
end
|
96
|
-
alias +@ play
|
97
|
-
|
98
|
-
# 設定されているサウンドを再生停止する
|
99
|
-
# ==== Exception
|
100
|
-
# PocketMiku::InvalidByteError
|
101
|
-
# packetの中に、1byte(0..255)に収まらない数値がある場合
|
102
|
-
def stop
|
103
|
-
send([0x80, @key, 0])
|
104
|
-
end
|
105
|
-
alias -@ stop
|
106
|
-
|
107
|
-
alias sing instance_eval
|
108
|
-
|
109
|
-
def method_missing(method, *args)
|
110
|
-
case method
|
111
|
-
when -> _ {CharTable.include? _}
|
112
|
-
set(key: args[0], velocity: args[1], sound: CharTable[method]).play
|
113
|
-
else
|
114
|
-
super
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def close
|
119
|
-
stop
|
120
|
-
@io.close
|
121
|
-
end
|
122
|
-
|
123
|
-
def closed?
|
124
|
-
@io.closed?
|
125
|
-
end
|
126
|
-
|
127
|
-
private
|
128
|
-
|
129
|
-
def pack(bytes)
|
130
|
-
bytes = [bytes] if bytes.is_a? Integer
|
131
|
-
bytes.each(&method(:byte_check))
|
132
|
-
bytes.pack "C*".freeze
|
133
|
-
end
|
134
|
-
|
135
|
-
def byte_check(byte, error_message="byte should 0...255 but give `%d'".freeze)
|
136
|
-
raise InvalidByteError, "`#{byte}' is not integer." unless byte.is_a? Integer
|
137
|
-
raise InvalidByteError, error_message % byte unless (0..0xFF).include?(byte)
|
138
|
-
byte
|
139
|
-
end
|
140
|
-
|
141
23
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module PocketMiku
|
4
|
+
class Device < PocketMiku::Base
|
5
|
+
|
6
|
+
# ==== Args
|
7
|
+
# [device]
|
8
|
+
# - String :: MIDIデバイスファイル名
|
9
|
+
# - IO,StringIO :: 出力するIOオブジェクト
|
10
|
+
# ==== Exception
|
11
|
+
# PocketMiku::ArgumentError
|
12
|
+
# ファイル device が見つからず、ストリームとしても利用できない場合
|
13
|
+
def initialize(device)
|
14
|
+
@io = case device
|
15
|
+
when IO,StringIO
|
16
|
+
device
|
17
|
+
when String
|
18
|
+
open(device, 'w')
|
19
|
+
else
|
20
|
+
raise PocketMiku::ArgumentError, "device should give IO or String. but give `#{device.class}'"
|
21
|
+
end
|
22
|
+
if block_given?
|
23
|
+
begin
|
24
|
+
self.sing(&Proc.new)
|
25
|
+
ensure
|
26
|
+
@io.close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# ポケットミクに直接MIDIパケットを送る
|
32
|
+
# ==== Args
|
33
|
+
# [packet]
|
34
|
+
# - Array :: バイト値配列をpack("C*")して送る
|
35
|
+
# - Integer :: 対応するキャラクタを送る
|
36
|
+
# ==== Exception
|
37
|
+
# PocketMiku::InvalidByteError
|
38
|
+
# packetの中に、1byte(0..255)に収まらない数値がある場合
|
39
|
+
# ==== Return
|
40
|
+
# self
|
41
|
+
def send(packet)
|
42
|
+
@io << PocketMiku::PacketFactory.pack(packet)
|
43
|
+
@io.flush
|
44
|
+
end
|
45
|
+
|
46
|
+
# score をこのデバイスで再生する
|
47
|
+
# ==== Array
|
48
|
+
# [score] PocketMiku::Score 楽譜情報
|
49
|
+
# ==== Exception
|
50
|
+
# PocketMiku::InvalidByteError
|
51
|
+
# packetの中に、1byte(0..255)に収まらない数値がある場合
|
52
|
+
def play(score)
|
53
|
+
score.map{|note|[note, note.to_s.freeze]}.each do |note, packet|
|
54
|
+
case note
|
55
|
+
when RestNote
|
56
|
+
sleep Rational(60.to_f, score.tempo) * Rational(note.length.to_f, Note4)
|
57
|
+
when Note
|
58
|
+
@io << packet
|
59
|
+
@io.flush
|
60
|
+
sleep Rational(60.to_f, score.tempo) * Rational(note.length.to_f, Note4)
|
61
|
+
stop 0, note.key
|
62
|
+
@io.flush
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
alias +@ play
|
67
|
+
|
68
|
+
# 特定のトラックとキーのサウンドを再生停止する
|
69
|
+
# ==== Args
|
70
|
+
# [track] トラック番号
|
71
|
+
# [key] 音程
|
72
|
+
# ==== Exception
|
73
|
+
# PocketMiku::InvalidByteError
|
74
|
+
# packetの中に、1byte(0..255)に収まらない数値がある場合
|
75
|
+
def stop(track, key)
|
76
|
+
send([0x80 + track, key, 0])
|
77
|
+
end
|
78
|
+
alias -@ stop
|
79
|
+
|
80
|
+
# ブロックをこのインスタンスのコンテキストで実行してから、ポケットミクで再生する
|
81
|
+
# ==== Array
|
82
|
+
# [score] PocketMiku::Score 楽譜情報
|
83
|
+
# ==== Return
|
84
|
+
# ブロックの評価結果
|
85
|
+
def sing(score=nil)
|
86
|
+
score = PocketMiku::Score.new(&Proc.new) if block_given?
|
87
|
+
play score
|
88
|
+
end
|
89
|
+
|
90
|
+
def close
|
91
|
+
#stop
|
92
|
+
@io.close
|
93
|
+
end
|
94
|
+
|
95
|
+
def closed?
|
96
|
+
@io.closed?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module PocketMiku
|
4
|
+
class Note < PocketMiku::Base
|
5
|
+
attr_reader :sound, :key, :velocity, :pitchbend, :length
|
6
|
+
|
7
|
+
# ==== Args
|
8
|
+
# [options]
|
9
|
+
# - sound :: 発音する文字テーブルコード
|
10
|
+
# - key :: 音程 (0-127)
|
11
|
+
# - velocity :: 強さ (0-127)
|
12
|
+
# - pitchvend :: ピッチベンド
|
13
|
+
# - length :: 音の長さ(相対)
|
14
|
+
def initialize(options={})
|
15
|
+
self.sound = options[:sound]
|
16
|
+
self.key = options[:key]
|
17
|
+
self.velocity = options[:velocity]
|
18
|
+
self.pitchbend = options[:pitchbend]
|
19
|
+
self.length = options[:length]
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
PocketMiku::PacketFactory.pack(to_a)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_a
|
27
|
+
[0xF0, 0x43, 0x79, 0x09, 0x11, 0x0A, 0, sound, 0xF7,
|
28
|
+
0x90, key, velocity]
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
{ sound: sound,
|
33
|
+
key: key,
|
34
|
+
velocity: velocity,
|
35
|
+
pitchbend: pitchbend,
|
36
|
+
length: length }
|
37
|
+
end
|
38
|
+
|
39
|
+
# ポケットミクに発音させる文字テーブル情報をセットする
|
40
|
+
# ==== Args
|
41
|
+
# [new] Integer|Symbol セットする文字テーブルの文字コード(Integer)か文字(Symbol)
|
42
|
+
# ==== Exceptions
|
43
|
+
# PocketMiku::CharMappingError
|
44
|
+
# newが文字テーブルに存在しない場合
|
45
|
+
# PocketMiku::InvalidByteError
|
46
|
+
# newが1byte(0..255)に収まらない数値である場合
|
47
|
+
# ==== Return
|
48
|
+
# 新しい sound の値。Symbolをセットしても必ず数値になる。
|
49
|
+
def sound=(new)
|
50
|
+
case new
|
51
|
+
when Fixnum
|
52
|
+
raise CharMappingError, "sound out of range 0..127" unless (0..127).include? new
|
53
|
+
@sound = new
|
54
|
+
when -> _ {CharTable.include? _}
|
55
|
+
@sound = CharTable[new]
|
56
|
+
else
|
57
|
+
raise CharMappingError, "unknown sound `#{new}'"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# ポケットミクに発音させる音程をセットする
|
62
|
+
# ==== Args
|
63
|
+
# [new] Integer 設定する音程(0-127)
|
64
|
+
# ==== Exceptions
|
65
|
+
# PocketMiku::ArgumentError
|
66
|
+
# new 0-127の範囲外
|
67
|
+
# ==== Return
|
68
|
+
# new
|
69
|
+
def key=(new)
|
70
|
+
raise ArgumentError, "key out of range 0..127" unless (0..127).include? new
|
71
|
+
@key = new
|
72
|
+
end
|
73
|
+
|
74
|
+
# ポケットミクに発音させるベロシティをセットする
|
75
|
+
# ==== Args
|
76
|
+
# [new] Integer 設定するベロシティ(0-127)
|
77
|
+
# ==== Exceptions
|
78
|
+
# PocketMiku::ArgumentError
|
79
|
+
# new 0-127の範囲外
|
80
|
+
# ==== Return
|
81
|
+
# new
|
82
|
+
def velocity=(new)
|
83
|
+
raise ArgumentError, "velocity out of range 0..127" unless (0..127).include? new
|
84
|
+
@velocity = new
|
85
|
+
end
|
86
|
+
|
87
|
+
# ポケットミクに発音させるピッチベンドをセットする
|
88
|
+
# ==== Args
|
89
|
+
# [new] Integer 設定するピッチベンド(-8192 - 8191)
|
90
|
+
# ==== Exceptions
|
91
|
+
# PocketMiku::ArgumentError
|
92
|
+
# new -8192 - 8191の範囲外
|
93
|
+
# ==== Return
|
94
|
+
# new
|
95
|
+
def pitchbend=(new)
|
96
|
+
raise ArgumentError, "pitchbend out of range -8192..8191" unless (-8192..8191).include? new
|
97
|
+
@pitchbend = new
|
98
|
+
end
|
99
|
+
|
100
|
+
# ポケットミクにこの音を発音する長さをセットする
|
101
|
+
# ==== Args
|
102
|
+
# [new] Integer 設定する長さ(1以上)
|
103
|
+
# ==== Exceptions
|
104
|
+
# PocketMiku::ArgumentError
|
105
|
+
# new -8192 - 8191の範囲外
|
106
|
+
# ==== Return
|
107
|
+
# new
|
108
|
+
def length=(new)
|
109
|
+
raise ArgumentError, "length must than 1" unless 1 <= new
|
110
|
+
@length = new
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class RestNote < Note
|
115
|
+
def initialize(length)
|
116
|
+
super(sound:0, key:0, velocity:0, pitchbend:0, length: length)
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_a
|
120
|
+
[]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PocketMiku::PacketFactory
|
2
|
+
extend self
|
3
|
+
def pack(bytes)
|
4
|
+
bytes = [bytes] if bytes.is_a? Integer
|
5
|
+
bytes.each(&PocketMiku::PacketFactory.method(:byte_check))
|
6
|
+
bytes.pack "C*".freeze
|
7
|
+
end
|
8
|
+
|
9
|
+
def byte_check(byte, error_message="byte should 0...255 but give `%d'".freeze)
|
10
|
+
raise PocketMiku::InvalidByteError, "`#{byte}' is not integer." unless byte.is_a? Integer
|
11
|
+
raise PocketMiku::InvalidByteError, error_message % byte unless (0..0xFF).include?(byte)
|
12
|
+
byte
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module PocketMiku
|
4
|
+
class Score < PocketMiku::Base
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :default
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@default = PocketMiku::Note.new(sound: 0,
|
11
|
+
key: 60,
|
12
|
+
velocity: 100,
|
13
|
+
pitchbend: 0,
|
14
|
+
length: QuarterNote)
|
15
|
+
@playlist = []
|
16
|
+
@tempo = 120
|
17
|
+
if block_given?
|
18
|
+
record(&Proc.new)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
alias record instance_eval
|
23
|
+
|
24
|
+
def to_a
|
25
|
+
@playlist.dup
|
26
|
+
end
|
27
|
+
|
28
|
+
def each
|
29
|
+
@playlist.each(&Proc.new)
|
30
|
+
end
|
31
|
+
|
32
|
+
# 再生速度を取得/設定する。テンポの値は、1分間に再生する四分音符の数
|
33
|
+
# ==== Args
|
34
|
+
# [new]
|
35
|
+
# - nil(指定なし) :: 現在のテンポを返す
|
36
|
+
# - Integer :: テンポをこの値に設定
|
37
|
+
# ==== Return
|
38
|
+
# 現在のテンポ
|
39
|
+
def tempo(new=nil)
|
40
|
+
case new
|
41
|
+
when nil
|
42
|
+
@tempo
|
43
|
+
when Integer
|
44
|
+
@tempo = new
|
45
|
+
else
|
46
|
+
raise ArgumentError, "new mush nil or Integer but give `#{new.class}'"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# PocketMiku::Note を末尾に追加する
|
51
|
+
# ==== Args
|
52
|
+
# [note] PocketMiku::Note
|
53
|
+
# ==== Return
|
54
|
+
# self
|
55
|
+
def add(note)
|
56
|
+
@playlist << note
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Note を作成して、再生キューの末尾に追加する
|
61
|
+
# ==== Args
|
62
|
+
# [sound] Symbol|Integer 発音する文字テーブルの文字(Symbol)か番号(Integer)
|
63
|
+
# [options] 以下のいずれか
|
64
|
+
# - Integer :: 音の高さ(key)
|
65
|
+
# - Hash :: PocketMiku::Noteの第一引数
|
66
|
+
# 設定されたなかった Note のオプションは、 default の値が使われる
|
67
|
+
# ==== Return
|
68
|
+
# self
|
69
|
+
def generate_note(sound, options=nil)
|
70
|
+
add case options
|
71
|
+
when NilClass
|
72
|
+
PocketMiku::Note.new default.to_h.merge sound: sound
|
73
|
+
when Integer
|
74
|
+
PocketMiku::Note.new default.to_h.merge sound: sound, key: options
|
75
|
+
when Hash, -> _ {_.respond_to? :to_h}
|
76
|
+
PocketMiku::Note.new default.to_h.merge(options).merge sound: sound
|
77
|
+
else
|
78
|
+
raise ArgumentError, "options must nil, Integer, or Hash. but given `#{options.class}'"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def っ(length)
|
83
|
+
add PocketMiku::RestNote.new(length)
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_missing(method, *args)
|
87
|
+
case method
|
88
|
+
when -> _ {CharTable.include? _}
|
89
|
+
generate_note method, *args
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
data/lib/pocket_miku/version.rb
CHANGED
data/spec/device_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe PocketMiku::Device, "pocket miku" do
|
5
|
+
before do
|
6
|
+
@stream_body = String.new
|
7
|
+
@stream = StringIO.new(@stream_body, 'w')
|
8
|
+
@pocketmiku = PocketMiku::Device.new(@stream)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should send raw data" do
|
12
|
+
@pocketmiku.send [0, 128, 255]
|
13
|
+
expect(@stream_body.unpack('C*')).to eq([0, 128, 255])
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should error send out of range raw data" do
|
17
|
+
expect { @pocketmiku.send [257] }.to raise_error(PocketMiku::InvalidByteError)
|
18
|
+
expect { @pocketmiku.send [-1] }.to raise_error(PocketMiku::InvalidByteError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should play DSL Context" do
|
22
|
+
@pocketmiku.sing do
|
23
|
+
あ 60
|
24
|
+
end
|
25
|
+
expect(@stream_body.unpack('C*')).to eq([0xF0, 0x43, 0x79, 0x09, 0x11, 0x0A, 0, 0, 0xF7, 0x90, 60, 100, 0x80, 60, 0])
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should raise IOError try to output after close" do
|
29
|
+
expect(@pocketmiku.closed?).to eq false
|
30
|
+
@pocketmiku.close
|
31
|
+
expect(@pocketmiku.closed?).to eq true
|
32
|
+
expect { @pocketmiku.sing { あ 60 } }.to raise_error IOError
|
33
|
+
end
|
34
|
+
end
|
data/spec/note_spec.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe PocketMiku::Note, "pocket miku note" do
|
5
|
+
before do
|
6
|
+
@note = PocketMiku::Note.new(sound: 0,
|
7
|
+
key: 0,
|
8
|
+
velocity: 0,
|
9
|
+
pitchbend: 0,
|
10
|
+
length: 1)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should set sound parameter" do
|
14
|
+
expect { @note.sound=-1 }.to raise_error(PocketMiku::CharMappingError)
|
15
|
+
expect { @note.sound=128 }.to raise_error(PocketMiku::CharMappingError)
|
16
|
+
expect { @note.sound=:亜 }.to raise_error(PocketMiku::CharMappingError)
|
17
|
+
expect { @note.sound=Object }.to raise_error(PocketMiku::CharMappingError)
|
18
|
+
|
19
|
+
@note.sound=0
|
20
|
+
expect(@note.sound).to eq 0
|
21
|
+
|
22
|
+
@note.sound=127
|
23
|
+
expect(@note.sound).to eq 127
|
24
|
+
|
25
|
+
@note.sound=:あ
|
26
|
+
expect(@note.sound).to eq 0
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should set key parameter" do
|
30
|
+
expect { @note.key=-1 }.to raise_error(PocketMiku::ArgumentError)
|
31
|
+
expect { @note.key=128 }.to raise_error(PocketMiku::ArgumentError)
|
32
|
+
@note.key=0
|
33
|
+
expect(@note.key).to eq 0
|
34
|
+
@note.key=127
|
35
|
+
expect(@note.key).to eq 127
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should set velocity parameter" do
|
39
|
+
expect { @note.velocity=-1 }.to raise_error(PocketMiku::ArgumentError)
|
40
|
+
expect { @note.velocity=128 }.to raise_error(PocketMiku::ArgumentError)
|
41
|
+
@note.velocity=0
|
42
|
+
expect(@note.velocity).to eq 0
|
43
|
+
@note.velocity=127
|
44
|
+
expect(@note.velocity).to eq 127
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should set pitchbend parameter" do
|
48
|
+
expect { @note.pitchbend=-8193 }.to raise_error(PocketMiku::ArgumentError)
|
49
|
+
expect { @note.pitchbend=8192 }.to raise_error(PocketMiku::ArgumentError)
|
50
|
+
@note.pitchbend=-8192
|
51
|
+
expect(@note.pitchbend).to eq(-8192)
|
52
|
+
@note.pitchbend=8191
|
53
|
+
expect(@note.pitchbend).to eq 8191
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should set length parameter" do
|
57
|
+
expect { @note.length=0 }.to raise_error(PocketMiku::ArgumentError)
|
58
|
+
@note.length=1
|
59
|
+
expect(@note.length).to eq 1
|
60
|
+
@note.length=9999
|
61
|
+
expect(@note.length).to eq 9999
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should return sound packet" do
|
65
|
+
note = PocketMiku::Note.new(sound: 1,
|
66
|
+
key: 2,
|
67
|
+
velocity: 3,
|
68
|
+
pitchbend: 4,
|
69
|
+
length: 5)
|
70
|
+
expect(note.to_a).to eq([0xF0, 0x43, 0x79, 0x09, 0x11, 0x0A, 0, 1, 0xF7,
|
71
|
+
0x90, 2, 3])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should write out to Hash" do
|
75
|
+
note = PocketMiku::Note.new(sound: 1,
|
76
|
+
key: 2,
|
77
|
+
velocity: 3,
|
78
|
+
pitchbend: 4,
|
79
|
+
length: 5)
|
80
|
+
expect(note.to_h).to eq(sound: 1,
|
81
|
+
key: 2,
|
82
|
+
velocity: 3,
|
83
|
+
pitchbend: 4,
|
84
|
+
length: 5)
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe PocketMiku::PacketFactory, "pocket miku note" do
|
5
|
+
it "should pack array to string" do
|
6
|
+
result = PocketMiku::PacketFactory.pack([32])
|
7
|
+
expect(result.unpack('C*')).to eq [32]
|
8
|
+
|
9
|
+
result = PocketMiku::PacketFactory.pack(32)
|
10
|
+
expect(result.unpack('C*')).to eq [32]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should raise exception too big/small bytes" do
|
14
|
+
expect { PocketMiku::PacketFactory.pack([-1]) }.to raise_error(PocketMiku::InvalidByteError)
|
15
|
+
expect { PocketMiku::PacketFactory.pack([256]) }.to raise_error(PocketMiku::InvalidByteError)
|
16
|
+
PocketMiku::PacketFactory.pack([0])
|
17
|
+
PocketMiku::PacketFactory.pack([255])
|
18
|
+
|
19
|
+
expect { PocketMiku::PacketFactory.pack(-1) }.to raise_error(PocketMiku::InvalidByteError)
|
20
|
+
expect { PocketMiku::PacketFactory.pack(256) }.to raise_error(PocketMiku::InvalidByteError)
|
21
|
+
PocketMiku::PacketFactory.pack(0)
|
22
|
+
PocketMiku::PacketFactory.pack(255)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/spec/score_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe PocketMiku::Score, "pocket miku score" do
|
5
|
+
before do
|
6
|
+
@score = PocketMiku::Score.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should get/set default data" do
|
10
|
+
expect { @score.default.key = -1 }.to raise_error(PocketMiku::ArgumentError)
|
11
|
+
expect { @score.default.key = 128 }.to raise_error(PocketMiku::ArgumentError)
|
12
|
+
expect { @score.default.velocity = -1 }.to raise_error(PocketMiku::ArgumentError)
|
13
|
+
expect { @score.default.velocity = 128 }.to raise_error(PocketMiku::ArgumentError)
|
14
|
+
expect { @score.default.pitchbend = -8193 }.to raise_error(PocketMiku::ArgumentError)
|
15
|
+
expect { @score.default.pitchbend = 8192 }.to raise_error(PocketMiku::ArgumentError)
|
16
|
+
expect { @score.default.length = 0 }.to raise_error(PocketMiku::ArgumentError)
|
17
|
+
|
18
|
+
@score.default.key = 0
|
19
|
+
expect(@score.default.key).to eq 0
|
20
|
+
@score.default.key = 127
|
21
|
+
expect(@score.default.key).to eq 127
|
22
|
+
@score.default.velocity = 0
|
23
|
+
expect(@score.default.velocity).to eq 0
|
24
|
+
@score.default.velocity = 127
|
25
|
+
expect(@score.default.velocity).to eq 127
|
26
|
+
@score.default.pitchbend = -8192
|
27
|
+
expect(@score.default.pitchbend).to eq(-8192)
|
28
|
+
@score.default.pitchbend = 8191
|
29
|
+
expect(@score.default.pitchbend).to eq 8191
|
30
|
+
@score.default.length = 1
|
31
|
+
expect(@score.default.length).to eq 1
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should can recording" do
|
35
|
+
@score.record do
|
36
|
+
あ 60
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
metadata
CHANGED
@@ -1,62 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pocket_miku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Toshiaki Asai
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-05 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '1.5'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- - ~>
|
24
|
+
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '1.5'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rspec
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ">="
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - ">="
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
description: Rubyコードから「ポケットミク」を使ってミクを調教するためのライブラリです。
|
@@ -66,43 +59,54 @@ executables: []
|
|
66
59
|
extensions: []
|
67
60
|
extra_rdoc_files: []
|
68
61
|
files:
|
69
|
-
- .gitignore
|
62
|
+
- ".gitignore"
|
70
63
|
- Gemfile
|
71
64
|
- LICENSE.txt
|
72
65
|
- README.md
|
73
66
|
- Rakefile
|
74
67
|
- lib/pocket_miku.rb
|
68
|
+
- lib/pocket_miku/base.rb
|
75
69
|
- lib/pocket_miku/chartable.rb
|
70
|
+
- lib/pocket_miku/device.rb
|
76
71
|
- lib/pocket_miku/exception.rb
|
72
|
+
- lib/pocket_miku/literal.rb
|
73
|
+
- lib/pocket_miku/note.rb
|
74
|
+
- lib/pocket_miku/packet_factory.rb
|
75
|
+
- lib/pocket_miku/score.rb
|
77
76
|
- lib/pocket_miku/version.rb
|
78
77
|
- pocket_miku.gemspec
|
79
|
-
- spec/
|
78
|
+
- spec/device_spec.rb
|
79
|
+
- spec/note_spec.rb
|
80
|
+
- spec/packet_factory_spec.rb
|
81
|
+
- spec/score_spec.rb
|
80
82
|
- spec/spec_helper.rb
|
81
83
|
homepage: https://github.com/toshia/pocket_miku
|
82
84
|
licenses:
|
83
85
|
- MIT
|
86
|
+
metadata: {}
|
84
87
|
post_install_message:
|
85
88
|
rdoc_options: []
|
86
89
|
require_paths:
|
87
90
|
- lib
|
88
91
|
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
92
|
requirements:
|
91
|
-
- -
|
93
|
+
- - ">="
|
92
94
|
- !ruby/object:Gem::Version
|
93
95
|
version: '0'
|
94
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
-
none: false
|
96
97
|
requirements:
|
97
|
-
- -
|
98
|
+
- - ">="
|
98
99
|
- !ruby/object:Gem::Version
|
99
100
|
version: '0'
|
100
101
|
requirements: []
|
101
102
|
rubyforge_project:
|
102
|
-
rubygems_version:
|
103
|
+
rubygems_version: 2.2.2
|
103
104
|
signing_key:
|
104
|
-
specification_version:
|
105
|
+
specification_version: 4
|
105
106
|
summary: Play voice via PocketMiku
|
106
107
|
test_files:
|
107
|
-
- spec/
|
108
|
+
- spec/device_spec.rb
|
109
|
+
- spec/note_spec.rb
|
110
|
+
- spec/packet_factory_spec.rb
|
111
|
+
- spec/score_spec.rb
|
108
112
|
- spec/spec_helper.rb
|
data/spec/pocket_miku_spec.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe PocketMiku, "pocket miku" do
|
5
|
-
before do
|
6
|
-
@stream_body = String.new
|
7
|
-
@stream = StringIO.new(@stream_body, 'w')
|
8
|
-
@pocketmiku = PocketMiku.new(@stream)
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should send raw data" do
|
12
|
-
@pocketmiku.send [0, 128, 255]
|
13
|
-
expect(@stream_body.unpack('C*')).to eq([0, 128, 255])
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should error send out of range raw data" do
|
17
|
-
expect { @pocketmiku.send [257] }.to raise_error(PocketMiku::InvalidByteError)
|
18
|
-
expect { @pocketmiku.send [-1] }.to raise_error(PocketMiku::InvalidByteError)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should error set invalid data" do
|
22
|
-
expect { @pocketmiku.set(key: -1) }.to raise_error(PocketMiku::InvalidByteError)
|
23
|
-
expect { @pocketmiku.set(key: 257) }.to raise_error(PocketMiku::InvalidByteError)
|
24
|
-
expect { @pocketmiku.set(velocity: -1) }.to raise_error(PocketMiku::InvalidByteError)
|
25
|
-
expect { @pocketmiku.set(velocity: 257) }.to raise_error(PocketMiku::InvalidByteError)
|
26
|
-
expect { @pocketmiku.set(sound: -1) }.to raise_error(PocketMiku::InvalidByteError)
|
27
|
-
expect { @pocketmiku.set(sound: 257) }.to raise_error(PocketMiku::InvalidByteError)
|
28
|
-
expect { @pocketmiku.set(sound: :亜) }.to raise_error(PocketMiku::CharMappingError)
|
29
|
-
expect { @pocketmiku.set(sound: Object) }.to raise_error(PocketMiku::CharMappingError)
|
30
|
-
end
|
31
|
-
|
32
|
-
it "should error set invalid sound" do
|
33
|
-
expect { @pocketmiku.sound = -1 }.to raise_error(PocketMiku::InvalidByteError)
|
34
|
-
expect { @pocketmiku.sound = 257 }.to raise_error(PocketMiku::InvalidByteError)
|
35
|
-
expect { @pocketmiku.sound = :亜 }.to raise_error(PocketMiku::CharMappingError)
|
36
|
-
expect { @pocketmiku.sound = Object }.to raise_error(PocketMiku::CharMappingError)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should play current data" do
|
40
|
-
@pocketmiku.set(key: 100, velocity: 101, sound: 102)
|
41
|
-
expect(@stream_body.unpack('C*')).to eq []
|
42
|
-
@pocketmiku.play
|
43
|
-
expect(@stream_body.unpack('C*')).to eq [0xF0, 0x43, 0x79, 0x09, 0x11, 0x0A, 0, 102, 0xF7, 0x90, 100, 101]
|
44
|
-
@stream_body.clear
|
45
|
-
@stream.seek 0
|
46
|
-
@pocketmiku.stop
|
47
|
-
expect(@stream_body.unpack('C*')).to eq [0x80, 100, 0]
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should play DSL Context" do
|
51
|
-
@pocketmiku.sing {あ(60, 100) }
|
52
|
-
expect(@stream_body.unpack('C*')).to eq([0xF0, 0x43, 0x79, 0x09, 0x11, 0x0A, 0, 0, 0xF7, 0x90, 60, 100])
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should raise InvalidByteError try to play that not yet set sound" do
|
56
|
-
expect { @pocketmiku.play }.to raise_error PocketMiku::InvalidByteError
|
57
|
-
end
|
58
|
-
|
59
|
-
it "should raise IOError try to output after close" do
|
60
|
-
@pocketmiku.set(key: 100, velocity: 101, sound: 102)
|
61
|
-
expect(@pocketmiku.closed?).to eq false
|
62
|
-
@pocketmiku.close
|
63
|
-
expect(@pocketmiku.closed?).to eq true
|
64
|
-
expect { @pocketmiku.play }.to raise_error IOError
|
65
|
-
end
|
66
|
-
end
|