how to control background audio from Dependecy-service in xamarin.ios - ios

I made a Dependency-Service for playing audio and background control in Xamarin.ios .
Audio was executed in background well.
but, Lockscreen controller was not shown.
I don't know what is problem.
I registered "UIBackgroundMode" and "audio" in "info.plist"
and set the AVAudioSession as playback.
I used play_pause(string url) function for playing audio and setting the MPNowPlayingInfo.
[assembly: Dependency(typeof(AudioService))]
namespace BibleHymn.iOS
{
class AudioService : IAudio
{
private bool isPlaying = false;
private bool interrupted = false;
int audioNum = 0;
AVFoundation.AVPlayer player;
public AudioService()
{
var avSession = AVAudioSession.SharedInstance();
avSession.SetCategory(AVAudioSessionCategory.Playback);
NSError activationError = null;
avSession.SetActive(true, out activationError);
}
public bool Play_Pause(string url)
{
if(player == null)
{
this.player = new AVFoundation.AVPlayer();
this.player = AVFoundation.AVPlayer.FromUrl(Foundation.NSUrl.FromString(url));
this.player.Play();
UpdateNotification();
}
isPlaying !=isPlaying;
return isPlaying;
}
public void UpdateNotification()
{
var item = new MPNowPlayingInfo
{
Title = "My Title"
};
MPNowPlayingInfoCenter.DefaultCenter.NowPlaying = item;
Device.BeginInvokeOnMainThread(() => {
UIKit.UIApplication.SharedApplication.BeginReceivingRemoteControlEvents();
});
}
}
I used play_pause from viewModel through button control's command.
init like ...
public IAudio streamService = DependencyService.Get<IAudio>();
and in command ...
streamService.Play_Pause(url);

You need to register background support for Audio in the info.plist by following screenshot
Also did you import Mediaplayer.framework

Related

Gluon iOS audio Player

I've been following various answers to various questions on the subject and I've come to a result and some code which looks like it works. I'm getting stuck with the NSURL part of it. I've got 2 mp3 tracks in the assets folder of my iOS gluon project. I've made the IOSAudioService Class to handle the playback. and I'm passing an argument from the play button in the view to the Play() method. Everything other than the actual file is registering as working. I'm getting an NSError, which from looking at the code is a nil value, so either the argument isn't passing correctly or it can't find the file. Code below.
public AVAudioPlayer backgroundMusic;
private double currentPosition;
NSURL songURL = null;
#Override
public void Play(String filename){
songURL = new NSURL(filename);
try {
if (backgroundMusic != null) {
Resume();
}
else {
//Start the audio at the beginning.
currentPosition = 0;
backgroundMusic = new AVAudioPlayer(songURL);
//create the mendia player and assign it to the audio
backgroundMusic.prepareToPlay();
backgroundMusic.play();}
//catch the audio error
} catch(NSErrorException e) {
System.out.println("error: " + e);
}
}
#Override
public void Stop() {
backgroundMusic.stop();
backgroundMusic = null;
}
#Override
public void Pause() {
currentPosition = backgroundMusic.getCurrentTime();
backgroundMusic.pause();
}
#Override
public void Resume() {
backgroundMusic.setCurrentTime(currentPosition);
backgroundMusic.play();
}
try {
services = (AudioService) Class.forName("com.gluonhq.charm.down.plugins.ios.IOSAudioService").newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
System.out.println("Error " + ex);
}
I'm getting the error at the catch block for NSExceptionError e.
if (services != null) {
final HBox hBox = new HBox(10,
MaterialDesignIcon.PLAY_ARROW.button(e -> services.Play("/audio.mp3")),
MaterialDesignIcon.PAUSE.button(e -> {
if (!pause) {
services.Pause();
pause = true;
} else {
services.Resume();
pause = false;
}
}),
MaterialDesignIcon.STOP.button(e -> services.Stop()));
//set the HBox alignment
hBox.setAlignment(Pos.CENTER);
hBox.getStyleClass().add("hbox");
//create and set up a vbox to include the image, audio controls and the text then set the alignment
final VBox vBox = new VBox(5, Image(), hBox, text1);
vBox.setAlignment(Pos.CENTER);
setCenter(new StackPane(vBox));
} else {
//start an error if service is null
setCenter(new StackPane(new Label("Only for Android")));
}
Services.get(LifecycleService.class).ifPresent(s -> s.addListener(LifecycleEvent.PAUSE, () -> services.Stop()));
}
I've also follow the advice on creating the service factory class and the interface from Audio performance with Javafx for Android (MediaPlayer and NativeAudioService) taking out the add audio element and I'm intending to do this on a view by view basis if possible.
Problem solved after must fiddling and looking in the Javadocs.Solved by adding/replacing some code with the below.
songURL = new NSURL("file:///" + NSBundle.getMainBundle().findResourcePath(filename, "mp3"));
try {
songURL.checkResourceIsReachable();}
catch(NSErrorException d) {
System.out.println("Song not found!" + d);
}

