В прошлый раз мы научились работать с WAV-файлами . Точнее, мы с вами научились работать с заголовком, а сами данные за нас проиграл кто-то другой. Настало время поработать непосредственно с самими данными, а заодно и попрактиковаться в работе с двухмерной графикой при помощи Java 2D.

Как уже отмечалось, сэмплы в WAV файле идут вперемешку для всех каналов, а также могут быть закодированы по крайней мере двумя разными способами. Не факт, что нас интересуют все каналы, которые есть в файле, и уж совершенно точно нам не хотелось бы думать о разных способах кодирования. Намного удобнее было бы работать с каналами по отдельности, и чтобы сэмплы всегда представлялись, скажем, действительным числом от -1 до 1. Сказано — сделано:

type ChannelData = Array [ Double ]
type Channels = Array [ ChannelData ]

def rawDataToChannels ( wavInfo : WavInfo,
rawData : Array [ Byte ] ) : Channels = {
val samplesNumber = ( wavInfo. dataSize / wavInfo. blockSize ) . toInt
val wavChannels : Channels = {
(
for ( _ < — 1L to wavInfo. channels )
yield Array. ofDim [ Double ] ( samplesNumber )
) . toArray
}
val sampleSizeInBytes = ( wavInfo. blockSize / wavInfo. channels ) . toInt
val maxAmplitude = Math. pow ( 2 , 8 * sampleSizeInBytes — 1 )

for ( i < 0 until samplesNumber ) {
for ( ch < 0 until wavInfo. channels . toInt ) {
val from = ( i * wavInfo. blockSize +
ch * ( wavInfo. blockSize / wavInfo. channels )
) . toInt
val sampleLong = arraySliceToLong ( rawData, from,
from + sampleSizeInBytes )
val sampleDouble = { // (*)
if ( sampleSizeInBytes == 2 ) sampleLong. toShort . toLong
else ( Byte. MaxValue . toLong — sampleLong ) . toDouble
}
wavChannels ( ch ) ( i ) = sampleDouble / maxAmplitude
}
}

wavChannels
}

Приведенный код, как и в прошлой заметке, не претендует на эффективность. Обратите внимание на строчку с пометкой (*). О разном представлении чисел в 8-и битовых и 16-и битовых WAV говорилось в прошлый раз.

Теперь мы можем с легкостью визуализировать содержимое файла:

def processFile ( inputFile : String, outputFile : String,
channelNumber : Int, width : Int,
height : Int ) : Unit = {
var optWavFile : Option [ WavFile ] = None
try {
optWavFile = Some ( new WavFile ( inputFile ) )
optWavFile foreach { wavFile =>
val info = wavFile. info ( )
val rawData = wavFile. rawData ( )
val channels = rawDataToChannels ( info, rawData )

val img = new BufferedImage ( width, height,
BufferedImage. TYPE_INT_ARGB )
val graphics = img. createGraphics ( )

graphics. setColor ( new Color ( 255 , 255 , 255 ) )
graphics. fillRect ( 0 , 0 , width, height )

graphics. setColor ( new Color ( 0 , 0 , 255 ) )

val totalSamples = channels ( channelNumber ) . length

for ( x < 0 until width ) {
val fromSample = x * ( totalSamples / width )
val toSample = ( x + 1 ) * ( totalSamples / width )
var min = 0.0
var max = 0.0
for ( sn < — fromSample to toSample ) {
val sample = channels ( channelNumber ) ( sn )
min = Math. min ( min, sample )
max = Math. max ( max, sample )
}
// (0;0) point is top left corner
// be careful and don’t draw an image upside down!
graphics. draw ( new Line2D. Double (
x. toDouble , ( height/ 2 ) . toDouble — max * ( height/ 2 ) . toDouble ,
x. toDouble , ( height/ 2 ) . toDouble — min * ( height/ 2 ) . toDouble
) )
}

ImageIO. write ( img, «PNG» , new File ( outputFile ) )
}
} finally {
optWavFile. foreach ( _ . close ( ) )
}
}

Алгоритм, как видите, не сложный. Ширина картинки известна заранее. Для каждой координаты X определяется соответствующий интервал на дорожке. На этом интервале находятся максимальные и минимальные значения сэмплов, которые и отображаются для данного X.

В первой версии программы была забавная ошибка — картинка рисовалась вверх ногами, так как я забыл, что за начало координат в графических библиотеках обычно принято считать левый верхний угол. Эту ошибку, кстати, довольно сложно заметить, когда рисуешь waveform!

А вот и примеры вывода программы:

Вверху вы видите две картинки для стерео-файла, нарисованные для левой и правой его дорожки. И за ними чуть ниже нарисована дорожка для моно-файла, полученного путем объединения дорожек в стерео-файле. Если наложить друг на друга первую и вторую картинку, получится третья, что и ожидалось.

Исходники к этой заметке вы найдете в этом репозитории . На момент написания этих строк программа не была протестирована на 24-х битовых WAV-файлах, а также файлах, содержащих дополнительные секции помимо «data». Такие файлы, например, генерирует Reaper. Если хотите, можете считать доработку программы соответствующим образом своим домашним заданием.

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

admin

Share
Published by
admin

Recent Posts

После включения диагностического запуска не удается войти в Windows — как исправить?

Некоторые пользователи, экспериментируя с конфигурацией системы в окне msconfig могут столкнуться с ситуацией, когда после…

1 неделя ago

WinScript — очистка и настройка Windows 11 и 10

На сайте не раз публиковались обзоры программ, предназначенных для очистки или настройки последних версий Windows.…

1 неделя ago

Предварительный просмотр накопительного обновления не устанавливается — решение

При установке некоторых обновлений Windows 11, имеющих в названии «Предварительный просмотр накопительного обновления», многие пользователи…

1 неделя ago

FixExec — восстановление ассоциаций .exe, .bat и .com файлов

Некоторые пользователи Windows 11, 10 и предыдущих версий системы могут столкнуться с ситуацией, когда исполняемые…

1 неделя ago

Ошибка 0x800705b4 при обновлении Windows 11 и 10 — как исправить?

При установке обновлений Windows 11/10 некоторые пользователи могут столкнуться с ошибкой с кодом 0x800705b4 и…

1 неделя ago

Обслуживание вашей версии Windows 11 окончено — что делать?

Пользователи Windows 11 могут столкнуться с сообщением «Обслуживание вашей версии Windows окончено» (Your version of…

1 неделя ago