2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Runtime.InteropServices;
20 using System.Threading.Tasks;
22 using static Tizen.Multimedia.Interop.Radio;
24 namespace Tizen.Multimedia
27 /// Provides a means for using the radio feature.
29 public class Radio : IDisposable
31 private Interop.RadioHandle _handle;
33 private const string FeatureFmRadio = "http://tizen.org/feature/fmradio";
36 /// Initializes a new instance of the Radio class.
38 /// <exception cref="NotSupportedException">The radio feature is not supported.</exception>
41 ValidateFeatureSupported(FeatureFmRadio);
47 _scanCompletedCallback = _ => ScanCompleted?.Invoke(this, EventArgs.Empty);
48 _interruptedCallback = (reason, _) => Interrupted?.Invoke(this, new RadioInterruptedEventArgs(reason));
49 _scanUpdatedCallback = (frequency, _) => ScanUpdated?.Invoke(this, new ScanUpdatedEventArgs(frequency));
50 _scanStoppedCallback = _ => ScanStopped?.Invoke(this, EventArgs.Empty);
52 SetScanCompletedCb(_handle, _scanCompletedCallback).ThrowIfFailed("Failed to initialize radio");
53 SetInterruptedCb(_handle, _interruptedCallback).ThrowIfFailed("Failed to initialize radio");
62 private Interop.RadioHandle Handle
68 throw new ObjectDisposedException(GetType().Name);
74 private ScanUpdatedCallback _scanUpdatedCallback;
76 private ScanStoppedCallback _scanStoppedCallback;
78 private ScanCompletedCallback _scanCompletedCallback;
80 private InterruptedCallback _interruptedCallback;
83 /// Occurs when the radio scanning information is updated.
85 public event EventHandler<ScanUpdatedEventArgs> ScanUpdated;
88 /// Occurs when the radio scanning stops.
90 public event EventHandler ScanStopped;
93 /// Occurs when the radio scanning is completed.
95 public event EventHandler ScanCompleted;
98 /// Occurs when the radio is interrupted.
100 public event EventHandler<RadioInterruptedEventArgs> Interrupted;
103 /// Gets the current state of the radio.
105 public RadioState State
110 GetState(Handle, out state);
116 /// Gets or sets the radio frequency in the range of 87500 ~ 108000 kHz.
118 /// <exception cref="ArgumentOutOfRangeException">
119 /// <paramref name="value"/> is less than <see cref="Range.Min"/> of <see cref="FrequencyRange"/>.<br/>
121 /// <paramref name="value"/> is greater than <see cref="Range.Max"/> of <see cref="FrequencyRange"/>.
128 GetFrequency(Handle, out value).ThrowIfFailed("Failed to get frequency");
133 if (value < FrequencyRange.Min || value > FrequencyRange.Max)
135 throw new ArgumentOutOfRangeException(nameof(Frequency), value, "Frequency must be within FrequencyRange.");
138 SetFrequency(Handle, value).ThrowIfFailed("Failed to set frequency");
143 /// Gets the current signal strength in the range of -128 ~ 128 dBm.
145 public int SignalStrength
150 GetSignalStrength(Handle, out value).ThrowIfFailed("Failed to get signal strength");
156 /// Gets or sets the value indicating if the radio is muted.
159 /// true if the radio is muted; otherwise, false.
160 /// The default is false.
167 GetMuted(Handle, out value).ThrowIfFailed("Failed to get the mute state");
172 SetMute(Handle, value).ThrowIfFailed("Failed to set the mute state");
177 /// Gets the channel spacing for the current region.
179 public int ChannelSpacing
184 GetChannelSpacing(Handle, out value).ThrowIfFailed("Failed to get channel spacing");
190 /// Gets or sets the radio volume level.
192 /// <remarks>Valid volume range is from 0 to 1.0(100%), inclusive.</remarks>
193 /// <value>The default is 1.0.</value>
194 /// <exception cref="ArgumentOutOfRangeException">
195 /// <paramref name="value"/> is less than zero.<br/>
197 /// <paramref name="value"/> is greater than 1.0.
204 GetVolume(Handle, out value).ThrowIfFailed("Failed to get volume level.");
209 if (value < 0F || 1.0F < value)
211 throw new ArgumentOutOfRangeException(nameof(value), value,
212 $"Valid volume range is 0 <= value <= 1.0, but got { value }.");
215 SetVolume(Handle, value).ThrowIfFailed("Failed to set volume level");
220 /// Gets the frequency for the region in the range of 87500 ~ 108000 kHz.
222 public Range FrequencyRange
228 GetFrequencyRange(Handle, out min, out max).ThrowIfFailed("Failed to get frequency range");
230 return new Range(min, max);
235 /// Starts the radio.
237 /// <remarks>The radio must be in the <see cref="RadioState.Ready"/> state.</remarks>
238 /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
241 ValidateRadioState(RadioState.Ready);
243 Interop.Radio.Start(Handle).ThrowIfFailed("Failed to start radio");
249 /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
250 /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
253 ValidateRadioState(RadioState.Playing);
255 Interop.Radio.Stop(Handle).ThrowIfFailed("Failed to stop radio");
259 /// Starts the radio scanning and triggers the <see cref="ScanUpdated"/> event when the scan information is updated.
261 /// <remarks>The radio must be in the <see cref="RadioState.Ready"/> or <see cref="RadioState.Playing"/> state.</remarks>
262 /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
263 /// <seealso cref="ScanUpdated"/>
264 /// <seealso cref="ScanCompleted"/>
265 public void StartScan()
267 ValidateRadioState(RadioState.Ready, RadioState.Playing);
269 ScanStart(Handle, _scanUpdatedCallback).ThrowIfFailed("Failed to start scanning");
273 /// Stops the radio scanning.
275 /// <remarks>The radio must be in the <see cref="RadioState.Scanning"/> state.</remarks>
276 /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
277 /// <seealso cref="ScanStopped"/>
278 public void StopScan()
280 ValidateRadioState(RadioState.Scanning);
282 ScanStop(Handle, _scanStoppedCallback).ThrowIfFailed("Failed to stop scanning");
286 /// Seeks up the effective frequency of the radio.
289 /// A task that represents the asynchronous seeking operation.
290 /// The result value is the current frequency in the range of 87500 ~ 108000 kHz.
291 /// It can be -1 if the seeking operation has failed.
293 /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
294 /// <exception cref="InvalidOperationException">
295 /// The radio is not in the valid state.<br/>
297 /// Seeking is in progress.
299 public Task<int> SeekUpAsync()
301 return SeekAsync(SeekUp);
305 /// Seeks down the effective frequency of the radio.
308 /// A task that represents the asynchronous seeking operation.
309 /// The result value is the current frequency in the range of 87500 ~ 108000 kHz.
310 /// It can be -1 if the seeking operation has failed.
312 /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
313 /// <exception cref="InvalidOperationException">
314 /// The radio is not in the valid state.<br/>
316 /// Seeking is in progress.
318 public Task<int> SeekDownAsync()
320 return SeekAsync(SeekDown);
323 private async Task<int> SeekAsync(Func<Interop.RadioHandle, SeekCompletedCallback, IntPtr, RadioError> func)
325 ValidateRadioState(RadioState.Playing);
327 var tcs = new TaskCompletionSource<int>();
328 SeekCompletedCallback callback = (currentFrequency, _) => tcs.TrySetResult(currentFrequency);
333 gcHandle = GCHandle.Alloc(callback);
335 func(Handle, callback, IntPtr.Zero).ThrowIfFailed("Failed to seek");
336 return await tcs.Task;
344 private void ValidateFeatureSupported(string featurePath)
346 if (Information.TryGetValue(featurePath, out bool supported) == false || supported == false)
348 throw new NotSupportedException($"The feature({featurePath}) is not supported.");
353 private void ValidateRadioState(params RadioState[] required)
355 RadioState curState = State;
357 if (required.Contains(curState) == false)
359 throw new InvalidOperationException($"{curState} is not valid state.");
363 #region IDisposable Support
364 private bool _disposed = false;
367 /// Releases the resources used by the Radio.
369 /// <param name="disposing">
370 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
372 protected virtual void Dispose(bool disposing)
385 /// Releases all resources used by the <see cref="Radio"/> object.
387 public void Dispose()