Showing "Import with Instagram" in UIActivityViewController

I am trying to add Instagram in "Share To" functionality in my app. I have seen the Instagram's iPhone hooks documents. I have created custom UIActivty which works fine but my question is, is there a way to add "Import with Instagram" functionality as it can be seen in iOS's Photos app iOS Photo App:
In my app for some reason, it does not show that "Import with Instagram". my app Share view :
I do not want to share only with Instagram so no ".igo"
EDIT: All of this is specifically for iOS versions < 10. For some reasons Instagram Share Extension works fine (for my app) in devices with iOS >= 10.
EDIT: I am trying to share image and video with ".jpeg" and ".mov" formats respectively
I have seen/read that Instagram added share extension in release 8.2, so technically all the apps should show "Instagram" in share tray, i.e. it can be seen in Google Photos app.
public void NativeShareImage(UIView sourceView, CGRect sourceRect,
UIImage image, string shareCaption, string emailSubject)
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.jpg");
NSError err = null;
using(var imgData = image.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Image saved before native share as {FileName}", filename);
}
else
{
Logger.Error("Image NOT saved before native share as to path {FileName}. {Error}", filename, err.Description);
return;
}
}
// this are the items that needs to be shared
// Instagram ignores the caption, that is known
var activityItems = new List<NSObject>
{
new NSString(shareCaption),
new NSUrl(new Uri(filename).AbsoluteUri)
};
// Here i add the custom UIActivity for Instagram
UIActivity[] applicationActivities =
{
new InstagramActivity(image, sourceRect, sourceView),
}
var activityViewController = new UIActivityViewController(activityItems.ToArray(), applicationActivities);
activityViewController.SetValueForKey(new NSString(emailSubject), new NSString("subject"));
activityViewController.CompletionWithItemsHandler = (activityType, completed, returnedItems, error) =>
{
UserSharedTo(activityType, completed);
};
// Hide some of the less used activity types so that Instagram shows up in the list. Otherwise it's pushed off the activity view
// and the user has to scroll to see it.
activityViewController.ExcludedActivityTypes = new[] { UIActivityType.AssignToContact, UIActivityType.CopyToPasteboard, UIActivityType.Print };
if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone)
{
PresentViewController(activityViewController, true, null);
}
else
{
activityViewController.ModalPresentationStyle = UIModalPresentationStyle.Popover;
PresentViewController(activityViewController, true, null);
// Get the popover presentation controller and configure it.
UIPopoverPresentationController presentationController = activityViewController.PopoverPresentationController;
presentationController.PermittedArrowDirections = UIPopoverArrowDirection.Down;
presentationController.SourceRect = sourceRect;
presentationController.SourceView = sourceView;
}
}
// when opening custom activity use ".igo" to only show instagram
public class InstagramActivity : UIActivity
{
public InstagramActivity(UIImage imageToShare, CGRect frame, UIView view, string shareCaption = "")
{
_ImageToShare = imageToShare;
_Frame = frame;
_View = view;
}
public override UIImage Image { get { return UIImage.FromBundle("Instagram"); } }
public override string Title { get { return "Instagram"; } }
public override NSString Type { get { return new NSString("PostToInstagram"); } }
public string Caption { get; set; }
public override bool CanPerform(NSObject[] activityItems)
{
return UIApplication.SharedApplication.CanOpenUrl(NSUrl.FromString("instagram://app"));
}
public override void Prepare(NSObject[] activityItems)
{
}
public override void Perform()
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.igo");
NSError err = null;
using(var imgData = _ImageToShare.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Instagram image saved as {FileName}", filename);
}
else
{
Logger.Error("Instagram image NOT saved as to path {FileName}. {Error}", filename, err.Description);
Finished(false);
return;
}
}
var url = NSUrl.FromFilename(filename);
_DocumentController = UIDocumentInteractionController.FromUrl(url);
_DocumentController.DidEndSendingToApplication += (o, e) => Finished(true);
_DocumentController.Uti = "com.instagram.exclusivegram";
if(!string.IsNullOrEmpty(ShareCaption))
{
_DocumentController.Annotation = NSDictionary.FromObjectAndKey(new NSString(ShareCaption), new NSString("InstagramCaption"));
}
_DocumentController.PresentOpenInMenu(_Frame, _View, true);
}
UIImage _ImageToShare;
CGRect _Frame;
UIView _View;
UIDocumentInteractionController _DocumentController;
}

