1 use std::fmt;
2 use webcore::try_from::{TryInto, TryFrom};
3 use webcore::value::{Value, Reference};
4 use webapi::event_target::{IEventTarget, EventTarget};
5 use webapi::dom_exception::{
6     SecurityError,
7     InvalidStateError,
8     NotSupportedError,
9     AbortError
10 };
11 
12 #[cfg(feature = "experimental_features_which_may_break_on_minor_version_bumps")]
13 use webcore::promise::{Promise, TypedPromise};
14 
15 /// This structure contains optional settings that may be provided
16 /// when requesting MIDI access.
17 // https://webaudio.github.io/web-midi-api/#dom-midioptions
18 #[derive(Clone, PartialEq, Debug, Default)]
19 pub struct MidiOptions {
20     /// This member informs the system whether the ability to send and receive
21     /// system exclusive messages is requested or allowed. If this member is
22     /// set to true, but system exclusive support is denied (either by policy
23     /// or by user action), the access request will fail with a `SecurityError`
24     /// error. If this support is not requested (and allowed), the system will
25     /// throw exceptions if the user tries to send system exclusive messages,
26     /// and will silently mask out any system exclusive messages received on
27     /// the port.
28     request_sysex: bool,
29 
30     /// This member informs the system whether the ability to utilize any software
31     /// synthesizers installed in the host system is requested or allowed. If this
32     /// member is set to true, but software synthesizer support is denied (either
33     /// by policy or by user action), the access request will fail with a `SecurityError`
34     /// error.
35     ///
36     /// Note that may result in a two-step request procedure if software synthesizer
37     /// support is desired but not required - software synthesizers may be disabled
38     /// when MIDI hardware device access is allowed.
39     request_software_synth: bool,
40 
41     #[doc(hidden)]
42     __non_exhaustive: ()
43 }
44 
45 error_enum_boilerplate! {
46     MidiAccessError,
47     SecurityError, AbortError, InvalidStateError, NotSupportedError
48 }
49 
50 /// This object provides the methods to list MIDI input and output devices,
51 /// and obtain access to an individual device.
52 // https://webaudio.github.io/web-midi-api/#dom-midiaccess
53 #[derive(Clone, Debug, Eq, PartialEq, ReferenceType)]
54 #[reference(instance_of = "MIDIAccess")]
55 #[reference(subclass_of(EventTarget))]
56 pub struct MidiAccess( Reference );
57 
58 impl MidiAccess {
59     /// Requests access to MIDI devices.
60     // https://webaudio.github.io/web-midi-api/#dom-navigator-requestmidiaccess
61     #[cfg(feature = "experimental_features_which_may_break_on_minor_version_bumps")]
new_with_options( options: &MidiOptions ) -> TypedPromise< MidiAccess, MidiAccessError >62     pub fn new_with_options( options: &MidiOptions ) -> TypedPromise< MidiAccess, MidiAccessError > {
63         let promise: Promise = js!(
64             if( !navigator.requestMIDIAccess ) {
65                 return new Promise( function( resolve, reject ) {
66                     reject( new DOMException( "WebMIDI is not supported by your browser!", "NotSupportedError" ) );
67                 });
68             }
69 
70             return navigator.requestMIDIAccess({
71                 sysex: @{options.request_sysex},
72                 software: @{options.request_software_synth}
73             });
74         ).try_into().unwrap();
75 
76         TypedPromise::new( promise )
77     }
78 
79     /// Requests access to MIDI devices with default options.
80     // https://webaudio.github.io/web-midi-api/#dom-navigator-requestmidiaccess
81     #[cfg(feature = "experimental_features_which_may_break_on_minor_version_bumps")]
new() -> TypedPromise< MidiAccess, MidiAccessError >82     pub fn new() -> TypedPromise< MidiAccess, MidiAccessError > {
83         Self::new_with_options( &MidiOptions::default() )
84     }
85 
86     /// The MIDI input ports available to the system.
87     // https://webaudio.github.io/web-midi-api/#dom-midiaccess-inputs
inputs( &self ) -> MidiInputMap88     pub fn inputs( &self ) -> MidiInputMap {
89         return js!(
90             return @{self}.inputs;
91         ).try_into().unwrap()
92     }
93 
94     /// The MIDI output ports available to the system.
95     // https://webaudio.github.io/web-midi-api/#dom-midiaccess-outputs
outputs( &self ) -> MidiOutputMap96     pub fn outputs( &self ) -> MidiOutputMap {
97         return js!(
98             return @{self}.outputs;
99         ).try_into().unwrap()
100     }
101 
102     /// This attribute informs the user whether system exclusive support is enabled.
103     // https://webaudio.github.io/web-midi-api/#dom-midiaccess-sysexenabled
sysex_enabled( &self ) -> bool104     pub fn sysex_enabled( &self ) -> bool {
105         return js!(
106             return @{self}.sysexEnabled;
107         ).try_into().unwrap()
108     }
109 }
110 
map_iter_next< K, V >( iter: &Value ) -> Option< (K, V) > where K: TryFrom< Value >, V: TryFrom< Value >, K::Error: fmt::Debug, V::Error: fmt::Debug111 fn map_iter_next< K, V >( iter: &Value ) -> Option< (K, V) >
112     where K: TryFrom< Value >,
113           V: TryFrom< Value >,
114           K::Error: fmt::Debug,
115           V::Error: fmt::Debug
116 {
117     let entry = js!( return @{&iter}.next(); );
118     let is_done: bool = js!( return @{&entry}.done; ).try_into().unwrap();
119     if is_done {
120         return None;
121     }
122 
123     let key = js!( return @{&entry}.value[0] ).try_into().unwrap();
124     let value = js!( return @{&entry}.value[1] ).try_into().unwrap();
125     Some( (key, value) )
126 }
127 
128 /// This type is used to represent all the currently available MIDI input ports.
129 // https://webaudio.github.io/web-midi-api/#dom-midiinputmap
130 #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
131 #[reference(instance_of = "MIDIInputMap")]
132 pub struct MidiInputMap( Reference );
133 
134 macro_rules! define_map {
135     ($map_name:ident, $item_name:ident, $iter_name:ident) => {
136         impl $map_name {
137             /// Returns the number of elements in this map.
138             pub fn len( &self ) -> u32 {
139                 js!( return @{self}.size; ).try_into().unwrap()
140             }
141 
142             /// Returns an iterator over the map.
143             pub fn iter( &self ) -> $iter_name {
144                 self.into_iter()
145             }
146         }
147 
148         #[derive(Debug)]
149         pub struct $iter_name {
150             iter: Value
151         }
152 
153         impl Iterator for $iter_name {
154             type Item = (String, $item_name);
155             fn next( &mut self ) -> Option< Self::Item > {
156                 map_iter_next( &self.iter )
157             }
158         }
159 
160         impl IntoIterator for $map_name {
161             type Item = (String, $item_name);
162             type IntoIter = $iter_name;
163 
164             #[inline]
165             fn into_iter( self ) -> Self::IntoIter {
166                 $iter_name {
167                     iter: js!( return @{self}.entries(); )
168                 }
169             }
170         }
171 
172         impl< 'a > IntoIterator for &'a $map_name {
173             type Item = (String, $item_name);
174             type IntoIter = $iter_name;
175 
176             #[inline]
177             fn into_iter( self ) -> Self::IntoIter {
178                 self.clone().into_iter()
179             }
180         }
181     }
182 }
183 
184 /// This interface represents a MIDI input or output port.
185 pub trait IMidiPort: IEventTarget {
186     /// A unique ID of the port.
187     ///
188     /// This can be used by developers to remember ports the user
189     /// has chosen for their application.
190     // https://webaudio.github.io/web-midi-api/#dom-midiport-id
id( &self ) -> String191     fn id( &self ) -> String {
192         return js!( return @{self.as_ref()}.id; ).try_into().unwrap();
193     }
194 
195     /// The system name of the port.
196     // https://webaudio.github.io/web-midi-api/#dom-midiport-name
name( &self ) -> Option< String >197     fn name( &self ) -> Option< String > {
198         return js!( return @{self.as_ref()}.name; ).try_into().unwrap();
199     }
200 }
201 
202 /// This object represents a MIDI input or output port.
203 // https://webaudio.github.io/web-midi-api/#dom-midiport
204 #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
205 #[reference(instance_of = "MIDIPort")]
206 #[reference(subclass_of(EventTarget))]
207 pub struct MidiPort( Reference );
208 
209 impl IEventTarget for MidiPort {}
210 impl IMidiPort for MidiPort {}
211 
212 /// This type is used to represent all the currently available MIDI output ports.
213 // https://webaudio.github.io/web-midi-api/#dom-midioutputmap
214 #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
215 #[reference(instance_of = "MIDIOutputMap")]
216 pub struct MidiOutputMap( Reference );
217 
218 define_map!( MidiInputMap, MidiInput, MidiInputMapIter );
219 define_map!( MidiOutputMap, MidiOutput, MidiOutputMapIter );
220 
221 /// A MIDI input port.
222 // https://webaudio.github.io/web-midi-api/#dom-midiinput
223 #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
224 #[reference(instance_of = "MIDIInput")]
225 #[reference(subclass_of(EventTarget, MidiPort))]
226 pub struct MidiInput( Reference );
227 
228 impl IEventTarget for MidiInput {}
229 impl IMidiPort for MidiInput {}
230 
231 /// A MIDI output port.
232 // https://webaudio.github.io/web-midi-api/#dom-midioutput
233 #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
234 #[reference(instance_of = "MIDIOutput")]
235 #[reference(subclass_of(EventTarget, MidiPort))]
236 pub struct MidiOutput( Reference );
237 
238 impl IEventTarget for MidiOutput {}
239 impl IMidiPort for MidiOutput {}
240