I've been working with the iOS LoadPresetDemo sample code - if loads AUPreset files to configure different types of samplers (pretty cool) - and have run into a question/issue.
The demo code runs fine but when I try to reuse the .aupreset files in test project built from scratch, the Trombone.aupreset doesn't work. Digging into it, I noticed what seems like oddness with the audio sample paths in the .aupreset file.
The paths in the plist (images below) point to:
file://localhost//Library/Audio/Sounds/Tbone/1a%23.caf
but that is not the correct path - according to the project directory structure. There are no "Library/Audio" directories - virtual or real. So I'm confused. The Apple demo works fine but my from scratch project does not (get error -43 when trying to load the samples). The code that loads the samples (at bottom) is not doing anything to relativize the paths at runtime.
Does anyone see what I am misunderstanding here? - Thanks!
// Load a synthesizer preset file and apply it to the Sampler unit
- (OSStatus) loadSynthFromPresetURL: (NSURL *) presetURL {
CFDataRef propertyResourceData = 0;
Boolean status;
SInt32 errorCode = 0;
OSStatus result = noErr;
// Read from the URL and convert into a CFData chunk
status = CFURLCreateDataAndPropertiesFromResource (
kCFAllocatorDefault,
(__bridge CFURLRef) presetURL,
&propertyResourceData,
NULL,
NULL,
&errorCode
);
NSAssert (status == YES && propertyResourceData != 0, #"Unable to create data and properties from a preset. Error code: %d '%.4s'", (int) errorCode, (const char *)&errorCode);
// Convert the data object into a property list
CFPropertyListRef presetPropertyList = 0;
CFPropertyListFormat dataFormat = 0;
CFErrorRef errorRef = 0;
presetPropertyList = CFPropertyListCreateWithData (
kCFAllocatorDefault,
propertyResourceData,
kCFPropertyListImmutable,
&dataFormat,
&errorRef
);
// Set the class info property for the Sampler unit using the property list as the value.
if (presetPropertyList != 0) {
result = AudioUnitSetProperty(
self.samplerUnit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0,
&presetPropertyList,
sizeof(CFPropertyListRef)
);
CFRelease(presetPropertyList);
}
if (errorRef) CFRelease(errorRef);
CFRelease (propertyResourceData);
return result;
}
I had the same Problem and after 2 hours of comparing the "load preset"-Demo with my code I found the solution:
When adding the sound-folder check the options:
Copy items
Create folder references for any added folders -- and the added folders will be blue like in the "load preset"-demo-project!
The following is from Apple Technical Note TN2283 and discusses the paths issue that was initially asked about. You will obviously need to have the Sounds Folder, your assets and preset file part of your bundle.
Technical Note TN2283 AUSampler - Loading Instruments Excerpt
When the AUSampler attempts to load audio files via the paths provided in the external file refs portion of an .aupreset file or a set of individual file URLs, it will use the following rules to resolve each path:
If the audio file is found at the original path, it is loaded.
If the audio file is NOT found, the AUSampler looks to see if a path includes a portion matching "/Sounds/", "/Sampler Files/" or "/Apple Loops/" in that order.
If the path DOES NOT include one of the listed sub-paths, an error is returned.
If the path DOES include one of the listed sub-paths, the portion of the path preceding the sub-path is removed and the following directory location constants are substituted in the following order:
Bundle Directory
NSLibraryDirectory
NSDocumentDirectory
NSDownloadsDirectory
For example, in an iOS application if the original path was "/Users/geddy/Library/Audio/Sounds/MyFavoriteHeadacheSound.caf" and this path was not found, the AUSampler would then search for the audio file in the following four places:
<Bundle_Directory>/Sounds/MyFavoriteHeadacheSound.caf
<NSLibraryDirectory>/Sounds/MyFavoriteHeadacheSound.caf
<NSDocumentDirectory>/Sounds/MyFavoriteHeadacheSound.caf
<NSDownloadsDirectory>/Sounds/MyFavoriteHeadacheSound.caf
Therefore using the above example, if you were moving a preset created on the Desktop to an iOS application you could simply place the MyFavoriteHeadacheSound.caf file in a folder called "Sounds" within your application bundle and the AUSampler will find the audio file referenced by the preset.
You need to add both presets to your target so that they are part of the bundle:
That's what the line:
NSURL *presetURL = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Trombone" ofType:#"aupreset"]];
says.
Also you may want to have a look at Referencing External Audio Files at the bottom of the page.
Related
I'm using the CameraView to capture a series of short videos. I want these videos to be viewable at a later time from within the app. I'm using the MediaCaptured event to get the path of captured videos and saving these paths in a SQLite db.
The first problem is on iOS, the path is valid while the app is open, but if I close the app and open it again the path is no longer valid. I've worked around this by copying the video to the AppDataDirectory but this seems bad because I haven't figured out how to delete the original so now two copies of the video exist.
The second problem is on both iOS and Android, after some amount of time (a few days or a week or more) these paths become invalid for some unknown reason.
What is the correct way to deal with this?
private void MediaCaptured(object obj)
{
MediaCapturedEventArgs args = obj as MediaCapturedEventArgs;
string sPath = "";
switch (Device.RuntimePlatform)
{
case Device.iOS:
//On iOS args.Video.File returns a path that isn't valid when the app is restarted. To get around this issue I am copying the file to the App Data Directory.
//The drawback is there are now two video files and I can't delete the original.
var pathSplit = args.Video.File.Split('/');
sPath = Path.Combine(FileSystem.AppDataDirectory, pathSplit[pathSplit.Length - 1]);
File.Copy(args.Video.File, sPath);
//TODO Should probalby be deleting the original video but not sure how (or if its possible).
break;
case Device.Android:
sPath = args.Video.File;
break;
}
SavePathToDB(sPath);
}
I'm trying to allow users to upload photos to the server and then view them (all users can view all photos) in production (NOT development). While in development mode everything is simple - I can upload the files to the public folder and then read then from there, in production mode I don't have access to the public folder anymore (as this approach is for static accesses and not dynamic).
So, I have 2 issues:
Upload: currently I can't understand how to save the uploaded photos to a specific folder, without using an absolute path to the location where I want the photos to be saved.
Here is the code for upload (similarly to this guide - https://www.playframework.com/documentation/2.4.x/ScalaFileUpload):
def uploadPhoto = Action(parse.multipartFormData) { request =>
import play.api.mvc.MultipartFormData
import play.api.libs.Files.TemporaryFile
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
try {
val multipartForm: MultipartFormData[TemporaryFile] = request.body
val pathDev: Path = Paths.get("./public/img");
val pathProduction: Path = Paths.get("/...<[full path]>.../public/img");
val imgPath =
if (Files.exists(pathDev)) { pathDev.toString() }
else { pathProduction.toString() }
val newFile = img.get.ref.moveTo(new File(s"$imgPath/$imgName"))
// [HERE I save the path to my DB]
Ok(Json.obj("imgName" -> imgName))
} catch {
case e: Exception =>
BadRequest("unknown error")
}
}
It is unclear to me how to serve the uploaded images back to users that want to see them.
I want to dynamically change the scr in the img html tag to show the relevant image, like so: $("#img").attr("src",'assets/img/1.jpg');
But as the public folder is not available, the images are "not there" (at least until I will "stage" the project and re-run it - https://www.playframework.com/documentation/2.4.x/Assets).
I tried the following approach (https://www.playframework.com/documentation/2.4.x/ScalaStream):
I have added the following line to my conf/routes file:
GET /img/:filename controllers.MyController.getPhoto(filename)
and defined the following function in the controller:
def getPhoto(filename: String) = Action {
Ok.sendFile(new java.io.File("./img/" + filename))
}
But the browser is downloading the file instead of showing it...
These are related:
Handling dynamic created files in play 2
How to serve uploaded files in Play!2 using Scala?
Any assistance will be very appropriated.
Here's how I fix this
ISSUE 1
For upload file path, in play you can define configurations in conf/application.conf file, and you can use different file for production mode, using -Dconfig.file=/path/to/the/file.
So I defined an attribuite called myapp.image.base, in debug mode just set it to "", and in production mode (I created a file called conf/application.prod.conf) , I put an absolute path to it.
So in my code, I always use the following command for file path (it's in Java, but you should find a similar way in Scala for reading configuration)
Play.application().configuration().getString("myapp.image.base")+"img"
ISSUE 2
For serving image
You need to create a router.
First in your routes file, add something like this:
GET /user/images/:name controllers.Application.imageAt(name:String)
And write a simple file reader in action imageAt which return the file in stream. Again my sample is in Java but you should archive the same using Scala
File imageFile = new File(ReportFileHelper.getImagePath());
if (imageFile.exists()) {
//resource type such as image+png, image+jpg
String resourceType = "image+"+imageName.substring(imageName.length()-3);
return ok(new FileInputStream(imageFile)).as(resourceType);
} else {
return notFound(imageFile.getAbsoluteFile());
}
After that, the images is reachable from url /user/images/
Play reads files off the classpath (which includes the assets directory). On the JVM, the classpath is immutable—once started, files added to folders on the classpath will not actually be added to the classpath.
This works in development mode because Play reloads the classpath whenever it detects a change. That same hot-reloading is not enabled in production (for good reason).
How do i get the shell IPreviewHandler for a particular file extension?
Background
Windows allows developers to create a preview handler for their custom file types:
Preview handlers are called when an item is selected to show a lightweight, rich, read-only preview of the file's contents in the view's reading pane. This is done without launching the file's associated application.
A preview handler is a hosted application. Hosts include the Windows Explorer in Windows Vista or Microsoft Outlook 2007.
I want to leverage the existing IPreviewHandler infrasturcture to get a thumbnail for a file.
In A Stream
The problem is that my files are not housed in the shell namespace (i.e. they are not sitting on the hard drive). They are sitting in memory, accessable through an IStream. This means i cannot use the legacy IExtractImage interface; as it does not support loading a file from a Stream.
Fortunately, this is why the modern IPreviewHandler supports (recommends, and prefers) loading data from a Stream, and recommends against loading previews from a file:
This method is preferred to Initialize due to its ability to use streams that are not accessible through a Win32 path, such as the contents of a compressed file with a .zip file name extension.
So how do i get it?
There is no documentation on the correct way to get ahold of the IPreviewHandler associated with a particular extension. But if i take the directions of how to register an IPreviewHandler, and read the contract from the other side:
HKEY_CLASSES_ROOT
.xyz
(Default) = xyzfile
HKEY_CLASSES_ROOT
xyzfile
shellex
{8895b1c6-b41f-4c1c-a562-0d564250836f} //IPreviewHandler subkey
(Default) = [clsid of the IPreviewHandler]
I should be able to follow the same route, given that i know the extension. Lets follow that with a real world example, a .jpg file:
Notice that the file has a preview. Notice i included the second screenshot only to reinforce the idea that the preview doesn't come from a file sitting on the hard drive.
Lets get spellunking!
First is the fact that it's a .jpg file:
HKEY_CLASSES_ROOT
.jpg
(Default) = ACDC_JPG
HKEY_CLASSES_ROOT
ACDC_JPG
ShellEx
{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}
ContextMenuHandlers
Wait, there is no {8895b1c6-b41f-4c1c-a562-0d564250836f} subkey for a previewhandler. That must mean that we cannot get a thumbnail for .jpg files.
reducto an absurdum
The Real Question
The careful reader will realize that the actual question i'm asking is:
How do i get the preview of an image contained only in a stream?
And while that is a useful question, and the real issue i'm having, having an answer on how to use IPreviewHandler is also a useful question.
So feel free to answer either; or both!
Bonus Reading
MSDN: Preview Handlers and Shell Preview Host
MSDN: How to Register a Preview Handler
MSDN: IInitializeWithStream::Initialize method
IPreviewHandler throws uncatchable exception
Outlook IPreviewHandler for Delphi
#hvd had the right answer.
File types have a ShellEx key, with {guid} subkeys. Each {guid} key represents a particular InterfaceID.
There are a number of standard shell interfaces that can be associated with a file type:
{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} IExtractImage
{953BB1EE-93B4-11d1-98A3-00C04FB687DA} IExtractImage2
{e357fccd-a995-4576-b01f-234630154e96} IThumbnailProvider
{8895b1c6-b41f-4c1c-a562-0d564250836f} IPreviewHandler
Unsupported spelunking of undocumented registry keys
If i want to find, for example, the clsid of the IPreviewHandler associated with a .jpg file, i would look in:
HKEY_CLASSES_ROOT/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
(default) = [clsid]
But that's not the only place i could look. I can also look in:
HKEY_CLASSES_ROOT/.jpg
(default) = jpgfile
HKEY_CLASSES_ROOT/jpgfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
(default) = [clsid]
But that's not the only place i could look. I can also look in:
HKEY_CLASSES_ROOT/SystemFileAssociations/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
(default) = [clsid]
But that's not the only place i could look. I can also look in:
HKEY_CLASSES_ROOT/SystemFileAssociations/jpegfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
(default) = [clsid]
But that's not the only place i could look. If i think the file is an image, i can also look in:
HKEY_CLASSES_ROOT/SystemFileAssociations/image/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
(default) = [clsid]
How did i find these locations? Did i only follow documented and supported locations? No, i spied on Explorer using Process Monitor as it went hunting for an IThumbnailProvider.
Don't use undocumented spellunking
So now i want to use a standard shell interface for a file-type myself. This means that i have to crawl the locations. But why crawl these locations in an undocumented, unsupported way. Why incur the wrath from the guy from high atop the thing? Use AssocQueryString:
Guid GetShellClsidForFileType(String fileExtension, Guid interfaceID)
{
//E.g.:
// String fileExtension = ".jpg"
// Guid interfaceID = "{8895b1c6-b41f-4c1c-a562-0d564250836f}"; //IExtractImage
//The interface we're after - in string form
String szInterfaceID := GuidToString(interfaceID);
//Buffer to receive the clsid string
DWORD bufferSize := 1024; //more than enough to hold a 38-character clsid
String buffer;
SetLength(buffer, bufferSize);
HRESULT hr := AssocQueryString(
ASSOCF_INIT_DEFAULTTOSTAR,
ASSOCSTR_SHELLEXTENSION, //for finding shell extensions
fileExtension, //e.g. ".txt"
szInterfaceID, //e.g. "{8895b1c6-b41f-4c1c-a562-0d564250836f}"
buffer, //will receive the clsid string
#bufferSize);
if (hr <> S_OK)
return Guid.Empty;
Guid clsid;
HRESULT hr = CLSIDFromString(buffer, out clsid);
if (hr <> NOERROR)
return Guid.Empty;
return clsid;
}
And so to get the clsid of IPreviewHandler for .xps files:
Guid clsid = GetShellClsidForFileType(".xps", IPreviewHandler);
How to get IPreviewHandler for a file extension?
With all the above, we can now answer the question:
IPreviewHandler GetPreviewHandlerForFileType(String extension)
{
//Extension: the file type to return IPreviewHandler for (e.g. ".xps")
Guid previewHandlerClassID = GetShellClsidForFileType(extension, IPreviewHandler);
//Create the COM object
IUnknown unk = CreateComObject(previewHandlerClassID);
//Return the actual IPreviewHanler interface (not IUnknown)
return (IPreviewhandler)unk;
}
i have an application on Xcode 4 for osx. In my program i have some places where i need to read and write to a plist file. currently i have used a file path of /users/my name/desktop/name of document plist. however naturally when i make the application into a app and transfer it to a different computer it is not able to find and read the files. what should i make the file path so that it works on any computer. below is my file path that i have
filepath = #"/Users/Gautambir/Desktop/CustomerNames.plist"
You should never, ever, hard-code paths. You should construct your paths using the various APIs available to do so.
There are several ways to construct valid paths. For instance, this works:
NSString* filePath = [[NSHomeDirectory() stringByAppendingPathComponent:#"Desktop"] stringByAppendingPathComponent:#"CustomerNames.plist"];
Alternatively, you could use:
NSString* filePath = [#"~/Desktop/CustomerNames.plist" stringByExpandingTildeInPath];
Although these are correct ways to build a path, to access special locations such as the Desktop, the Documents folder or the Application Support folder, you should use the NSSearchPathForDirectoriesInDomains() function or, preferably, the NSFileManager methods URLsForDirectory:inDomains: or URLForDirectory:inDomain:appropriateForURL:create:error:.
These URL-based method should always be preferred over their path-based equivalents. Apple recommends all developers move to support the URL-based methods as soon as practicable.
This is primarily because file URLs can store bookmark data, so that if a file moves the URL can still be resolved, which is not the case with paths as paths are just strings and can't store metadata.
Here's how you'd use the file manager to find your URL:
NSFileManager* fm = [NSFileManager defaultManager];
NSURL* desktopURL = [fm URLForDirectory:NSDesktopDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
shouldCreate:NO
error:nil];
NSURL* fileURL = [desktopURL URLByAppendingPathComponent:#"CustomerNames.plist"];
I am working on an application where I have an images folder relative to my application root. I want to be able to specify this relative path in the Properties -> Settings designer eg. "\Images\". The issue I am running into is in cases where the Environment.CurrentDirectory gets changed via an OpenFileDialog the relative path doesn't resolve to the right location. Is there a way to specifiy in the Settings file a path that will imply to always start from the application directory as opposed to the current directory? I know I can always dynamically concatenate the application path to the front of the relative path, but I would like my Settings property to be able to resolve itself.
As far as I know, there is no built-in functionality that will allow this type of path resolution. Your best option is to dynamically determine the applications executing directory and concatenate to it your images path. You don't want to use Environment.CurrentDirectory specifically for the reasons you mention - the current directory may not always be correct for this situation.
The safest code I've found to find the executing assembly location is this:
public string ExecutingAssemblyPath()
{
Assembly actualAssembly = Assembly.GetEntryAssembly();
if (this.actualAssembly == null)
{
actualAssembly = Assembly.GetCallingAssembly();
}
return actualAssembly.Location;
}
Are you looking for Application.ExecutablePath ? That should tell you where the application's executable is, remove the executable name, and then append your path to it.
2 options:
The code that uses the setting can resolve the setting against the directory of the current executing assembly.
You can create your own type that serializes as a string relative to the executing assembly, and has an accessor for the full path that will resolve against the directory of the current executing assembly.
Code sample:
string absolutePath = Settings.Default.ImagePath;
if(!Path.IsPathRooted(absolutePath))
{
string root = Assembly.GetEntryAssembly().Location;
root = Path.GetDirectoryName(root);
absolutePath = Path.Combine(root, absolutePath);
}
The nice thing about this code is that it allows a fully qualified path, or a relative path, in your settings. If you need the path to be relative to a different assembly, you can change which assembly's location you use - GetExecutingAssembly() will give you the location of the assembly with the code you're running, and GetCallingAssembly() would be good if you go with option 2.
This seem to work in both WinForms and ASP.NET (gives the path to the config file):
new System.IO.FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile).Directory;
For Windows and Console applications, the obvious way is by using:
Application.StartupPath
I suggest you to use Assembly.CodeBase, as shown below:
public static string RealAssemblyFilePath()
{
string dllPath=Assembly.GetExecutingAssembly().CodeBase.Substring(8);
return dllPath;
}
You can try Application.ExecutablePath. But you need to make reference to System.Windows.Forms. This may not be a good idea if you want your class library to steer clear of forms and UI stuff.
You can try the Assembly.GetExecutingAssembly().Location. But if, somehow, you do a "Shadow Copy" before you run your application (like the default NUnit behavior), then this property will return you the shadow copy location, not the real, physical location.
The best way is to implement a function that calls the CodeBase property of Assembly object and chop off the irrelevant portion of the string.
I use the following two methods to help with that:
public static IEnumerable<DirectoryInfo> ParentDirs(this DirectoryInfo dir) {
while (dir != null) {
yield return dir;
dir = dir.Parent;
}
}
public static DirectoryInfo FindDataDir(string relpath, Assembly assembly) {
return new FileInfo((assembly).Location)
.Directory.ParentDirs()
.Select(dir => Path.Combine(dir.FullName + #"\", relpath))
.Where(Directory.Exists)
.Select(path => new DirectoryInfo(path))
.FirstOrDefault();
}
The reason to look at parent dirs to to be easier in use during development when various build scripts end up sticking things in directories like bin\x64\Release\NonsensePath\.