Friday, October 2, 2020

How to trim audio (MP3) files

Summary: PowerShell script to trim beginning and end of the audio (MP3, etc) files.
The following PowerShell script will remove the specified number of seconds from the beginning and/or end of every audio file with the given extension (.mp3 in this case) under the specified folder and all subfolders unerneath (requires FFmpeg binaries):
# Input folder holding audio files.
$inputDir = "Z:\Lectures"

# Seconds to trim from beginning of file.
$trimStart = 0.0

# Seconds to trim from the end of file.
$trimEnd = 9.0

# Path to the directory holding FFMPEG tools.
$ffmpegDir = "c:\ffmpeg\bin"

# Extension of the audio files.
$ext = ".mp3"

# Extension for temporary files.
$tmpExt = ".TMP$ext"

# Paths to FFMPEG tools.
$ffmpeg  = Join-Path $ffmpegDir "ffmpeg.exe"
$ffprobe = Join-Path $ffmpegDir "ffprobe.exe"

# Process all audio files in the directory and subdirectories.
Get-ChildItem -LiteralPath $inputDir -Filter "*$ext" -Recurse -File | ForEach-Object {
    # Original file path.
    $oldFile = $_.FullName

    # Original file name.
    $oldName = $_.Name

    # Temp file path will be in the same folder named after the original file.
    $tmpFile = "$oldFile$tmpExt"

    # Get the length of the audio track (it's a sting holding a floating number with possible new line).
    $duration = (& $ffprobe -v 0 -show_entries format=duration -of compact=p=0:nk=1 $oldFile) | Out-String

    $duration = $duration.Trim()

    # Set new length of the audio by removing the trimmed parts.
    $duration -= ($trimEnd + $trimStart)

    # Trim the file.
    & $ffmpeg -ss $trimStart -t $duration -i $oldFile -acodec copy $tmpFile

    # Delete the original file.
    Remove-Item -LiteralPath $oldFile -Force

    # Rename the temp file to the original.
    Rename-Item -LiteralPath $tmpFile $oldName -Force
}

Thursday, July 18, 2019

Minimize your app config file

Summary: How to keep application configuration files (app.config, web.config) nice and clean.
We use configuration files to store application settings that can be modified, so we do not need to recompile the application. Some applications have lots of settings, but many of these settings are not likely to change or may only change when the application is rebuilt. In such case, here is a nice technique that will allow you to reduce the size of the config file.

Here is the basic idea:
  1. Create a static configuration class (let's call it Config).
  2. In the Config class, implement static methods to get a configuration property value that either gets it from the application's config file (if the setting is defined in the appSettings section) or uses the passed default (you'd need to implement these methods for different data types).
  3. In the Config class, define the static properties that get initialized by calling the methods mentioned above with the hard-coded defaults. To make it easier to remember, the config file's appSettings keys must be named after the Config class properties.
Now, you can remove the settings that are not very likely to change from the config file and if they need to be changed before the application is updated, simply add them back.

Here is the code:

The configuration class is responsible for initialization of the application settings:
using System;
using System.Configuration;

namespace MyApp.Configuration
{
    public static class Config
    {
        public static string OPERATION_LIST = 
            GetValue("OPERATION_LIST", "Create|Read|Update|Delete|Assign|Revoke|Enable|Disable");

        public static string OBJECT_LIST = 
            GetValue("OBJECT_LIST", "User|Group|Role");

        private static string GetValue
        (
            string keyName,
            string defaultValue = null
        )
        {
            string configValue = ConfigurationManager.AppSettings.Get(keyName);

            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }

        private static int GetValue
        (
            string keyName,
            int defaultValue
        )
        {
            string configValue = ConfigurationManager.AppSettings.Get(keyName);

            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return Int32.Parse(configValue);
        }

        private static bool GetValue
        (
            string keyName,
            bool defaultValue
        )
        {
            string configValue = ConfigurationManager.AppSettings.Get(keyName);

            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return bool.Parse(configValue);
        }

        private static object GetValue
        (
            string keyName,
            Enum defaultValue,
            Type type
        )
        {
            string configValue = ConfigurationManager.AppSettings.Get(keyName);

            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return Enum.Parse(type, configValue);
        }
    }
}
Using an application setting is now as easy as referencing the corresponding configuration class property:
string[] operations = Config.OPERATION_LIST.Split('|')
    .Select(op => op.Trim())
    .ToArray();
string[] objects= Config.OBJECT_LIST.Split('|')
    .Select(op => op.Trim())
    .ToArray();
Notice that we do not need to define these values in the application config file unless we have to modify them before we release an application update, in which case, simply add them to the appSettings section:


  
    
  

I assume this must be obvious but just in case: NEVER HARD CODE SENSITIVE INFORMATION (PASSWORDS, ENCRYPTION KEYS, ETC) IN THE APPLICATION SOURCE CODE.

Okay, I'm done for today.

UPDATE: And here is an even better option: BasicConfiguration.

Wednesday, July 10, 2019

How to get or set a nested class property using C#

