diff --git a/MPfm/MPfm.MVP/MPfm.MVP.iOS.csproj b/MPfm/MPfm.MVP/MPfm.MVP.iOS.csproj
index 1c7a396e..fbc83338 100644
--- a/MPfm/MPfm.MVP/MPfm.MVP.iOS.csproj
+++ b/MPfm/MPfm.MVP/MPfm.MVP.iOS.csproj
@@ -295,6 +295,6 @@
-
+
\ No newline at end of file
diff --git a/MPfm/MPfm.Sound/PeakFiles/PeakFileStartedData.cs b/MPfm/MPfm.Sound/PeakFiles/PeakFileStartedData.cs
index 6bb704e6..ab85ec72 100644
--- a/MPfm/MPfm.Sound/PeakFiles/PeakFileStartedData.cs
+++ b/MPfm/MPfm.Sound/PeakFiles/PeakFileStartedData.cs
@@ -30,5 +30,6 @@ public class PeakFileStartedData
/// Defines the total number of blocks to read.
///
public int TotalBlocks { get; set; }
+ public string AudioFilePath { get; set; }
}
}
\ No newline at end of file
diff --git a/MPfm/MPfm.iOS/Classes/Controllers/PlayerViewController.cs b/MPfm/MPfm.iOS/Classes/Controllers/PlayerViewController.cs
index f59fe4d3..e8105492 100644
--- a/MPfm/MPfm.iOS/Classes/Controllers/PlayerViewController.cs
+++ b/MPfm/MPfm.iOS/Classes/Controllers/PlayerViewController.cs
@@ -67,10 +67,6 @@ public override void DidReceiveMemoryWarning()
public override void ViewDidLoad()
{
- // Set fonts
-// lblPosition.Font = UIFont.FromName("OstrichSans-Black", 18);
-// lblLength.Font = UIFont.FromName("OstrichSans-Black", 18);
-
// Load button bitmaps
btnPrevious.SetImage(UIImage.FromBundle("Images/Buttons/previous"), UIControlState.Normal);
btnPrevious.SetImage(UIImage.FromBundle("Images/Buttons/previous_on"), UIControlState.Highlighted);
diff --git a/MPfm/MPfm.iOS/Classes/Controls/MPfmNavigationController.cs b/MPfm/MPfm.iOS/Classes/Controls/MPfmNavigationController.cs
index 99250a93..153103aa 100644
--- a/MPfm/MPfm.iOS/Classes/Controls/MPfmNavigationController.cs
+++ b/MPfm/MPfm.iOS/Classes/Controls/MPfmNavigationController.cs
@@ -53,11 +53,11 @@ public MPfmNavigationController(MobileNavigationTabType tabType) : base()
// Create messenger hub to listen to player changes
_messengerHub = Bootstrapper.GetContainer().Resolve();
_messengerHub.Subscribe((message) => {
- Console.WriteLine("NavCtrl (" + TabType.ToString() + ") - PlayerPlaylistIndexChangedMessage");
+ //Console.WriteLine("NavCtrl (" + TabType.ToString() + ") - PlayerPlaylistIndexChangedMessage");
UpdateNowPlayingView();
});
_messengerHub.Subscribe((message) => {
- Console.WriteLine("NavCtrl (" + TabType.ToString() + ") - PlayerStatusMessage - Status=" + message.Status.ToString());
+ //Console.WriteLine("NavCtrl (" + TabType.ToString() + ") - PlayerStatusMessage - Status=" + message.Status.ToString());
if(message.Status == PlayerStatusType.Playing)
_isPlayerPlaying = true;
else
@@ -122,7 +122,7 @@ public MPfmNavigationController(MobileNavigationTabType tabType) : base()
public override void ViewWillLayoutSubviews()
{
- Console.WriteLine("MPfmNavCtrl - ViewWillLayoutSubviews");
+ //Console.WriteLine("MPfmNavCtrl - ViewWillLayoutSubviews");
float x = 12;
if(this.VisibleViewController.NavigationItem.LeftBarButtonItem != null)
@@ -163,10 +163,10 @@ public override void ViewWillLayoutSubviews()
private void UpdateNowPlayingView()
{
- Console.WriteLine("NavCtrl (" + TabType.ToString() + ") - UpdateNowPlayingView: isPlayerPlaying=" + _isPlayerPlaying.ToString() + " isViewPlayer=" + _isViewPlayer.ToString());
+ //Console.WriteLine("NavCtrl (" + TabType.ToString() + ") - UpdateNowPlayingView: isPlayerPlaying=" + _isPlayerPlaying.ToString() + " isViewPlayer=" + _isViewPlayer.ToString());
if(_isPlayerPlaying && !_isViewPlayer)
{
- Console.WriteLine("NavCtrl - Showing Now Playing view...");
+ //Console.WriteLine("NavCtrl - Showing Now Playing view...");
UIView.Animate(0.3f, () => {
_btnEffects.Frame = new RectangleF(UIScreen.MainScreen.Bounds.Width, 0, 44, 44);
_btnEffects.Alpha = 0;
diff --git a/MPfm/MPfm.iOS/Classes/Controls/MPfmWaveFormView.cs b/MPfm/MPfm.iOS/Classes/Controls/MPfmWaveFormView.cs
index 2d84a1df..f6cd3ab7 100644
--- a/MPfm/MPfm.iOS/Classes/Controls/MPfmWaveFormView.cs
+++ b/MPfm/MPfm.iOS/Classes/Controls/MPfmWaveFormView.cs
@@ -31,21 +31,23 @@
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MPfm.iOS.Helpers;
+using MPfm.iOS.Managers;
+using MPfm.iOS.Managers.Events;
namespace MPfm.iOS.Classes.Controls
{
[Register("MPfmWaveFormView")]
public class MPfmWaveFormView : UIView
{
- private IPeakFileService _peakFileService;
+ private WaveFormCacheManager _waveFormCacheManager;
private string _status = "Initial status";
private bool _isLoading = false;
private bool _isGeneratingImageCache = false;
private UIImage _imageCache = null;
- private AudioFile _currentAudioFile = null;
private float _cursorX;
private float _secondaryCursorX;
- private List _waveDataHistory;
+ private CGColor _colorGradient1 = new CGColor(0, 0, 0, 1);
+ private CGColor _colorGradient2 = new CGColor(0.15f, 0.15f, 0.15f, 1);
public WaveFormDisplayType DisplayType { get; set; }
@@ -61,7 +63,7 @@ public long Position
_position = value;
// Don't bother if a peak file is loading
- if(_isLoading || _imageCache == null || _waveDataHistory.Count == 0)
+ if(_isLoading || _imageCache == null)
return;
// Invalidate cursor
@@ -145,67 +147,61 @@ public MPfmWaveFormView(RectangleF frame)
private void Initialize()
{
this.BackgroundColor = UIColor.Black;
- _waveDataHistory = new List();
DisplayType = WaveFormDisplayType.Stereo;
-
- _peakFileService = Bootstrapper.GetContainer().Resolve();
- _peakFileService.OnProcessStarted += HandleOnPeakFileProcessStarted;
- _peakFileService.OnProcessData += HandleOnPeakFileProcessData;
- _peakFileService.OnProcessDone += HandleOnPeakFileProcessDone;
+ _waveFormCacheManager = Bootstrapper.GetContainer().Resolve();
+ _waveFormCacheManager.GeneratePeakFileBegunEvent += HandleGeneratePeakFileBegunEvent;
+ _waveFormCacheManager.GeneratePeakFileProgressEvent += HandleGeneratePeakFileProgressEvent;
+ _waveFormCacheManager.GeneratePeakFileEndedEvent += HandleGeneratePeakFileEndedEvent;
+ _waveFormCacheManager.GenerateWaveFormBitmapBegunEvent += HandleGenerateWaveFormBegunEvent;
+ _waveFormCacheManager.GenerateWaveFormBitmapEndedEvent += HandleGenerateWaveFormEndedEvent;
}
- void HandleOnPeakFileProcessStarted(PeakFileStartedData data)
+ private void HandleGeneratePeakFileBegunEvent(object sender, GeneratePeakFileEventArgs e)
{
InvokeOnMainThread(() => {
- _waveDataHistory = new List((int)data.TotalBlocks);
- if(_imageCache != null)
- {
- _imageCache.Dispose();
- _imageCache = null;
- }
- _isLoading = true;
- _status = "Generating wave form (0% done)";
- SetNeedsDisplay();
+ RefreshStatus("Generating wave form (0% done)");
});
}
- void HandleOnPeakFileProcessData(PeakFileProgressData data)
+ private void HandleGeneratePeakFileProgressEvent(object sender, GeneratePeakFileEventArgs e)
{
InvokeOnMainThread(() => {
- // Add wave data to history. Use a while a loop to modify the collection while looping.
- List minMaxs = data.MinMax;
- while (true)
- {
- if (minMaxs.Count == 0)
- break;
-
- _waveDataHistory.Add(minMaxs[0]);
- minMaxs.RemoveAt(0);
- }
-
- _status = "Generating wave form (" + data.PercentageDone.ToString("0") + "% done)";
- SetNeedsDisplay();
+ RefreshStatus("Generating wave form (" + e.PercentageDone.ToString("0") + "% done)");
+ });
+ }
+
+ private void HandleGeneratePeakFileEndedEvent(object sender, GeneratePeakFileEventArgs e)
+ {
+ InvokeOnMainThread(() => {
+ _waveFormCacheManager.RequestBitmap(e.AudioFilePath, WaveFormDisplayType.Stereo, Bounds, 1);
});
}
- void HandleOnPeakFileProcessDone(PeakFileDoneData data)
+ private void HandleGenerateWaveFormBegunEvent(object sender, GenerateWaveFormEventArgs e)
+ {
+
+ }
+
+ private void HandleGenerateWaveFormEndedEvent(object sender, GenerateWaveFormEventArgs e)
{
InvokeOnMainThread(() => {
- //if(data.Cancelled)
- if(_currentAudioFile != null && data.AudioFilePath != _currentAudioFile.FilePath)
- {
- if(_imageCache != null)
- {
- _imageCache.Dispose();
- _imageCache = null;
- }
- _waveDataHistory = new List();
- }
-
- _status = string.Empty;
_isLoading = false;
+ _imageCache = e.Image;
SetNeedsDisplay();
- });
+ });
+ }
+
+ void HandleOnPeakFileProcessStarted(PeakFileStartedData data)
+ {
+ }
+
+ void HandleOnPeakFileProcessData(PeakFileProgressData data)
+ {
+
+ }
+
+ void HandleOnPeakFileProcessDone(PeakFileDoneData data)
+ {
}
public void FlushCache()
@@ -219,104 +215,67 @@ public void FlushCache()
public void LoadPeakFile(AudioFile audioFile)
{
- // Indicate peak file loading
- _currentAudioFile = audioFile;
+ Console.WriteLine("WaveFormView - LoadPeakFile audioFile: " + audioFile.FilePath);
+ RefreshStatus("Loading peak file...");
+ _waveFormCacheManager.LoadPeakFile(audioFile);
+ }
+
+ private void RefreshStatus(string status)
+ {
_isLoading = true;
- _status = "Loading peak file...";
+ _status = status;
SetNeedsDisplay();
+ }
- // Check if another peak file is already loading
- Console.WriteLine("WaveFormView - LoadPeakFile audioFile: " + audioFile.FilePath);
- if (_peakFileService.IsLoading)
- _peakFileService.Cancel();
-
- // Check if the peak file subfolder exists
- string peakFileFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "PeakFiles");
- if (!Directory.Exists(peakFileFolder))
- {
- try
- {
- Console.WriteLine("PeakFile - Creating folder " + peakFileFolder + "...");
- DirectoryInfo directoryInfo = Directory.CreateDirectory(peakFileFolder);
- }
- catch(Exception ex)
- {
- Console.WriteLine("PeakFile - Failed to create folder: " + ex.Message);
- return;
- }
- }
+ private void DrawStatus(string status)
+ {
+ var context = UIGraphics.GetCurrentContext();
+
+ // Draw gradient background
+ CoreGraphicsHelper.FillGradient(context, Bounds, _colorGradient2, _colorGradient1);
+
+ // Draw status string
+ NSString str = new NSString(status);
+ context.SetFillColor(UIColor.White.CGColor);
+ float y = (Bounds.Height - 30) / 2;
+ str.DrawString(new RectangleF(0, y, Bounds.Width, 30), UIFont.FromName("HelveticaNeue", 12), UILineBreakMode.TailTruncation, UITextAlignment.Center);
+ return;
+ }
- // Generate peak file path
- string peakFilePath = Path.Combine(peakFileFolder, Normalizer.NormalizeStringForUrl(audioFile.ArtistName + "_" + audioFile.AlbumTitle + "_" + audioFile.Title + "_" + audioFile.FileType.ToString()) + ".peak");
+ private void DrawWaveFormBitmap()
+ {
+ _isLoading = false;
+ int heightAvailable = (int)Frame.Height;
- // Check if peak file exists
- if (File.Exists(peakFilePath))
+ // Draw bitmap cache
+ var context = UIGraphics.GetCurrentContext();
+ context.DrawImage(Bounds, _imageCache.CGImage);
+
+ // Calculate position
+ float positionPercentage = (float)Position / (float)Length;
+ _cursorX = (positionPercentage * Bounds.Width) - ScrollX;
+
+ // Draw cursor line
+ context.SetStrokeColor(new CGColor(0, 0.5f, 1, 1));
+ context.SetLineWidth(1.0f);
+ context.StrokeLineSegments(new PointF[2] { new PointF(_cursorX, 0), new PointF(_cursorX, heightAvailable) });
+
+ // Check if a secondary cursor must be drawn
+ if(_secondaryPosition > 0)
{
- Task>.Factory.StartNew(() => {
- List data = null;
- try
- {
- data = _peakFileService.ReadPeakFile(peakFilePath);
- if(data != null)
- return data;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Error reading peak file: " + ex.Message);
- }
-
- try
- {
- Console.WriteLine("Peak file could not be loaded - Generating " + peakFilePath + "...");
- _peakFileService.GeneratePeakFile(audioFile.FilePath, peakFilePath);
- }
- catch (Exception ex)
- {
- Console.WriteLine("Error generating peak file: " + ex.Message);
- }
- return null;
- }, TaskCreationOptions.LongRunning).ContinueWith(t => {
- List data = (List)t.Result;
-
- if (data == null)
- {
- // The peak file has been generated on another thread
- return;
- }
- else
- {
- InvokeOnMainThread(() => {
- // Refresh image cache
- _waveDataHistory = data;
- if(_imageCache != null)
- {
- _imageCache.Dispose();
- _imageCache = null;
- }
- _isLoading = false;
- _status = string.Empty;
- SetNeedsDisplay();
- });
- }
- }, TaskScheduler.FromCurrentSynchronizationContext());
- }
- else
- {
- // Start generating peak file in background
- Console.WriteLine("Peak file doesn't exist - Generating " + peakFilePath + "...");
- _peakFileService.GeneratePeakFile(audioFile.FilePath, peakFilePath);
+ // Calculate position
+ float secondaryPositionPercentage = (float)SecondaryPosition / (float)Length;
+ _secondaryCursorX = (secondaryPositionPercentage * Bounds.Width) - ScrollX;
+
+ // Draw cursor line
+ context.SetStrokeColor(new CGColor(1, 1, 1, 1));
+ context.SetLineWidth(1.0f);
+ context.StrokeLineSegments(new PointF[2] { new PointF(_secondaryCursorX, 0), new PointF(_secondaryCursorX, heightAvailable) });
}
}
- public void CancelPeakFileGeneration()
- {
- _peakFileService.Cancel();
- }
-
- public override void Draw(RectangleF rect)
+ private void GenerateWaveFormBitmap()
{
- base.Draw(rect);
-
// Calculate available size
int widthAvailable = (int)Frame.Width;
int heightAvailable = (int)Frame.Height;
@@ -325,334 +284,23 @@ public override void Draw(RectangleF rect)
widthAvailable = (int)(Frame.Width * Zoom);
}
- CGContext context;
- CGColor colorGradient1 = new CGColor(0, 0, 0, 1);
- CGColor colorGradient2 = new CGColor(0.15f, 0.15f, 0.15f, 1);
-
- // Check if a wave form needs to be displayed
- string emptyWaveFormMessage = string.Empty;
- if (_isLoading)
- emptyWaveFormMessage = _status;
- else if(_waveDataHistory.Count == 0)
- emptyWaveFormMessage = string.Empty;
-
- //if(!string.IsNullOrEmpty(emptyWaveFormMessage))
- if(_isLoading || _waveDataHistory.Count == 0)
+ var context = UIGraphics.GetCurrentContext();
+ if(!_isGeneratingImageCache)
{
- context = UIGraphics.GetCurrentContext();
-
- // Draw gradient background
- CoreGraphicsHelper.FillGradient(context, Bounds, colorGradient2, colorGradient1);
-
- // Draw status string
- NSString str = new NSString(emptyWaveFormMessage);
- context.SetFillColor(UIColor.White.CGColor);
- float y = (Bounds.Height - 30) / 2;
- str.DrawString(new RectangleF(0, y, Bounds.Width, 30), UIFont.FromName("HelveticaNeue", 12), UILineBreakMode.TailTruncation, UITextAlignment.Center);
- return;
+ RefreshStatus("Generating wave form image cache...");
+ _isGeneratingImageCache = true;
}
- // Check if bitmap cache should be reloaded
- if (_imageCache != null)
- {
- // Draw bitmap cache
- context = UIGraphics.GetCurrentContext();
- context.DrawImage(Bounds, _imageCache.CGImage);
+ }
- // Calculate position
- float positionPercentage = (float)Position / (float)Length;
- _cursorX = (positionPercentage * Bounds.Width) - ScrollX;
+ public override void Draw(RectangleF rect)
+ {
+ base.Draw(rect);
- // Draw cursor line
- context.SetStrokeColor(new CGColor(0, 0.5f, 1, 1));
- context.SetLineWidth(1.0f);
- context.StrokeLineSegments(new PointF[2] { new PointF(_cursorX, 0), new PointF(_cursorX, heightAvailable) });
-
- // Check if a secondary cursor must be drawn
- if(_secondaryPosition > 0)
- {
- // Calculate position
- float secondaryPositionPercentage = (float)SecondaryPosition / (float)Length;
- _secondaryCursorX = (secondaryPositionPercentage * Bounds.Width) - ScrollX;
-
- // Draw cursor line
- context.SetStrokeColor(new CGColor(1, 1, 1, 1));
- context.SetLineWidth(1.0f);
- context.StrokeLineSegments(new PointF[2] { new PointF(_secondaryCursorX, 0), new PointF(_secondaryCursorX, heightAvailable) });
- }
- }
- else if (_imageCache == null && _waveDataHistory.Count > 0)
- {
- // Let the user know the image cache is generating
- context = UIGraphics.GetCurrentContext();
-
- // Generate image in another thread
- if(!_isGeneratingImageCache)
- {
- // Draw gradient background
- CoreGraphicsHelper.FillGradient(context, Bounds, colorGradient2, colorGradient1);
-
- // Draw status string
- NSString str = new NSString("Generating wave form image cache...");
- context.SetFillColor(UIColor.White.CGColor);
- float y = (Bounds.Height - 30) / 2;
- str.DrawString(new RectangleF(0, y, Bounds.Width, 30), UIFont.FromName("HelveticaNeue", 12), UILineBreakMode.TailTruncation, UITextAlignment.Center);
-
- _isGeneratingImageCache = true;
- Task.Factory.StartNew(() => {
-
- try
- {
- Console.WriteLine("WaveFormView - Creating image cache...");
- UIGraphics.BeginImageContextWithOptions(Bounds.Size, false, 0);
- context = UIGraphics.GetCurrentContext();
- if (context == null)
- {
- // Error
- Console.WriteLine("Error initializing image cache!");
- return null;
- }
-
- // Draw gradient background
- CoreGraphicsHelper.FillGradient(context, Bounds, colorGradient1, colorGradient2);
-
- // Declare variables
- float x1 = 0;
- float x2 = 0;
- float leftMin = 0;
- float leftMax = 0;
- float rightMin = 0;
- float rightMax = 0;
- float mixMin = 0;
- float mixMax = 0;
- float leftMaxHeight = 0;
- float leftMinHeight = 0;
- float rightMaxHeight = 0;
- float rightMinHeight = 0;
- float mixMaxHeight = 0;
- float mixMinHeight = 0;
- int historyIndex = 0;
- int historyCount = 0;
- float lineWidth = 0;
- float lineWidthPerHistoryItem = 0;
- int nHistoryItemsPerLine = 0;
- float desiredLineWidth = 0.5f;
- WaveDataMinMax[] subset = null;
-
- historyCount = _waveDataHistory.Count;
-
- // Find out how many samples are represented by each line of the wave form, depending on its width.
- // For example, if the history has 45000 items, and the control has a width of 1000px, 45 items will need to be averaged by line.
- lineWidthPerHistoryItem = (float)widthAvailable / (float)historyCount;
- //float historyItemsPerLine = (float)WaveDataHistory.Count / (float)Bounds.Width;
-
- // Check if the line width is below the desired line width
- if (lineWidthPerHistoryItem < desiredLineWidth)
- {
- // Try to get a line width around 0.5f so the precision is good enough and no artifacts will be shown.
- while (lineWidth < desiredLineWidth)
- {
- // Increment the number of history items per line
- //Console.WriteLine("Determining line width (lineWidth: " + lineWidth.ToString() + " desiredLineWidth: " + desiredLineWidth.ToString() + " nHistoryItemsPerLine: " + nHistoryItemsPerLine.ToString() + " lineWidthPerHistoryItem: " + lineWidthPerHistoryItem.ToString());
- nHistoryItemsPerLine++;
- lineWidth += lineWidthPerHistoryItem;
- }
- nHistoryItemsPerLine--;
- lineWidth -= lineWidthPerHistoryItem;
- }
- else
- {
- // The lines are larger than 0.5 pixels.
- lineWidth = lineWidthPerHistoryItem;
- nHistoryItemsPerLine = 1;
- }
-
- Console.WriteLine("WaveFormView - historyItemsPerLine: " + nHistoryItemsPerLine.ToString());
-
- context.SetStrokeColor(new CGColor(1, 1, 0.5f, 1));
- context.SetLineWidth(0.5f);
- //context.SetLineWidth(lineWidth);
-
- for (float i = 0; i < widthAvailable; i += lineWidth)
- {
- // Round to 0.5
- //i = (float)Math.Round(i * 2) / 2;
- float iRound = (float)Math.Round(i * 2) / 2;
-
- // Determine the maximum height of a line (+/-)
- //Console.WriteLine("WaveForm - Rendering " + i.ToString() + " (rnd=" + iRound.ToString() + ") on " + widthAvailable.ToString());
- float heightToRenderLine = 0;
- if (DisplayType == WaveFormDisplayType.Stereo)
- {
- heightToRenderLine = (float)heightAvailable / 4;
- }
- else
- {
- heightToRenderLine = (float)heightAvailable / 2;
- }
-
- // Determine x position
- x1 = i;
- x2 = i;
-
- try
- {
- // Check if there are multiple history items per line
- if (nHistoryItemsPerLine > 1)
- {
- if (historyIndex + nHistoryItemsPerLine > historyCount)
- {
- // Create subset with remaining data
- subset = new WaveDataMinMax[historyCount - historyIndex];
- _waveDataHistory.CopyTo(historyIndex, subset, 0, historyCount - historyIndex);
- }
- else
- {
- subset = new WaveDataMinMax[nHistoryItemsPerLine];
- _waveDataHistory.CopyTo(historyIndex, subset, 0, nHistoryItemsPerLine);
- }
-
- leftMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Left);
- leftMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Left);
- rightMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Right);
- rightMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Right);
- mixMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Mix);
- mixMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Mix);
- }
- else
- {
- leftMin = _waveDataHistory[historyIndex].leftMin;
- leftMax = _waveDataHistory[historyIndex].leftMax;
- rightMin = _waveDataHistory[historyIndex].rightMin;
- rightMax = _waveDataHistory[historyIndex].rightMax;
- mixMin = _waveDataHistory[historyIndex].mixMin;
- mixMax = _waveDataHistory[historyIndex].mixMax;
- }
-
- // Increment history count
- //historyCount += historyItemsPerLine;
-
- leftMaxHeight = leftMax * heightToRenderLine;
- leftMinHeight = leftMin * heightToRenderLine;
- rightMaxHeight = rightMax * heightToRenderLine;
- rightMinHeight = rightMin * heightToRenderLine;
- mixMaxHeight = mixMax * heightToRenderLine;
- mixMinHeight = mixMin * heightToRenderLine;
- }
- catch(Exception ex)
- {
- throw;
- }
-
- // Determine display type
- if (DisplayType == WaveFormDisplayType.LeftChannel ||
- DisplayType == WaveFormDisplayType.RightChannel ||
- DisplayType == WaveFormDisplayType.Mix)
- {
- // Calculate min/max line height
- float minLineHeight = 0;
- float maxLineHeight = 0;
-
- // Set mib/max
- if (DisplayType == WaveFormDisplayType.LeftChannel)
- {
- minLineHeight = leftMinHeight;
- maxLineHeight = leftMaxHeight;
- }
- else if (DisplayType == WaveFormDisplayType.RightChannel)
- {
- minLineHeight = rightMinHeight;
- maxLineHeight = rightMaxHeight;
- }
- else if (DisplayType == WaveFormDisplayType.Mix)
- {
- minLineHeight = mixMinHeight;
- maxLineHeight = mixMaxHeight;
- }
-
- // ------------------------
- // Positive Max Value
-
- // Draw positive value (y: middle to top)
-
- context.StrokeLineSegments(new PointF[2] {
- new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine - maxLineHeight)
- });
-
- // ------------------------
- // Negative Max Value
-
- // Draw negative value (y: middle to height)
- context.StrokeLineSegments(new PointF[2] {
- new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine + (-minLineHeight))
- });
- }
- else if (DisplayType == WaveFormDisplayType.Stereo)
- {
- // -----------------------------------------
- // LEFT Channel - Positive Max Value
-
- // Draw positive value (y: middle to top)
- context.StrokeLineSegments(new PointF[2] {
- new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine - leftMaxHeight)
- });
-
- // -----------------------------------------
- // LEFT Channel - Negative Max Value
-
- // Draw negative value (y: middle to height)
- context.StrokeLineSegments(new PointF[2] {
- new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine + (-leftMinHeight))
- });
-
- // -----------------------------------------
- // RIGHT Channel - Positive Max Value
-
- // Multiply by 3 to get the new center line for right channel
- // Draw positive value (y: middle to top)
- context.StrokeLineSegments(new PointF[2] {
- new PointF(x1, (heightToRenderLine * 3)), new PointF(x2, (heightToRenderLine * 3) - rightMaxHeight)
- });
-
- // -----------------------------------------
- // RIGHT Channel - Negative Max Value
-
- // Draw negative value (y: middle to height)
- context.StrokeLineSegments(new PointF[2] {
- new PointF(x1, (heightToRenderLine * 3)), new PointF(x2, (heightToRenderLine * 3) + (-rightMinHeight))
- });
- }
-
- // Increment the history index; pad the last values if the count is about to exceed
- if (historyIndex < historyCount - 1)
- {
- // Increment by the number of history items per line
- historyIndex += nHistoryItemsPerLine;
- }
- }
-
- // Get image from context
- _imageCache = UIGraphics.GetImageFromCurrentImageContext();
- UIGraphics.EndImageContext();
- return _imageCache;
- }
- catch(Exception ex)
- {
- Console.WriteLine("Error while creating image cache: " + ex.Message);
- }
- return null;
- }, TaskCreationOptions.LongRunning).ContinueWith(t => {
- _isGeneratingImageCache = false;
- if(t.Result != null)
- {
- // Force refresh control now that the image cache is generated
- InvokeOnMainThread(() => {
- SetNeedsDisplay();
- });
- }
- }, TaskScheduler.FromCurrentSynchronizationContext());
- }
- }
+ if(_isLoading)
+ DrawStatus(_status);
+ else if (_imageCache != null)
+ DrawWaveFormBitmap();
}
}
diff --git a/MPfm/MPfm.iOS/Classes/Helpers/Events/GenerateWaveFormEventArgs.cs b/MPfm/MPfm.iOS/Classes/Helpers/Events/GenerateWaveFormEventArgs.cs
new file mode 100644
index 00000000..ac4b2498
--- /dev/null
+++ b/MPfm/MPfm.iOS/Classes/Helpers/Events/GenerateWaveFormEventArgs.cs
@@ -0,0 +1,25 @@
+// Copyright © 2011-2013 Yanick Castonguay
+//
+// This file is part of MPfm.
+//
+// MPfm is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// MPfm is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with MPfm. If not, see .
+
+using System;
+
+namespace MPfm.iOS.Helpers.Events
+{
+ public class GenerateWaveFormEventArgs : EventArgs
+ {
+ }
+}
diff --git a/MPfm/MPfm.iOS/Classes/Managers/Events/GeneratePeakFileEventArgs.cs b/MPfm/MPfm.iOS/Classes/Managers/Events/GeneratePeakFileEventArgs.cs
new file mode 100644
index 00000000..7e3e8ed2
--- /dev/null
+++ b/MPfm/MPfm.iOS/Classes/Managers/Events/GeneratePeakFileEventArgs.cs
@@ -0,0 +1,32 @@
+// Copyright © 2011-2013 Yanick Castonguay
+//
+// This file is part of MPfm.
+//
+// MPfm is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// MPfm is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with MPfm. If not, see .
+
+using System;
+
+namespace MPfm.iOS.Managers.Events
+{
+ public class GeneratePeakFileEventArgs : EventArgs
+ {
+ public string AudioFilePath { get; set; }
+ public float PercentageDone { get; set; }
+
+ public GeneratePeakFileEventArgs()
+ : base()
+ {
+ }
+ }
+}
diff --git a/MPfm/MPfm.iOS/Classes/Managers/Events/GenerateWaveFormEventArgs.cs b/MPfm/MPfm.iOS/Classes/Managers/Events/GenerateWaveFormEventArgs.cs
new file mode 100644
index 00000000..6bfe0d32
--- /dev/null
+++ b/MPfm/MPfm.iOS/Classes/Managers/Events/GenerateWaveFormEventArgs.cs
@@ -0,0 +1,36 @@
+// Copyright © 2011-2013 Yanick Castonguay
+//
+// This file is part of MPfm.
+//
+// MPfm is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// MPfm is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with MPfm. If not, see .
+
+using System;
+using MonoTouch.UIKit;
+using MPfm.iOS.Classes.Controls;
+
+namespace MPfm.iOS.Managers.Events
+{
+ public class GenerateWaveFormEventArgs : EventArgs
+ {
+ public string AudioFilePath { get; set; }
+ public float Zoom { get; set; }
+ public WaveFormDisplayType DisplayType { get; set; }
+ public UIImage Image { get; set; }
+
+ public GenerateWaveFormEventArgs()
+ : base()
+ {
+ }
+ }
+}
diff --git a/MPfm/MPfm.iOS/Classes/Managers/WaveFormCacheManager.cs b/MPfm/MPfm.iOS/Classes/Managers/WaveFormCacheManager.cs
new file mode 100644
index 00000000..dd7a585a
--- /dev/null
+++ b/MPfm/MPfm.iOS/Classes/Managers/WaveFormCacheManager.cs
@@ -0,0 +1,477 @@
+// Copyright © 2011-2013 Yanick Castonguay
+//
+// This file is part of MPfm.
+//
+// MPfm is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// MPfm is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with MPfm. If not, see .
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using MPfm.Core;
+using MPfm.Sound.AudioFiles;
+using MPfm.Sound.PeakFiles;
+using MonoTouch.CoreGraphics;
+using MonoTouch.Foundation;
+using MonoTouch.UIKit;
+using MPfm.iOS.Classes.Controls;
+using MPfm.iOS.Managers.Events;
+using MPfm.iOS.Helpers;
+
+namespace MPfm.iOS.Managers
+{
+ ///
+ /// Manager for caching wave form bitmaps.
+ ///
+ public class WaveFormCacheManager
+ {
+ private IPeakFileService _peakFileService;
+ private Dictionary> _waveDataCache = new Dictionary>();
+ private Dictionary, UIImage> _bitmapCache = new Dictionary, UIImage>();
+ private CGColor _colorGradient1 = new CGColor(0, 0, 0, 1);
+ private CGColor _colorGradient2 = new CGColor(0.15f, 0.15f, 0.15f, 1);
+ private float _padding = 0;
+
+ public delegate void GeneratePeakFileEventHandler(object sender, GeneratePeakFileEventArgs e);
+ public delegate void GenerateWaveFormEventHandler(object sender, GenerateWaveFormEventArgs e);
+
+ public event GeneratePeakFileEventHandler GeneratePeakFileBegunEvent;
+ public event GeneratePeakFileEventHandler GeneratePeakFileProgressEvent;
+ public event GeneratePeakFileEventHandler GeneratePeakFileEndedEvent;
+ public event GenerateWaveFormEventHandler GenerateWaveFormBitmapBegunEvent;
+ public event GenerateWaveFormEventHandler GenerateWaveFormBitmapEndedEvent;
+
+ public WaveFormCacheManager(IPeakFileService peakFileService)
+ {
+ _peakFileService = peakFileService;
+ _peakFileService.OnProcessStarted += HandleOnPeakFileProcessStarted;
+ _peakFileService.OnProcessData += HandleOnPeakFileProcessData;
+ _peakFileService.OnProcessDone += HandleOnPeakFileProcessDone;
+ }
+
+ protected virtual void OnGeneratePeakFileBegun(GeneratePeakFileEventArgs e)
+ {
+ if(GeneratePeakFileBegunEvent != null)
+ GeneratePeakFileBegunEvent(this, e);
+ }
+
+ protected virtual void OnGeneratePeakFileProgress(GeneratePeakFileEventArgs e)
+ {
+ if(GeneratePeakFileProgressEvent != null)
+ GeneratePeakFileProgressEvent(this, e);
+ }
+
+ protected virtual void OnGeneratePeakFileEnded(GeneratePeakFileEventArgs e)
+ {
+ if(GeneratePeakFileEndedEvent != null)
+ GeneratePeakFileEndedEvent(this, e);
+ }
+
+ protected virtual void OnGenerateWaveFormBitmapBegun(GenerateWaveFormEventArgs e)
+ {
+ if(GenerateWaveFormBitmapBegunEvent != null)
+ GenerateWaveFormBitmapBegunEvent(this, e);
+ }
+
+ protected virtual void OnGenerateWaveFormBitmapEnded(GenerateWaveFormEventArgs e)
+ {
+ if(GenerateWaveFormBitmapEndedEvent != null)
+ GenerateWaveFormBitmapEndedEvent(this, e);
+ }
+
+ void HandleOnPeakFileProcessStarted(PeakFileStartedData data)
+ {
+ OnGeneratePeakFileBegun(new GeneratePeakFileEventArgs());
+ }
+
+ void HandleOnPeakFileProcessData(PeakFileProgressData data)
+ {
+ OnGeneratePeakFileProgress(new GeneratePeakFileEventArgs(){
+ AudioFilePath = data.AudioFilePath,
+ PercentageDone = data.PercentageDone
+ });
+ }
+
+ void HandleOnPeakFileProcessDone(PeakFileDoneData data)
+ {
+ OnGeneratePeakFileEnded(new GeneratePeakFileEventArgs(){
+ AudioFilePath = data.AudioFilePath,
+ PercentageDone = 100
+ });
+ }
+
+ // Flow:
+ // LoadPeakFile(filePath)
+ // Control displays "Loading peak file"
+ // Peak file doesn't exist
+ // Sending GeneratingPeakFile event
+ // Generating peak file
+ // Peak file exists
+ // Loads peak file
+ // Peak file is loaded.
+
+ // Flow:
+ // Control requests bitmap at 100% zoom
+ // Generating wave form.
+ // Is wave form in cache?
+ // Return bitmap
+ // No, generating bitmap
+ // Tell control the bitmap has been loaded
+
+ // Flow:
+ // User changes the zoom level on the control
+ // Same as before but specify a different zoom.
+ // Thus the bitmap always returns to the control by an event.
+
+ public void LoadPeakFile(AudioFile audioFile)
+ {
+ // Indicate peak file loading
+ bool isLoading = true;
+ string status = "Loading peak file...";
+ //SetNeedsDisplay();
+
+ // Check if another peak file is already loading
+ Console.WriteLine("WaveFormCacheManager - LoadPeakFile audioFile: " + audioFile.FilePath);
+ if (_peakFileService.IsLoading)
+ _peakFileService.Cancel();
+
+ // Check if the peak file subfolder exists
+ string peakFileFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "PeakFiles");
+ if (!Directory.Exists(peakFileFolder))
+ {
+ try
+ {
+ Console.WriteLine("WaveFormCacheManager - Creating folder " + peakFileFolder + "...");
+ DirectoryInfo directoryInfo = Directory.CreateDirectory(peakFileFolder);
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine("WaveFormCacheManager - Failed to create folder: " + ex.Message);
+ return;
+ }
+ }
+
+ // Generate peak file path
+ string peakFilePath = Path.Combine(peakFileFolder, Normalizer.NormalizeStringForUrl(audioFile.ArtistName + "_" + audioFile.AlbumTitle + "_" + audioFile.Title + "_" + audioFile.FileType.ToString()) + ".peak");
+
+ // Check if peak file exists
+ if (File.Exists(peakFilePath))
+ {
+ Task>.Factory.StartNew(() => {
+ List data = null;
+ try
+ {
+ Console.WriteLine("WaveFormCacheManager - Reading peak file: " + peakFilePath);
+ data = _peakFileService.ReadPeakFile(peakFilePath);
+ if(data != null)
+ return data;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error reading peak file: " + ex.Message);
+ }
+
+ try
+ {
+ Console.WriteLine("Peak file could not be loaded - Generating " + peakFilePath + "...");
+ OnGeneratePeakFileBegun(new GeneratePeakFileEventArgs());
+ _peakFileService.GeneratePeakFile(audioFile.FilePath, peakFilePath);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error generating peak file: " + ex.Message);
+ }
+ return null;
+ }, TaskCreationOptions.LongRunning).ContinueWith(t => {
+ Console.WriteLine("WaveFormCacheManager - Read peak file over.");
+ List data = (List)t.Result;
+ if (data == null)
+ {
+ Console.WriteLine("WaveFormCacheManager - Could not load peak file succesfully.");
+ return;
+ }
+
+ Console.WriteLine("WaveFormCacheManager - Adding wave data to cache...");
+ if(!_waveDataCache.ContainsKey(audioFile.FilePath))
+ _waveDataCache.Add(audioFile.FilePath, data);
+
+ OnGeneratePeakFileEnded(new GeneratePeakFileEventArgs(){
+ AudioFilePath = audioFile.FilePath,
+ PercentageDone = 100
+ });
+
+ }, TaskScheduler.FromCurrentSynchronizationContext());
+ }
+ else
+ {
+ // Start generating peak file in background
+ Console.WriteLine("Peak file doesn't exist - Generating " + peakFilePath + "...");
+ _peakFileService.GeneratePeakFile(audioFile.FilePath, peakFilePath);
+ }
+ }
+
+ public void RequestBitmap(string audioFilePath, WaveFormDisplayType displayType, RectangleF bounds, float zoom)
+ {
+ UIImage imageCache;
+ RectangleF boundsWaveForm;
+
+ // key = FilePath + DisplayType + Zoom
+
+ // Calculate available size
+ int widthAvailable = (int)bounds.Width;
+ int heightAvailable = (int)bounds.Height;
+ if(zoom > 1)
+ {
+ widthAvailable = (int)(bounds.Width * zoom);
+ }
+ boundsWaveForm = new RectangleF(0, 0, widthAvailable - (_padding * 2), heightAvailable - (_padding * 2));
+
+ Task.Factory.StartNew(() => {
+ try
+ {
+ Console.WriteLine("WaveFormCacheManager - Creating image cache...");
+ UIGraphics.BeginImageContextWithOptions(bounds.Size, false, 0);
+ var context = UIGraphics.GetCurrentContext();
+ if (context == null)
+ {
+ // Error
+ Console.WriteLine("Error initializing image cache!");
+ return null;
+ }
+
+ // Draw gradient background
+ CoreGraphicsHelper.FillGradient(context, bounds, _colorGradient1, _colorGradient2);
+
+ // Declare variables
+ float x1 = 0;
+ float x2 = 0;
+ float leftMin = 0;
+ float leftMax = 0;
+ float rightMin = 0;
+ float rightMax = 0;
+ float mixMin = 0;
+ float mixMax = 0;
+ float leftMaxHeight = 0;
+ float leftMinHeight = 0;
+ float rightMaxHeight = 0;
+ float rightMinHeight = 0;
+ float mixMaxHeight = 0;
+ float mixMinHeight = 0;
+ int historyIndex = 0;
+ int historyCount = 0;
+ float lineWidth = 0;
+ float lineWidthPerHistoryItem = 0;
+ int nHistoryItemsPerLine = 0;
+ float desiredLineWidth = 0.5f;
+ WaveDataMinMax[] subset = null;
+
+ historyCount = _waveDataCache[audioFilePath].Count;
+
+ // Find out how many samples are represented by each line of the wave form, depending on its width.
+ // For example, if the history has 45000 items, and the control has a width of 1000px, 45 items will need to be averaged by line.
+ lineWidthPerHistoryItem = boundsWaveForm.Width / (float)historyCount;
+
+ // Check if the line width is below the desired line width
+ if (lineWidthPerHistoryItem < desiredLineWidth)
+ {
+ // Try to get a line width around 0.5f so the precision is good enough and no artifacts will be shown.
+ while (lineWidth < desiredLineWidth)
+ {
+ // Increment the number of history items per line
+ //Console.WriteLine("Determining line width (lineWidth: " + lineWidth.ToString() + " desiredLineWidth: " + desiredLineWidth.ToString() + " nHistoryItemsPerLine: " + nHistoryItemsPerLine.ToString() + " lineWidthPerHistoryItem: " + lineWidthPerHistoryItem.ToString());
+ nHistoryItemsPerLine++;
+ lineWidth += lineWidthPerHistoryItem;
+ }
+ nHistoryItemsPerLine--;
+ lineWidth -= lineWidthPerHistoryItem;
+ }
+ else
+ {
+ // The lines are larger than 0.5 pixels.
+ lineWidth = lineWidthPerHistoryItem;
+ nHistoryItemsPerLine = 1;
+ }
+
+ Console.WriteLine("WaveFormView - historyItemsPerLine: " + nHistoryItemsPerLine.ToString());
+
+ context.SetStrokeColor(new CGColor(1, 1, 0.5f, 1));
+ context.SetLineWidth(0.5f);
+ //context.SetLineWidth(lineWidth);
+
+ for (float i = 0; i < boundsWaveForm.Width; i += lineWidth)
+ {
+ // Round to 0.5
+ //i = (float)Math.Round(i * 2) / 2;
+ float iRound = (float)Math.Round(i * 2) / 2;
+
+ // Determine the maximum height of a line (+/-)
+ //Console.WriteLine("WaveForm - Rendering " + i.ToString() + " (rnd=" + iRound.ToString() + ") on " + widthAvailable.ToString());
+ float heightToRenderLine = 0;
+ if (displayType == WaveFormDisplayType.Stereo)
+ heightToRenderLine = boundsWaveForm.Height / 4;
+ else
+ heightToRenderLine = boundsWaveForm.Height / 2;
+
+ // Determine x position
+ x1 = i;
+ x2 = i;
+
+ if (nHistoryItemsPerLine > 1)
+ {
+ if (historyIndex + nHistoryItemsPerLine > historyCount)
+ {
+ // Create subset with remaining data
+ subset = new WaveDataMinMax[historyCount - historyIndex];
+ _waveDataCache[audioFilePath].CopyTo(historyIndex, subset, 0, historyCount - historyIndex);
+ }
+ else
+ {
+ subset = new WaveDataMinMax[nHistoryItemsPerLine];
+ _waveDataCache[audioFilePath].CopyTo(historyIndex, subset, 0, nHistoryItemsPerLine);
+ }
+
+ leftMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Left);
+ leftMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Left);
+ rightMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Right);
+ rightMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Right);
+ mixMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Mix);
+ mixMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Mix);
+ }
+ else
+ {
+ leftMin = _waveDataCache[audioFilePath][historyIndex].leftMin;
+ leftMax = _waveDataCache[audioFilePath][historyIndex].leftMax;
+ rightMin = _waveDataCache[audioFilePath][historyIndex].rightMin;
+ rightMax = _waveDataCache[audioFilePath][historyIndex].rightMax;
+ mixMin = _waveDataCache[audioFilePath][historyIndex].mixMin;
+ mixMax = _waveDataCache[audioFilePath][historyIndex].mixMax;
+ }
+
+ leftMaxHeight = leftMax * heightToRenderLine;
+ leftMinHeight = leftMin * heightToRenderLine;
+ rightMaxHeight = rightMax * heightToRenderLine;
+ rightMinHeight = rightMin * heightToRenderLine;
+ mixMaxHeight = mixMax * heightToRenderLine;
+ mixMinHeight = mixMin * heightToRenderLine;
+
+ // Determine display type
+ if (displayType == WaveFormDisplayType.LeftChannel ||
+ displayType == WaveFormDisplayType.RightChannel ||
+ displayType == WaveFormDisplayType.Mix)
+ {
+ // Calculate min/max line height
+ float minLineHeight = 0;
+ float maxLineHeight = 0;
+
+ // Set mib/max
+ if (displayType == WaveFormDisplayType.LeftChannel)
+ {
+ minLineHeight = leftMinHeight;
+ maxLineHeight = leftMaxHeight;
+ }
+ else if (displayType == WaveFormDisplayType.RightChannel)
+ {
+ minLineHeight = rightMinHeight;
+ maxLineHeight = rightMaxHeight;
+ }
+ else if (displayType == WaveFormDisplayType.Mix)
+ {
+ minLineHeight = mixMinHeight;
+ maxLineHeight = mixMaxHeight;
+ }
+
+ // ------------------------
+ // Positive Max Value
+
+ // Draw positive value (y: middle to top)
+
+ context.StrokeLineSegments(new PointF[2] {
+ new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine - maxLineHeight)
+ });
+
+ // ------------------------
+ // Negative Max Value
+
+ // Draw negative value (y: middle to height)
+ context.StrokeLineSegments(new PointF[2] {
+ new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine + (-minLineHeight))
+ });
+ }
+ else if (displayType == WaveFormDisplayType.Stereo)
+ {
+ // -----------------------------------------
+ // LEFT Channel - Positive Max Value
+
+ // Draw positive value (y: middle to top)
+ context.StrokeLineSegments(new PointF[2] {
+ new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine - leftMaxHeight)
+ });
+
+ // -----------------------------------------
+ // LEFT Channel - Negative Max Value
+
+ // Draw negative value (y: middle to height)
+ context.StrokeLineSegments(new PointF[2] {
+ new PointF(x1, heightToRenderLine), new PointF(x2, heightToRenderLine + (-leftMinHeight))
+ });
+
+ // -----------------------------------------
+ // RIGHT Channel - Positive Max Value
+
+ // Multiply by 3 to get the new center line for right channel
+ // Draw positive value (y: middle to top)
+ context.StrokeLineSegments(new PointF[2] {
+ new PointF(x1, (heightToRenderLine * 3)), new PointF(x2, (heightToRenderLine * 3) - rightMaxHeight)
+ });
+
+ // -----------------------------------------
+ // RIGHT Channel - Negative Max Value
+
+ // Draw negative value (y: middle to height)
+ context.StrokeLineSegments(new PointF[2] {
+ new PointF(x1, (heightToRenderLine * 3)), new PointF(x2, (heightToRenderLine * 3) + (-rightMinHeight))
+ });
+ }
+
+ // Increment the history index; pad the last values if the count is about to exceed
+ if (historyIndex < historyCount - 1)
+ historyIndex += nHistoryItemsPerLine;
+ }
+
+ // Get image from context
+ imageCache = UIGraphics.GetImageFromCurrentImageContext();
+ UIGraphics.EndImageContext();
+ return imageCache;
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine("Error while creating image cache: " + ex.Message);
+ }
+ return null;
+ }, TaskCreationOptions.LongRunning).ContinueWith(t => {
+ Console.WriteLine("WaveFormCacheManager - Created image successfully.");
+ OnGenerateWaveFormBitmapEnded(new GenerateWaveFormEventArgs(){
+ AudioFilePath = audioFilePath,
+ Zoom = zoom,
+ DisplayType = displayType,
+ Image = t.Result
+ });
+ }, TaskScheduler.FromCurrentSynchronizationContext());
+
+ }
+ }
+}
diff --git a/MPfm/MPfm.iOS/MPfm.iOS.csproj b/MPfm/MPfm.iOS/MPfm.iOS.csproj
index d2fc6fc1..331429a9 100644
--- a/MPfm/MPfm.iOS/MPfm.iOS.csproj
+++ b/MPfm/MPfm.iOS/MPfm.iOS.csproj
@@ -185,6 +185,9 @@
MarkerDetailsViewController.cs
+
+
+
@@ -232,6 +235,7 @@
+