MonoTouch Load image in background

I am having a problem trying to load an image and display it using System.Threading.Task
My Code is as follows
Task DownloadTask { get; set; }
public string Instrument { get; set; }
public PriceChartViewController(string Instrument) {
this.Instrument = Instrument;
DownloadTask = Task.Factory.StartNew(() => { });
}
private void LoadChart(ChartType chartType) {
NSData data = new NSData();
DownloadTask = DownloadTask.ContinueWith(prevTask => {
try {
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
NSUrl nsUrl = new NSUrl(chartType.Uri(Instrument));
data = NSData.FromUrl(nsUrl);
}
finally {
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
}
});
DownloadTask = DownloadTask.ContinueWith(t => {
UIImage image = new UIImage(data);
chartImageView = new UIImageView(image);
chartImageView.ContentScaleFactor = 2f;
View.AddSubview(chartImageView);
this.Title = chartType.Title;
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
}
The second Continue with does not seem to be being called?
Initially my code looked like the following without the background processing and it worked perfectly.
private void oldLoadChart(ChartType chartType) {
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
NSUrl nsUrl = new NSUrl(chartType.Uri(Instrument));
NSData data = NSData.FromUrl(nsUrl);
UIImage image = new UIImage(data);
chartImageView = new UIImageView(image);
chartImageView.ContentScaleFactor = 2f;
View.AddSubview(chartImageView);
this.Title = chartType.Title;
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
}
Does anyone know what I am doing wrong?
Your first part of the thread is crashing, that's why it is never getting to the second:
You are calling UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
from a non-UI thread. Setting the network activity indicator is a UI operation and may only be executed on the main thread. You'll have to wrap it in InvokeOnMainThread().
If you add a try-catch, you'll see the exception.
Add catch before finally. Maybe you just have an exception so workflow doesn't go any further.

AVCaptureSession CMSampleBuffer issue

I have a problem with releasing memory from CMSampleBuffer in my AVCaptureSession camera.
This is my code for setting capture session. If I dispose imageDataSampleBuffer, app crashes.
using MonoTouch.CoreVideo;
using MonoTouch.CoreMedia;
using MonoTouch.AVFoundation;
using MonoTouch.ImageIO;
using MonoTouch.UIKit;
using MonoTouch.CoreFoundation;
using MonoTouch.Foundation;
using System.Drawing;
using System;
namespace myNamespace
{
public class AVFoundationCamera : UIViewController
{
public AVFoundationCamera (CameraController parView)
{
parentView = parView;
}
NSError error;
AVCaptureSession session;
AVCaptureDevice device;
AVCaptureDeviceInput input;
AVCaptureStillImageOutput output;
AVCaptureVideoPreviewLayer captureVideoPreviewLayer;
NSDictionary outputSettings;
AVCaptureConnection captureConnection;
UIButton buttCaptureImage;
public UIImageView imageV;
NSData imageData;
CameraController parentView;
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
CreateControls();
SetupSession();
}
public override void DidReceiveMemoryWarning ()
{
imageData.Dispose();
session.Dispose();
device.Dispose();
input.Dispose();
output.Dispose();
captureVideoPreviewLayer.Dispose();
base.DidReceiveMemoryWarning ();
}
private void CreateControls()
{
imageV = new UIImageView(new RectangleF(0, 0, UIScreen.MainScreen.ApplicationFrame.Width, UIScreen.MainScreen.ApplicationFrame.Height - UIApplication.SharedApplication.StatusBarFrame.Height));
View.AddSubview(imageV);
buttCaptureImage = UIButton.FromType(UIButtonType.RoundedRect);
buttCaptureImage.Frame = new RectangleF(0, 60, 150, 30);
buttCaptureImage.SetTitle("Take a photo", UIControlState.Normal);
buttCaptureImage.TouchUpInside += HandleButtCaptureImageTouchUpInside;
View.AddSubview(buttCaptureImage);
}
void HandleButtCaptureImageTouchUpInside (object sender, EventArgs e)
{
captureConnection = null;
foreach (AVCaptureConnection capConn in output.Connections)
{
foreach (AVCaptureInputPort port in capConn.inputPorts)
{
if (port.MediaType == AVMediaType.Video)
{
captureConnection = capConn;
break;
}
}
if (captureConnection != null)
break;
}
output.CaptureStillImageAsynchronously(captureConnection, HandleAVCaptureCompletionHandlercompletionHandler);
buttCaptureImage.Enabled = false;
}
void HandleAVCaptureCompletionHandlercompletionHandler (CMSampleBuffer imageDataSampleBuffer, NSError error)
{
try
{
using (var pool = new NSAutoreleasePool ()) {
imageData = AVCaptureStillImageOutput.JpegStillToNSData(imageDataSampleBuffer);
//imageDataSampleBuffer.Dispose();
parentView.DismissModalViewControllerAnimated(true);
parentView.HandlePickedImage(imageData);
session.StopRunning();
}
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
}
private void SetupSession()
{
session = new AVCaptureSession();
session.BeginConfiguration();
session.SessionPreset = AVCaptureSession.PresetPhoto;
captureVideoPreviewLayer = new AVCaptureVideoPreviewLayer(session);
captureVideoPreviewLayer.Frame = imageV.Bounds;
imageV.Layer.AddSublayer(captureVideoPreviewLayer);
device = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);
input = new AVCaptureDeviceInput(device, out error);
session.AddInput(input);
output = new AVCaptureStillImageOutput();
output.OutputSettings = NSDictionary.FromObjectAndKey(new NSString("AVVideoCodecKey"), new NSString("AVVideoCodecJPEG"));
session.AddOutput(output);
session.CommitConfiguration();
session.StartRunning();
}
}
}
This is just a regular camera for taking photos. I tried with UIImagePickerController you posted here: https://github.com/migueldeicaza/TweetStation/blob/master/TweetStation/UI/Camera.cs which eliminates UIImagePickerController bug but whenever I click "Take a photo" button, preview window shows up which allocates memory. If I press "Retake", memory is being released, but in FinishedPiCkingMedia event handler I can't release it. So, after few photos it crashes.
Any solution works for me, but it would be great to see what I'm doing wrong.
Thank you once again.
This was a bug in MonoTouch.
There is a workaround you can use until you get the fix:
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static void CFRetain (IntPtr handle);
void HandleAVCaptureCompletionHandlercompletionHandler (CMSampleBuffer imageDataSampleBuffer, NSError error)
{
try {
CFRetain (imageDataSampleBuffer.Handle);
(...)
} finally {
imageDataSampleBuffer.Dispose ();
}
}
I've added a Dispose call, there might be a limited amount of buffers available, and this way you ensure that the app doesn't exhaust them (since it may take a little time before the GC would free it automatically)
Also note that you should remove the workaround once you install a MonoTouch version with the real fix, since you'll leak the buffers otherwise.

AIR application: Cannot access a property or method of a null object reference. No idea what's causing this

I'm using code for the open source MP3 player Howler and trying to port it to a Spark MobileApplication type. I'm getting a null pointer exception and I have no idea what's causing it. I've tried debugging extensively with breakpoints at what I think is causing the error, and set breakpoints in the untouched Howler project but all the variables in scope seem to be identical between my non-working project, and the Howler project. The only thing I can think of is that Howler uses MX components and I am using spark. I've pasted all my code below (which is very long) but I've bolded the lines that are throwing the error. The error occurs immediately after I choose a folder in the browse folder dialog.
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Home"
xmlns:Comp="components.*" xmlns:display="flash.display.*">
<s:Button id="browse" x="546" y="43" label="Open Directory" click="browseForFolder()"/>
<s:DataGrid id="dgPlaylist" width="82" height="141" itemRenderer="components.DurationFormatter">
</s:DataGrid>
<s:Button id="btnForward" x="187" y="126" label="Forward"/>
<s:Button id="btnPause" x="90" y="39" label="Pause"/>
<s:Button id="btnBack" x="55" y="166" label="Back" click="changeSoundIndex(-1)"/>
<s:Button id="btnPlay" x="336" y="199" label="Button"/>
<s:Button id="btnStop" x="366" y="89" label="Stop"/>
<s:VScrollBar id="sldrPosition" x="280" y="43" mouseDown="thumbTimer.stop()"
mouseUp="thumbTimer.start()"
/>
<s:VScrollBar id="sldrVolume" x="265" y="234" change="ChangeSoundTransform()"
/>
<s:RichText id="txtID3" x="236" y="41" width="99">
</s:RichText>
<fx:Declarations>
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.ericcarlisle.PlayList;
import com.ericcarlisle.PlayModes;
import com.ericcarlisle.Utility;
import flash.desktop.NativeDragManager;
import flash.media.Sound;
import mx.core.UIComponent;
import mx.events.CloseEvent;
import mx.events.DragEvent;
import org.osmf.traits.PlayTrait;
import spark.components.DataGrid;
// Player properties
private var playMode:String = "STOP";
private var volume:uint;
private const panning:int = 0;
private var selectedFileCount:uint;
private var loadedFileCount:uint;
private var soundIndex:uint;
// Player objects
private var SoundObj:Sound;
private var Channel:SoundChannel;
private var Transform:SoundTransform;
private var thumbTimer:Timer;
private var PlayList:com.ericcarlisle.PlayList;
private var SoundFilter:FileFilter = new FileFilter("Sounds", "*.mp3;*.wav");
//private var PlaylistFilter:FileFilter = new FileFilter("Sounds", "*.pls;*.m3u");
// Visualization objects.
private var Spectrum:ByteArray;
private const VISUALIZER_HEIGHT:Number = 50;
private const VISUALIZER_COLOR:Number = 0x336699;
// ID3 and other metadata
private var ID3:ID3Info;
private var Duration:int;
/*---------- PLAYER INITIALIZER ----------*/
// Initialization function used to add event handlers and set initial settings.
private function init():void
{
// Set player initial settings.
playMode = PlayModes.STOP;
selectedFileCount = 0;
loadedFileCount = 0;
soundIndex = 0;
// Set initial application height.
//this.height= cvsControlBar.height + cvsPlayer.height;
// Set volume.
volume = sldrVolume.value;
// Instantiate sound objects.
Channel = new SoundChannel();
Transform = new SoundTransform(volume/100, panning);
PlayList = new com.ericcarlisle.PlayList();
// Bind playlist data to datagrid.
dgPlaylist.dataProvider = PlayList.Sounds;
// Create a timer to control the song position hslider.
thumbTimer = new Timer(500);
thumbTimer.addEventListener(TimerEvent.TIMER, onTimerTick);
// Create event handlers for application.
this.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onPlayerDragInto);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onPlayerDropInto);
this.addEventListener(InvokeEvent.INVOKE, onInvoke);
}
/*---------- DRAG/DROP & FILE MANAGEMENT ----------*/
private function onInvoke(event:InvokeEvent):void
{
if (event.arguments.length > 0)
{
var file:File;
var files:Array = new Array();
for (var i:int = 0; i < event.arguments.length; i++)
{
file = new File(event.arguments[i]);
files.push(file);
}
if (PlayList.Sounds.length > 0) removeAllSounds();
loadFiles(files);
}
}
// Handles file selection event dispatched by browse dialog.
private function onFileSelect(event:FileListEvent):void
{
loadFiles(event.files);
}
// Handles folder selection event dispatched by browse dialog.
private function onDirectorySelect(event:Event):void
{
var directory:File = event.target as File;
**loadFiles(directory.getDirectoryListing());**
}
// Loads a batch of files into the playlist.
private function loadFiles(files:Array):void
{
var file:File;
// Count the number of files selected. Only accept files with .mp3 extension.
selectedFileCount = 0;
for (var i:uint = 0; i < files.length; i++)
{
file = files[i];
if (file.extension == "mp3") selectedFileCount++;
}
// Reset the count on files currently loaded.
loadedFileCount = 0;
**// Set the player mode so that loaded files are played automatically.
if (PlayList.Sounds.length == 0) playMode = PlayModes.LOADTOPLAY;**
// Load files as sound objects.
for(var j:uint = 0; j < files.length; j++)
{
file = files[j];
if (file.extension == "mp3" || file.extension == "wav")
{
var sound:Sound = new Sound(new URLRequest(file.url));
sound.addEventListener(Event.ID3,onID3);
}
}
}
// Presents file browse (multiple file) dialog.
private function browseForFiles():void
{
var SoundFile:File = new File();
SoundFile.browseForOpenMultiple("Open", [SoundFilter]);//, PlaylistFilter]);
SoundFile.addEventListener(FileListEvent.SELECT_MULTIPLE, onFileSelect);
}
// Presents file browse (folder) dialog.
private function browseForFolder():void
{
var directory:File = File.documentsDirectory;
directory.browseForDirectory("Select Directory");
directory.addEventListener(Event.SELECT, onDirectorySelect);
}
// Accept files dragged into player.
private function onPlayerDragInto(event:Event):void
{
NativeDragManager.acceptDragDrop(this);
}
// Manages files dropped into player.
private function onPlayerDropInto(event:NativeDragEvent):void
{
// Accept only files.
if (event.clipboard.hasFormat(ClipboardFormats.FILE_LIST_FORMAT))
{
// Parse dragged contents into array of files.
var dragFiles:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
// Load the files.
loadFiles(dragFiles);
}
}
/*---------- SOUND MANAGEMENT ----------*/
private function loadSound():void
{
SoundObj = new Sound();
SoundObj.load(new URLRequest(PlayList.Sounds[soundIndex]["url"]));
SoundObj.addEventListener(Event.COMPLETE, onSoundLoaded);
}
private function onSoundLoaded(event:Event):void
{
// Retrieve data for current sound from playlist.
var soundData:Object = PlayList.Sounds[soundIndex];
// Place ID3 information into the readout panel.
//txtID3.htmlText = Utility.ReadoutHTML(soundData["title"], soundData["track"], soundData["album"], soundData["artist"], soundData["year"], soundData["duration"]);
// Configure the holizontal slider to act as a playhead.
sldrPosition.maximum = soundData["duration"];
// Set the selected row in the playlist display.
dgPlaylist.selectedIndex = soundIndex;
// Start the player if the mode is correct.
if (playMode == PlayModes.LOADTOPLAY)
{
playSound();
}
else
{
playMode = PlayModes.LOADED;
}
}
// Plays the current sound.
public function playSound():void
{
// Load sound into channel.
Channel.stop();
Channel = SoundObj.play(sldrPosition.value,0,Transform);
playMode = PlayModes.PLAY;
// Start position timer.
thumbTimer.start();
// Configure UI controls.
btnPlay.visible = false;
btnPause.visible = true;
sldrPosition.enabled = true;
btnPlay.enabled = true;
btnStop.enabled = true;
setBackForwardButtons();
Channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
}
private function setBackForwardButtons():void
{
if (soundIndex == PlayList.Sounds.length-1 || PlayList.Sounds.length == 0)
{
btnForward.enabled = false;
}
else
{
btnForward.enabled = true;
}
if (soundIndex == 0 || PlayList.Sounds.length == 0)
{
btnBack.enabled = false;
}
else
{
btnBack.enabled = true;
}
}
// Stops the current sound.
public function stopSound():void
{
Channel.stop();
thumbTimer.stop();
sldrPosition.value = 0;
playMode = PlayModes.STOP;
//Visualizer.graphics.clear();
btnPlay.visible = true;
btnPause.visible = false;
}
// Pause a playing sound.
private function pauseSound():void
{
Channel.stop();
thumbTimer.stop();
btnPlay.visible = true;
btnPause.visible = false;
}
// Change the sound index
private function changeSoundIndex(delta:int):void
{
stopSound();
playMode = PlayModes.LOADTOPLAY;
soundIndex = soundIndex + delta;
loadSound();
}
// Change the volume and panning via the sound transform object.
private function ChangeSoundTransform():void
{
volume = Math.round(sldrVolume.value);
Channel.soundTransform = new SoundTransform(volume/100, panning);
}
// Handles event for sound completing.
private function onSoundComplete(event:Event):void
{
stopSound();
soundIndex++;
if (soundIndex < PlayList.Sounds.length)
{
playMode = PlayModes.LOADTOPLAY;
loadSound();
}
}
// Load ID3 information into local variables.
// Update the readout panel.
// Configure position slider.
private function onID3(event:Event):void
{
var sound:Sound = Sound(event.target);
ID3 = ID3Info(sound.id3);
Duration = Math.floor(sound.length);
// Load sound id3 data into the playlist.
PlayList.AddSound(ID3.songName, ID3.album, ID3.artist, ID3.track, ID3.year, ID3.genre, Duration, sound.url);
// Increment the loaded file count.
loadedFileCount++;
if (loadedFileCount == selectedFileCount * 2)
{
// Refresh the playlist so that new results will be visually displayed.
PlayList.Sounds.refresh();
// Set the count properties.
selectedFileCount = 0;
loadedFileCount = 0;
soundIndex = 0;
if (playMode == PlayModes.LOADTOPLAY) loadSound();
}
}
/*---------- VISUALIZATION ----------*/
private function UpdateVisualizer():void
{
// Instantiate a new byte array to contain spectrum data.
Spectrum = new ByteArray();
// Clear the visualizer graphics.
//Visualizer.graphics.clear();
// Dump the spectrum data into the byte array.
SoundMixer.computeSpectrum(Spectrum,false,0);
var f:Number;
var i:int;
var ave:int;
//Visualizer.graphics.lineStyle(1, VISUALIZER_COLOR,1);
//Visualizer.graphics.beginFill(VISUALIZER_COLOR, 0.75);
for (i = 0; i < 512; i=i+10)
{
f = Spectrum.readFloat();
//Visualizer.drawRoundRect(Math.floor(i*0.7) + 7, cvsReadout.height - 10, 4, -Math.abs(f) * (cvsReadout.height-10));
}
//Visualizer.graphics.endFill();
}
// Updates the position of the hslider thumb.
private function onTimerTick(event:TimerEvent):void
{
sldrPosition.value = Math.round(Channel.position);
}
// Update the wave visualizer if the sound is playing.
private function onEnterFrame(event:Event):void
{
if (playMode == PlayModes.PLAY)
{
UpdateVisualizer();
}
}
// Show application information.
private function showHowlerInfo():void
{
//cvsAbout.visible = true;
}
private function togglePlayList():void
{
}
private function onItemDoubleClick(event:Event):void
{
this.playMode = PlayModes.LOADTOPLAY;
thumbTimer.stop();
sldrPosition.value = 0;
loadSound();
}
/*---------- ERROR HANDLING ----------*/
// Handles IO errors.
private function onIOError(event:IOErrorEvent):void
{
//Alert.show("File load error: " + event.text);
}
private function startMove():void
{
stage.nativeWindow.startMove();
}
private function unloadSound():void
{
stopSound();
txtID3.text = "";
btnPlay.visible = true;
btnPause.visible = false;
btnPlay.enabled = false;
btnStop.enabled = false;
}
private function removeSound():void
{
var index:int = dgPlaylist.selectedIndex;
if (index >= 0)
{
if (index == soundIndex)
{
unloadSound();
}
PlayList.RemoveSoundAt(index);
PlayList.Sounds.refresh();
setBackForwardButtons();
}
}
private function removeAllSounds():void
{
unloadSound();
PlayList.Sounds.removeAll();
PlayList.Sounds.refresh();
setBackForwardButtons();
}
private function onKeyDown(event:KeyboardEvent):void
{
if (event.charCode.toString() == "8" || event.charCode.toString() == "127")
{
removeSound();
}
}
]]>
</fx:Script>
</s:View>
I'm new to Flex so I don't know if this is causing the problem or not, but the Howler app uses an MX:DataGrid defined like this:
<mx:DataGrid x="6"
y="7"
width="388"
height="310"
id="dgPlaylist"
keyDown="onKeyDown(event)"
dragMoveEnabled="true"
doubleClickEnabled="true"
dragEnabled="true"
dropEnabled="true"
dragComplete="onPlaylistDragDrop(event)"
itemDoubleClick="onItemDoubleClick(event)">
<mx:columns>
<mx:DataGridColumn width="168" headerText="Title" dataField="title" />
<mx:DataGridColumn width="160" headerText="Album" dataField="album"/>
<mx:DataGridColumn width="60" headerText="Duration" dataField="duration" textAlign="right" itemRenderer="components.DurationFormatter"/>
</mx:columns>
</mx:DataGrid>
It has additional columns that I'm not using. Could this be the cause?
We are going to need the error message. Generally what you are experiencing is called a null pointer exception ( just like the error said ). Which basically means you tried to access a property of an object that is null. For example, running the following code will cause the same problem
var arrayCollection:ArrayCollection;
arrayCollection.addItem( new Object() );
To fix your error, you should find the object that the error is referring to in your code and make sure it isn't null when your program executes whatever line it is complaining about, or add an if-loop in the event it is null.

Resources