Summary: C# methods to set and get nested class property values.
If you need to get a set a value of a nested object property, here is a couple of functions that can help you do it using reflection (.NET 4.7, C#):
/// 
/// Gets the value of a nested object property.
/// 
/// 
/// Project that owns the property.
/// < /param>
/// 
/// Name of the property.
/// < /param>
/// 
/// Property value (or null, if property does not exists).
/// 
/// 
/// 
/// The code assumes that the property exists;
/// if it does not, the code will return null.
/// 
/// 
/// The property does not need to be nested.
/// 
/// 
/// The code handles both class properties and fields.
/// 
/// 
public static object GetPropertyValue
(
    object source, 
    string name
)
{
    if (name.Contains("."))
    {
        var names = name.Split(new char[] { '.' }, 2);

        return GetPropertyValue(GetPropertyValue(source, names[0]), names[1]);
    }
    else
    {
        PropertyInfo prop = null;

        prop = source.GetType().GetProperty(name);

        if (prop != null)
            return prop != null ? prop.GetValue(source) : null;

        FieldInfo field = source.GetType().GetField(name);

        if (field == null) return null;

        return field.GetValue(source);
    }
}

/// 
/// Sets the value of a nested object property.
/// 
/// 
/// Object that owns the property to be set. 
/// < /param>
/// 
/// Name of the property.
/// < /param>
/// 
/// Property value.
/// < /param>
/// 
/// 
/// The code assumes that the property exists;
/// if it does not, the code will do nothing.
/// 
/// 
/// The property does not need to be nested.
/// 
/// 
/// The code handles both class properties and fields.
/// 
/// 
public static void SetPropertyValue
(
    object target,
    string name, 
    object value
)
{
    var names = name.Split('.');

    for (int i = 0; i < names.Length - 1; i++)
    {
        PropertyInfo prop = target.GetType().GetProperty(names[i]);
        if (prop != null)
        {
            target = prop.GetValue(target);
            continue;
        }

        FieldInfo field = target.GetType().GetField(names[i]);
        if (field != null)
        {
            target = field.GetValue(target);
            continue;
        }

        return;
    }

    PropertyInfo targetProp = target.GetType().GetProperty(names.Last());

    if (targetProp != null)
        targetProp.SetValue(target, value);
}

Friday, November 17, 2017

How to rotate a video without re-encoding

Summary: A few tips on video rotation.
If your phone messes up the rotation metadata flag in a video file, download ffmpeg and run the following command:
ffmpeg -i input.mp4 -c copy -metadata:s:v:0 rotate=0 output.mp4
This should fix the problem without re-encoding the video. If it does not, try setting the rotate switch to a different number, such as 90, 180, or 270. For additional information about video orientation MP4 and other video files, check the See also section.

See also:
iPhone recorded videos getting rotated on Windows systems
Rotate a MP4 file, while preserving codec and quality attributes
Rotate mp4 videos without re-encoding
Rotating videos with FFmpeg
How to rotate a video 180° with FFmpeg?

How to fix slow startup for old Photoshop/Premiere Elements apps (on Windows)

Summary: Easy way to fix slow startup issues for older versions of Adobe products (on Windows).
If you have an older version of Adobe video and photo editing products, such as Photoshop Elements or Premiere Elements, you may be irritated with the long startup time, which could take several minutes. Apparently, the applications try to connect to a server that is no longer in service (btw, excellent job, Adobe!), so they wait... and wait... and wait... The good thing is that there seems to be an easy fix for this.

According to the answer to this post, you just need to add the following entry to your hosts file located in the %WINDIR%\System32\Drivers\Etc folder:
127.0.0.1   static.photoshop.com
This solved the problem form my Photoshop Elements 9 and Premiere Elements 13 instances running on Windows 7 (yeah, I know, need to upgrade to Windows 10, but that's a different story).

Enjoy!

Friday, October 20, 2017

Save STDOUT/STDERR to clipboard (and more)

Summary: How to redirect the STDOUT or STDERR stream to clipboard.
Note to self (mostly), in case I forget the command that would redirect standard output or standard error stream and save it on the clipboard.

Redirect STDOUT to clipboard:
command | clip
Redirect STDERR to clipboard:
command 2>&1 | clip
The following will save the contents of the current directory to the clipboard:
dir | clip
Now, let's try the following:
dir xyz: | clip
The clipboard now will hold something like this:
 Volume in drive C is SYSTEM
 Volume Serial Number is 1234-ABCD

 Directory of C:\Windows\SysWOW64
Now, let's redirect STDERR to clipboard:
dir xyz:2>&1 | clip
The clipboard now will hold something like this:
"xyz:" is not a recognized device.
"xyz:" is not a recognized device.
 Volume in drive C is SYSTEM
 Volume Serial Number is 1234-ABCD

 Directory of C:\Windows\SysWOW64

File Not Found
See also:
Forgotten (but Awesome) Windows Command Prompt Features by Scott Hanselman
How to pipe stderr, and not stdout?

Tuesday, August 22, 2017

How to clean up BIN and OBJ folders in a Visual Studio solution

Summary: Some ideas on cleaning up intermediate and output folders in Visual Studio projects.
There may be better (and more elegant) ways of cleaning up the output (BIN) and intermediate (OBJ) folders generated by Visual Studio build process, but the following script is probably the easiest option you can use:

@echo off
rem Delete BIN and OBJ folders from the immediate folder and all subfolders.
rem

rem Switch to the script folder.
cd "%~dp0"

rem Use the following to suppress the 'File Not Found' message if no folders are found.
setlocal enabledelayedexpansion 
for /f "tokens=*" %%G in ('dir /B /AD /S bin 2^>nul') do rmdir /S /Q "%%G"
for /f "tokens=*" %%G in ('dir /B /AD /S obj 2^>nul') do rmdir /S /Q "%%G"

Notice that the script call setlocal enabledelayedexpansion to allow the 2^>nul redirection in the for loops (without it, it would output the "File Not Found" message if the folder and subfolders do not hold the "BIN" or "OBJ" folders.

See also:
How to clean Visual Studio bin and obj folders
I want to delete all bin and obj folders to force all projects to rebuild everything
Want to suppress File not found output
Suppress command line output