14 июня 2010 г.

И снова о разрезании lossless-аудио (ape, flac, wv, wav) по файлу cue

Ранее я выкладывал перевод одной заметки о том, как разрезать lossless-аудио на отдельные треки. Однако, в комментариях мне подсказали более простой способ.


Для этого понадибится утилита shntool (в Debian и Ubuntu живёт в одноимённом пакете). Вообще, это настоящий швейцарский армейский нож в области работы со звуковыми файлами (в том числе и сжатыми), в чём можно убедиться, открыв соответствующий man:
man shntool
Вот перечень некоторых команд, которые эта утилита поддерживает:
  • len — отображает продолжительность, размер и свойства звуковых данных
  • join — объединяет несколько файлов в один
  • split — разбивает звуковой файл на несколько отдельных файлов
  • cue — генерирует файл CUE или список точек разбиения по набору файлов
  • conv — преобразует из одного формата в другой
  • trim — удаляет тишину по краям записи
Список поддерживаемых форматов тоже немал: wav (RIFF WAVE), aiff (Audio Interchange File Format), shn (Shorten low complexity waveform coder), flac (Free Lossless Audio Codec), ape (Monkey's Audio Compressor), ofr (OptimFROG Lossless WAVE), lpac (Lossless Predictive Audio Compression), wv (WavPack Hybrid Lossless Audio Compression), alac (Apple Lossless Audio Codec) и другие, менее распространённые.

Естественно, для поддержки форматов нужно доустанавливать соответствующие утилиты. Для flac, ape и wv:
sudo apt-get install flac wavpack monkeys-audio
Собственно команда для разрезания будет иметь вид:
Эта команда не только разобьёт исходный flac-файл, но и переименует каждый трек в соответствии с форматом:

Исполнитель (Альбом) - Номер - Название

Естественно, можно указать и другой формат, задав свою строку и используя постановочные символы %p, %a, %n, %t.

Если файлы Music.flac и Music.cue расположены в одной папке, имеют одинаковое имя, как в данном случае и других подобных файлов нет, то можно использовать следующий скрипт:
#!/bin/bash
f=`ls *.cue`
f=${f%.cue}
shntool split -f "$f.cue" -o flac "$f.flac" -t "%p (%a) - %n - %t"
Я его назвал split_flac и поместил в ~/bin. Чтобы им воспользоваться достаточно зайти в директорию с разрезаемыми файлами и выполнить всего одну команду:
split_flac
Аналогичный скрипт можно написать и для формата APE. А можно сделать один большой интеллектуальный скрипт, если не лень.

Чтобы не было закорючек в именах, нужно следить, чтобы файл cue был в той же кодировке, что и текущая локаль (обычно, UTF-8).

Единственный минус — теги будут пустыми, но их можно заполнить командой cuetag или какой-то специализированной утилитой наподобие EasyTAG.

Читать дальше…

8 июня 2010 г.

Решето Эратосфена / Sieve of Eratosthenes

Programming Praxis, 19.02.2009

Более двух тысяч лет назад Эратосфен, который вычислил окружность Земли, расстояние до Солнца и наклон земной оси, изобрёл широту и долготу, а также придумал високосный день, создал систематический метод перечисления всех простых числе, который используется и по сей день.
Эратосфен родился в Кирене (на территории современной Ливии) и жил с 276 г. до н. э. по 194 до н. э. Он провёл большую часть своей жизни в Александрии (Египет), где был вторым главой Великой библиотеки перед Аполлонием Родосским.

Решето Эратосфена начинается с создания списка натуральных чисел до желаемого предела; проиллюстрируем метод, вычислив простые числа до тридцати:

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

Теперь возьмём первое число в списке — 2 — и вычеркнем каждое второе число:

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

Затем берём следующее невычеркнутое число в списке — 3 — и вычёркиваем каждое третье число; некоторые из них окажутся уже вычеркнутыми:

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

повторяем последний шаг для следующего невычеркнутого числа — 5:


2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

И так далее, каждый раз вычёркивая все числа, кратные следующему невычеркнутому числу в списке. Все невычеркнутые числа к концу процедуры будут простыми:

2 3 5 7 11 13 17 19 23 29

Этот метод называется решетом, потому что он просеивает весь исходный диапазон чисел в поисках простых чисел.

Решето допускает некоторые оптимизации.

Во-первых, нужно рассматривать только нечётные числа, так как первый проход вычеркнет все чётные за исключением 2, которое можно рассматривать отдельно.

Во-вторых, вычёркивание нужно начинать с квадрата числа, которое рассматривается в данный момент, так как все меньшие составные числа уже были вычеркнуты на предыдущих шагах; например, рассматривая 3, вычёркивание нужно начинать с 9, так как 6 было вычеркнуто, когда рассматривали 2.

В третьих, процедура останавливается на квадратном корне из максимального числа, так как все простые числа, большие него уже будут вычеркнуты к этому моменту. Так, в рассмотренном выше примере не было необходимости проверять числа, большие либо равные 7, так как квадрат 7 больше 30 — наибольшего числа в списке.

Напишите функцию, которая принимает один аргумент n и возвращает список простых чисел, меньших либо равных n используя оптимизированный алгоритм, описанный выше. Примените эту функцию к 15485863 и посчитайте количество полученных простых чисел.


Решение.
Jos Koot написал следующую версию решета Эратосфена:


(define (primes n)
(let* ((max-index (quotient (- n 3) 2))
(v (make-vector (+ 1 max-index) #t)))
(let loop ((i 0) (ps '(2)))
(let ((p (+ i i 3)) (startj (+ (* 2 i i) (* 6 i) 3)))
(cond ((>= (* p p) n)
(let loop ((j i) (ps ps))
(cond ((> j max-index) (reverse ps))
((vector-ref v j)
(loop (+ j 1) (cons (+ j j 3) ps)))
(else (loop (+ j 1) ps)))))
((vector-ref v i)
(let loop ((j startj))
(if (<= j max-index)
(begin (vector-set! v j #f)
(loop (+ j p)))))
(loop (+ 1 i) (cons p ps)))
(else (loop (+ 1 i) ps)))))))


Существует миллион простых числе, меньших, либо равных 15485863:


> (length (primes 15485863))
1000000


Моё решение.

from math import sqrt

def sieve(n):
l = range(3,n+1,2)
i = 0
while 2*i+3 <= sqrt(n):
k = l[i]
l[(k**2-3)/2:(n-1)/2:k] = [0] * ((n-k**2+1)/(2*k)+1)
i += 1
while l[i] == 0:
i += 1
return [2] + filter(lambda x: x != 0, l)

print len(sieve(15485863))

Казалось бы, использование сечений — не самый оптимальный способ присвоить значение сразу многим элементам списка, но при замене страшной строчки «l[(k**2-3)/2:(n-1)/2:k] = [0] * ((n-k**2+1)/(2*k)+1)» на цикл программ стала завершать работу не за 3.172 с, а за 5,056 с.

Всего же для просеивания 15485863 числе программе потребовалось 545 итераций. Из упомянутых трёх секунд только полторы уходило на собственно просеивание. Остальное время программа отфильтровывала вычеркнутые элементы.

Читать дальше…

7 июня 2010 г.

Обратный польский калькулятор / RPN Calculator

Дисклеймер. Хотелось бы поделиться с людьми, которые не особо знают английский, задачками с довольно интересного сайта Programming Praxis. Готовых переводов задач я не нашёл, а потому буду потихоньку сам их тут выкладывать. Задачки простые, но для того, чтобы не терять навыки программирования — самое то.

Programming Praxis, 19.02.2009

Реализовать обратный польский калькулятор, принимающий выражения вида «19 2.14 + 4.5 2 4.3 / - *», которое обычно вычисляется как «(19 + 2.14) * (4.5 - 2 / 4.3)» и соответствует 85.2974. Программа должна читать выражение со стандартного входа и выводить вершину стека с символом конца строки в стандартный вывод. Программа должна сохранять состояние стека операндов между выражениями.


Решение.
Единственная сложность заключается в обработке новой строки, которая в Scheme трактуется как пробел. Функция (op token) преобразует знак, из символа, полученного со входа, в оператор, который может быть использован для вычислений. Формат первой строки программы специфичен для Chez Scheme, но большинство реализаций Scheme предоставляют что-то подобное.


#! /usr/bin/scheme –script
(define (next)
(cond ((eof-object? (peek-char)) (exit))
((char=? (peek-char) #\space) (read-char) (next))
((char=? (peek-char) #\newline) (read-char) ‘nl)
(else (read))))

(define (op token) (case token ((+) +) ((-) -) ((*) *) ((/) /)))

(let rpn ((token (next)) (stack ‘()))
(cond ((eq? ‘nl token) (display (car stack)) (newline) (rpn (next) (cdr stack)))
((number? token) (rpn (next) (cons token stack)))
(else (rpn (next) (cons ((op token) (cadr stack) (car stack)) (cddr stack))))))


Моё решение.

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from sys import stdin, stdout

def calc():
stack = []
result = None
while True:
line = yield result
for token in line.split():
try:
number = float(token)
stack[:0] = [number]
result = number
except ValueError:
try:
result = {
'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x / y
}[token](stack[1], stack[0])
stack[0:2] = [result]
except IndexError:
raise ValueError, 'Stack is empty'
except KeyError:
raise ValueError, 'Unknown operation'
c = calc()
c.next()

for line in stdin.xreadlines():
print >> stdout, c.send(line)


Тут есть несколько необычных для новичка моментов.

line = yield result
— это «вывернутый наизнанку» вызов функции, который позволяет разбить цикл обработки на две части: работу со стеком и получение строк со стандартного ввода. Команда yield возвращает значение подобно return, но функция при этом не завершается и будет продолжена при вызове метода send снаружи. причём, аргумент send будет передан через yield внутрь функции. Звучит запутанно, но генераторы — это действительно очень мощная и гибкая штука.

Другая необычность — использование индексирования словаря. Это позволяет ввести конструкцию, аналогичную, например, switch в C, без использования if—elif. Так код выглядит чище, хотя, конечно, тратятся ресурсы на создание анонимного словаря и поиск ключа в нём.

Странные индексы списка stack с двоеточиями — это сечения (slices). Тоже довольно удобная штука. Они есть также в MatLab и Fortran, где без них программирование заметно усложнилось бы.

Читать дальше…