/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using Tizen.System; using static Tizen.Multimedia.Interop.Radio; namespace Tizen.Multimedia { /// /// Provides a means for using the radio feature. /// public class Radio : IDisposable { private Interop.RadioHandle _handle; private const string FeatureFmRadio = "http://tizen.org/feature/fmradio"; /// /// Initializes a new instance of the Radio class. /// /// The radio feature is not supported. public Radio() { ValidateFeatureSupported(FeatureFmRadio); Create(out _handle); try { _scanCompletedCallback = _ => ScanCompleted?.Invoke(this, EventArgs.Empty); _interruptedCallback = (reason, _) => Interrupted?.Invoke(this, new RadioInterruptedEventArgs(reason)); _scanUpdatedCallback = (frequency, _) => ScanUpdated?.Invoke(this, new ScanUpdatedEventArgs(frequency)); _scanStoppedCallback = _ => ScanStopped?.Invoke(this, EventArgs.Empty); SetScanCompletedCb(_handle, _scanCompletedCallback).ThrowIfFailed("Failed to initialize radio"); SetInterruptedCb(_handle, _interruptedCallback).ThrowIfFailed("Failed to initialize radio"); } catch (Exception) { _handle.Dispose(); throw; } } private Interop.RadioHandle Handle { get { if (_disposed) { throw new ObjectDisposedException(GetType().Name); } return _handle; } } private ScanUpdatedCallback _scanUpdatedCallback; private ScanStoppedCallback _scanStoppedCallback; private ScanCompletedCallback _scanCompletedCallback; private InterruptedCallback _interruptedCallback; /// /// Occurs when the radio scanning information is updated. /// public event EventHandler ScanUpdated; /// /// Occurs when the radio scanning stops. /// public event EventHandler ScanStopped; /// /// Occurs when the radio scanning is completed. /// public event EventHandler ScanCompleted; /// /// Occurs when the radio is interrupted. /// public event EventHandler Interrupted; /// /// Gets the current state of the radio. /// public RadioState State { get { RadioState state; GetState(Handle, out state); return state; } } /// /// Gets or sets the radio frequency in the range of 87500 ~ 108000 kHz. /// /// /// is less than of .\n /// - or - \n /// is greater than of .\n /// public int Frequency { get { int value = 0; GetFrequency(Handle, out value).ThrowIfFailed("Failed to get frequency"); return value; } set { if (value < FrequencyRange.Min || value > FrequencyRange.Max) { throw new ArgumentOutOfRangeException(nameof(Frequency), value, "Frequency must be within FrequencyRange."); } SetFrequency(Handle, value).ThrowIfFailed("Failed to set frequency"); } } /// /// Gets the current signal strength in the range of -128 ~ 128 dBm. /// public int SignalStrength { get { int value = 0; GetSignalStrength(Handle, out value).ThrowIfFailed("Failed to get signal strength"); return value; } } /// /// Gets the value indicating if the radio is muted. /// /// /// true if the radio is muted; otherwise, false. /// The default is false. /// public bool IsMuted { get { bool value; GetMuted(Handle, out value).ThrowIfFailed("Failed to get the mute state"); return value; } set { SetMute(Handle, value).ThrowIfFailed("Failed to set the mute state"); } } /// /// Gets the channel spacing for the current region. /// public int ChannelSpacing { get { int value; GetChannelSpacing(Handle, out value).ThrowIfFailed("Failed to get channel spacing"); return value; } } /// /// Gets or sets the radio volume level. /// /// Valid volume range is from 0 to 1.0(100%), inclusive. /// The default is 1.0. /// /// is less than zero.\n /// - or -\n /// is greater than 1.0. /// public float Volume { get { float value; GetVolume(Handle, out value).ThrowIfFailed("Failed to get volume level."); return value; } set { if (value < 0F || 1.0F < value) { throw new ArgumentOutOfRangeException(nameof(value), value, $"Valid volume range is 0 <= value <= 1.0, but got { value }."); } SetVolume(Handle, value).ThrowIfFailed("Failed to set volume level"); } } /// /// Gets the frequency for the region in the range of 87500 ~ 108000 kHz. /// public Range FrequencyRange { get { int min, max; GetFrequencyRange(Handle, out min, out max).ThrowIfFailed("Failed to get frequency range"); return new Range(min, max); } } /// /// Starts the radio. /// /// The radio must be in the state. /// The radio is not in the valid state. public void Start() { ValidateRadioState(RadioState.Ready); Interop.Radio.Start(Handle).ThrowIfFailed("Failed to start radio"); } /// /// Stops the radio. /// /// The radio must be in the state. /// The radio is not in the valid state. public void Stop() { ValidateRadioState(RadioState.Playing); Interop.Radio.Stop(Handle).ThrowIfFailed("Failed to stop radio"); } /// /// Starts the radio scanning and triggers the ScanInformationUpdated event when the scan information is updated. /// /// The radio must be in the or state. /// The radio is not in the valid state. /// /// public void StartScan() { ValidateRadioState(RadioState.Ready, RadioState.Playing); ScanStart(Handle, _scanUpdatedCallback).ThrowIfFailed("Failed to start scanning"); } /// /// Stops the radio scanning. /// /// The radio must be in the state. /// The radio is not in the valid state. /// public void StopScan() { ValidateRadioState(RadioState.Scanning); ScanStop(Handle, _scanStoppedCallback).ThrowIfFailed("Failed to stop scanning"); } /// /// Seeks up the effective frequency of the radio. /// /// /// A task that represents the asynchronous seeking operation. /// The result value is the current frequency in the range of 87500 ~ 108000 kHz. /// It can be -1 if the seeking operation has failed. /// /// The radio must be in the state. /// /// The radio is not in the valid state.\n /// -or-\n /// Seeking is in progress. /// public Task SeekUpAsync() { return SeekAsync(SeekUp); } /// /// Seeks down the effective frequency of the radio. /// /// /// A task that represents the asynchronous seeking operation. /// The result value is the current frequency in the range of 87500 ~ 108000 kHz. /// It can be -1 if the seeking operation has failed. /// /// The radio must be in the state. /// /// The radio is not in the valid state.\n /// -or-\n /// Seeking is in progress. /// public Task SeekDownAsync() { return SeekAsync(SeekDown); } private async Task SeekAsync(Func func) { ValidateRadioState(RadioState.Playing); var tcs = new TaskCompletionSource(); SeekCompletedCallback callback = (currentFrequency, _) => tcs.TrySetResult(currentFrequency); GCHandle gcHandle; try { gcHandle = GCHandle.Alloc(callback); func(Handle, callback, IntPtr.Zero).ThrowIfFailed("Failed to seek"); return await tcs.Task; } finally { gcHandle.Free(); } } private void ValidateFeatureSupported(string featurePath) { if (Information.TryGetValue(featurePath, out bool supported) == false || supported == false) { throw new NotSupportedException($"The feature({featurePath}) is not supported."); } } private void ValidateRadioState(params RadioState[] required) { RadioState curState = State; if (required.Contains(curState) == false) { throw new InvalidOperationException($"{curState} is not valid state."); } } #region IDisposable Support private bool _disposed = false; /// /// Releases the resources used by the Radio. /// /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. /// protected virtual void Dispose(bool disposing) { if (!_disposed) { if (_handle != null) { _handle.Dispose(); } _disposed = true; } } /// /// Releases all resources used by the object. /// public void Dispose() { Dispose(true); } #endregion } }