Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   [решено] получить длительность аудио (wav)-файла. (http://forum.oszone.net/showthread.php?t=348965)

9119 08-06-2021 21:39 2959753

получить длительность аудио (wav)-файла.
 
Добрый.

Есть необходимость вывести длительность wav-файла в консоль.


Нашел для себя способ такой:

Код:

Get-MediaInfoSummary "C:\project\Book0001.wav" | Select-String -Pattern 'Duration'
на выходе получим:
Код:

Duration                                : 58 s 378 ms
Duration                                : 58 s 378 ms

причем дважды, т.к так выводит модуль.
Скрытый текст
General
Complete name : C:\project\Book0001.wav
Format : Wave
File size : 19.6 MiB
Duration : 58 s 378 ms
Overall bit rate mode : Constant
Overall bit rate : 2 823 kb/s

Audio
Format : PCM
Format profile : Float
Codec ID : 3
Codec ID/Hint : IEEE
Duration : 58 s 378 ms
Bit rate mode : Constant
Bit rate : 2 822 kb/s
Channel(s) : 2 channels
Sampling rate : 44.1 kHz
Bit depth : 32 bits
Stream size : 19.6 MiB (100%)


Для использования в переменной
Код:

$duration = Get-MediaInfoSummary "C:\project\Book0001.wav" | Select-String -Pattern 'Duration'
мне нужно 5-ти значное число и только в единственном экземпляре.

Т.е. было
Код:

Duration                                : 58 s 378 ms
Duration                                : 58 s 378 ms

а нужно:
первый вариант:
Код:

58378
второй вариант:
Код:

58,378

Помогите с кодом пожалуйста.

Iska 08-06-2021 22:20 2959755

Код:

$oMatchInfo = Get-MediaInfoSummary "C:\project\Book0001.wav" | Select-String -Pattern '^Duration : (\d+) s (\d+) ms$'
-join $oMatchInfo[0].Matches[0].Groups[1..2].Value


Foreigner 08-06-2021 22:32 2959758

Цитата:

Цитата 9119
Get-MediaInfoSummary »

Это какая-то обертка mediainfo?

Код:

mediainfo --Output="Audio;%Duration/String3%" file.wav # HH:MM:ss.mmm
 mediainfo --Output="Audio;%Duration%" file.wav # выводит миллисекунды
 
 mediainfo --Info-Parameters # для другого форматирования вывода.


Iska 08-06-2021 22:53 2959762

Цитата:

Цитата Foreigner
Это какая-то обертка mediainfo? »

Обёртка над обёрткой, над обёрткой: PowerShell Gallery | Get-MediaInfo 3.7GitHub - stax76/MediaInfo.NET: MediaInfo.NET is a Windows application that shows media file information → MediaInfo.dll (MediaInfo).

greg zakharov 08-06-2021 22:57 2959765

Хм, а не проще ли тогда через проводник запросить длительность файла, нежели плодить обёртки?

Iska 08-06-2021 23:07 2959767

greg zakharov, если я правильно понял — его миллисекунды интересуют.

greg zakharov 08-06-2021 23:53 2959773

Iska, ежели так, можно и вовсе отказаться от каких-либо библиотек и, приложив к мозгу немного знаний математики, вычислить продолжительность аудио самостоятельно. В противном случае, что-то было в WPF относительно базовых данных по аудио файлам. К слову, можно использовать DirectX.

Iska 09-06-2021 03:44 2959776

greg zakharov, возможно. Лень искать, когда не интересно и самому не надо.

9119 09-06-2021 10:07 2959785

Цитата:

Цитата greg zakharov
Хм, а не проще ли тогда через проводник запросить длительность файла, нежели плодить обёртки? »

милисекунды не важны. как и не нужны лишние обертки.
просто не нашел другого способа.

как это сделать через проводник?

в идеале нужно чтобы результат выводился только в секундах.
т.е, например аудио - 2м43сек... на выходе должно быть - 163

DJ Mogarych 09-06-2021 10:44 2959787

Код:

$path = 'C:\project\Book0001.wav'

$shell = New-Object -COMObject Shell.Application
$folder = Split-Path $path
$file = Split-Path $path -Leaf
$shellfolder = $shell.Namespace($folder)
$shellfile = $shellfolder.ParseName($file)

[TimeSpan]::Parse("$($shellfolder.GetDetailsOf($shellfile, 27);)").totalseconds

https://superuser.com/questions/7045...le-in-a-script
https://stackoverflow.com/questions/...s-fff/21894303

9119 09-06-2021 12:35 2959795

Вложений: 1
DJ Mogarych, хороший вариант. Спасибо.

помогите с кодировкой пож...

Код:

$path = "E:\!!Youtube\АРХИВ\!временное\Ролики\!в работе\tmp\1\Book0001.wav"
$shell = New-Object -COMObject Shell.Application
$folder = Split-Path $path
$file = Split-Path $path -Leaf
$shellfolder = $shell.Namespace($folder)
$shellfile = $shellfolder.ParseName($file)
$seconds = [TimeSpan]::Parse("$($shellfolder.GetDetailsOf($shellfile, 27);)").totalseconds
_________________________________________________________

$frames = $seconds * 30
$music_path = "E:\\!!Youtube\\АРХИВ\\!временное\\Ролики\\!в работе\\tmp\\1\\Book0001.wav"
$video_path = "E:\\!!Youtube\\АРХИВ\\!временное\\Ролики\\!в работе\\tmp\\1\\ProShow Slideshow 1.mp4"


Get-Content C:\project\kc\temp_1.tscproj | ForEach-Object {$_ -replace "music_path", $music_path} | Set-Content C:\project\kc\temp_2.tscproj
Get-Content C:\project\kc\temp_2.tscproj | ForEach-Object {$_ -replace "video_path", $video_path} | Set-Content C:\project\kc\temp_final.tscproj


при выполнении -replace, в конечном файле вместо (русских) значений из переменных - кракозябры!
(скрин кое как прикрепил)

Кодировка начального файла - utf-8. (без BOM). Так должно и остаться.

Помогите с кодом пож

DJ Mogarych 09-06-2021 13:22 2959799

Код:

Get-Content C:\project\kc\temp_1.tscproj -Encoding UTF8 | ForEach-Object {$_ -replace "music_path", $music_path} | Set-Content C:\project\kc\temp_2.tscproj -Encoding utf8

9119 09-06-2021 14:24 2959801

огромное спасибо, значение вставляется корректно.

но на выходе получаем utf-8 С BOM (если верить notepad++).
А софту, который использует этот файл - нужен обычный UTF-8, и с utf-8 С BOM он не работает (пишет мол файл поврежден)
Стоит в notepad++ выставить обычный UTF-8, сохранить и все ок, все работает.


Могу сразу после замены получить обычный UTF-8 (БЕЗ BOM)?



Код:

-Encoding ASCII
дает желаемую кодировку, но все русские буквы внутри файла становятся "???"

DJ Mogarych 09-06-2021 15:12 2959811

Потому что -Encoding ASCII - это 7-битная кодировка.

Судя по всему, проще всего поставить Powershell Core (https://github.com/PowerShell/powers...eleases/latest), там UTF8 without BOM - это кодировка по умолчанию.

https://stackoverflow.com/a/34969243

Либо воспользоваться решением оттуда же.
Есть вариант выключить BOM на уровне системы (в Windows 10), но лично я не стал бы так делать.

9119 09-06-2021 15:46 2959818

DJ Mogarych, поставил core. Спасибо

greg zakharov 09-06-2021 17:02 2959832

DJ Mogarych, уже дал ответ как решается задача, через системный шелл. Давайте до кучи рассмотрим как вычислить продолжительность математически (Iska вроде бы сетовал, что ему не шибко хочется заморачиваться с расчётами). Для примера возьмём RIFF Wave (это те самые WAV, что можно отыскать в %systemroot%\Media). Формат такого WAV беспрецедентно прост: первые 12 байт - т.н. RIFF описатель, следующий 24 байта - описатели формата, далее два блока по четыре байта - пролог данных, а там и сами данные.

Как видим всё просто, бутылка, дабы разобраться, нам не потребуется.
Обобщённый алгоритм вычисления длительности в миллисекундах:
Код:

using namespace System.IO

$br = [BinaryReader]::new(($fs = [File]::OpenRead(<# наш wav файл #>))
$fs.Position = 0x1C
$byterate = $br.ReadUInt32() # см. в таблицу касательно последовательности байт
$ms = (($fs.Length - 0x2C) * 1000) / $byterate # искомое значение
$br.Dispose()

Перевести в секунды, минуты, часы (нужное подчеркнуть) полученное значение, полагаю, уже не составит труда.

Serguei Kouzmine 10-06-2021 07:15 2959892

@DJ Mogarych а не проще разве отрезать бом ?


Код:

. .\strip_bom.ps1 test1.txt
Код:

Directory: C:\Users\Serguei


Mode                LastWriteTime        Length Name
----                -------------        ------ ----
-a----        6/10/2021  12:01 AM            10 test1.txt.new
-a----        6/10/2021  12:00 AM            13 test1.txt


Код:

od -x test1.txt ; od -x test1.txt.new
0000000 bbef d1bf d082 d0b5 d1ba d181 0082
0000015
0000000 82d1 b5d0 bad0 81d1 82d1
0000012

UTF-8
The UTF-8 representation of the BOM is the (hexadecimal) byte sequence 0xEF,0xBB,0xBF.

сам скрипт:

Код:

param(
  [String]$source,
  [String]$dest = "${source}.new"
)
# https://stackoverflow.com/questions/288111/remove-byte-order-mark-from-a-file-readallbytes-byte

$text = [System.IO.File]::ReadAllText($source)
$writer = new-object System.IO.StreamWriter($dest, $false, (new-object System.Text.UTF8Encoding($false)))
$writer.Write($text)
$writer.Close()


Iska 10-06-2021 21:54 2959992

Цитата:

Цитата Serguei Kouzmine
@DJ Mogarych а не проще разве отрезать бом ? »

Не проще. Проще не писать BOM.
Код:

[System.IO.File]::WriteAllLines('C:\Мои проекты\0309\Sample.txt', 'Мама мыла раму', [System.Text.UTF8Encoding]::new($false))

Serguei Kouzmine 11-06-2021 04:36 2960005

не писать BOM можно в своих логах а в чужих его придется отрезать

Iska 11-06-2021 19:23 2960069

Serguei Kouzmine, в каком сообщении темы Вы видите «чужой лог»?

YuS_2 26-06-2021 08:56 2960933

Цитата:

Цитата greg zakharov
Хм, а не проще ли тогда через проводник запросить длительность файла, нежели плодить обёртки? »

Да, проще...
Тут вот попался старый скрипт, без использования внешних модулей и прочих оберток:
script.ps1
Код:

#requires -v 3.0
<#
.SYNOPSIS
        Скрипт для получения строк детализированной информации из файлов.

.Description
        Получает дополнительные свойства из файлов, в зависимости от выбранных параметров.
        Для получения свойств одного файла, можно указать его с относительным или абсолютным
        путем. Для массового получения свойств - необходимо указать каталог с файлами.
        Скрипт параметризированный.

.Parameter Items
        Обязательный: Указываются файлы или каталоги (в случае каталога, будут выбраны
        файлы содержащиеся в нем по фильтру)

.Parameter Filter
        Необязательный: Фильтр для отбора файлов. Поддерживаются маски

.Parameter Propnum
        Необязательный: Единственный номер параметра или диапазон параметров, или набор
        параметров. См. примеры

.Parameter Recurse
        Переключатель: При наличии, будет осуществляться рекурсивная выборка файлов из
        каталогов. Для элементов-файлов, переключатель игнорируется.

.Parameter Force
        Переключатель: При наличии, будут выбираться скрытые, системные и т.п. файлы.

.Parameter Outfiles
        Переключатель: Для осуществления вывода в отдельные файлы. Имя файла будет
        составлено из имени и расширения обрабатываемого файла и нового расширения .txt
        Файлы складываются рядом со скриптом.

.Example
        .\script.ps1 -item 'd:\root1','d:\root2','d:\root3\file.mp4' -propnum 27 -recurse -force

        Это пример выборки файлов, указанных в перечислении -items, для дальнейшей обработки,
        Будет получено свойство с номером 27. Каталоги из перечисления будут обработаны рекурсивно
        для выборки всех файлов.
       
.Example
        .\script.ps1 -item 'd:\root1' -propnum (1..2048) -force

        Это пример выборки файлов из указанного каталога, для дальнейшей обработки.
        Будут получены свойства с номерами от 1 до 2048. Выбираются все файлы.
       
.Example
        .\script.ps1 -item 'd:\root1' -filter ('*.mpg','*.avi','*.mkv','*.mp4','*.3gp','*.vob') `
        -propnum 27 |ft

        Это пример выборки файлов из указанного каталога, для дальнейшей обработки.
        Будет получено свойство с номером 27. Выбираются только доступные файлы и только с
        указанными расширениями в фильтре.

.Notes
        Created By YuS

        Version: 1.00
        Date: 2/04/2018
        Purpose/Change:        Первая версия
        Version: 2.00
        Date: 17/09/2019
        Purpose/Change:        Изменена логика скрипта, добавлена возможность выборки набора свойств.
                Добавлена возможность вывода в консоль. Добавлен подсчет общей продолжительности
                медиафайлов, если выбрано единственное свойство 27. Расширена возможность
                группировки по параметрам.
                Поправлены старые баги, добавлены новые. :)

.Link
        ссылки удалены, ибо первично публиковался не здесь.
       
#>


[CmdletBinding()]
Param (
        [parameter(mandatory=$true)]
        [string[]]$items,
        [string[]]$filter = '*.*',
        [ValidateRange(0,2048)]
        [int[]]$propnum = 27,
        [switch]$recurse,
        [switch]$force,
        [switch]$outfiles
)

function testpth($x){
        $pars = @{}
        $pars.path = [management.automation.wildcardpattern]::escape($x)
        if ($force){$pars.force = $true}
        if ($filter){$pars.include = $filter}
        if(test-path $x -pathtype container){
                $pars.path += '\*'
                if ($recurse){$pars.recurse = $true}
                dir @pars|?{!$_.psiscontainer}
        } elseif (test-path $x -pathtype leaf){
                gi @pars
        } else {
                write-host Path no correct
                sleep -s 5
                break
        }
}
function info($var,$k){
        process{
                $a = new-object -com 'shell.application'
                $b = $a.namespace($var.directoryname)
                [int]$i=0
                foreach($i in $propnum){
                        $rslt = $b.getdetailsof($b.parsename($var.name),$i)
                        if($rslt){
                                [pscustomobject]@{
                                        IDFile = $k
                                        IDProp = $i
                                        Name = $var.name
                                        FullName = $var.fullname
                                        DirectoryName = $var.directoryname
                                        Type = $b.getdetailsof($null,$i)
                                        Context = $rslt
                                }
                        }
                }
        }
}

$arr = $items|%{testpth $_}|%{$n=1}{info $_ $n;$n++}

if ($outfiles){
        # Подготовка хэш-таблиц с последующей группировкой
        $p = $arr|group idfile -ashash -asstr
        # Вывод в отдельные файлы
        1..$p.count|%{
                if($p."$_".idprop.length -eq 1){
                        $name = [string]$p."$_".name + '.txt'
                }else{
                        $name = [string]$p."$_".name[0] + '.txt'
                }
                $p."$_"|ft idfile,idprop,fullname,type,context -auto|out-file -lit $name -app -enc utf8
        }
} else {
        $arr
}

<#
# При необходимости вывода в консоль единственного свойства 27 (общая продолжительность медиафайлов),
# этот блок надо раскомментировать
if ($propnum.length -eq 1 -and $propnum -eq 27){
        try{
                $total = [timespan]::new(0,0,(([timespan[]]$arr.context).totalseconds|
                measure -sum).sum)
                write-host "Общая продолжительность: $total" -for cyan
        }catch{
                write-host 'Context' не является продолжительностью медиафайла: "`n" $_ -for red
        }
}
#>


Цитата:

Цитата 9119
мне нужно 5-ти значное число и только в единственном экземпляре. »

Для такого варианта, использовать так:
Код:

([timespan[]](.\script.ps1 -item 'file.mp3' -prop 27).context).totalmilliseconds
в -item можно указать набор файлов или даже каталог с файлами, а также можно использовать фильтры... в описании скрипта есть примеры.

Iska 26-06-2021 09:05 2960934

YuS_2, там точность до секунд, посему нет смысла переводить в миллисекунды.

YuS_2 26-06-2021 09:46 2960948

Цитата:

Цитата Iska
нет смысла переводить в миллисекунды. »

где-то тут прочитал, что требуются миллисекунды... но если достаточно секунд, то просто заменить .totalmilliseconds на .totalseconds

Iska 26-06-2021 10:35 2960955

YuS_2, у автора сначала фигурировали миллисекунды, потом он написал, что ему достаточно и секунд.

Я про то, что Shell.Application даёт точность до секунд. Насколько я понимаю.

YuS_2 26-06-2021 12:39 2960962

Цитата:

Цитата Iska
Я про то, что Shell.Application даёт точность до секунд. »

Собственно, да, но если есть желание, то можно и так:
Код:

([timespan]'00:20:33').TotalDays
:)


Время: 12:40.

Время: 12:40.
© OSzone.net 2001-