Начал отвечать в этом треде, понял что получилась почти статья.
Пишем через Mumble в ogg, Downmix (все голоса в одну моно-дорожку). Постобработка в Audacity цепочкой фильтров. Цепочка в этом посте более-менее расписана. Чтобы у всех ведущих выпуски получались одинаковыми весь софт живет на VNC (как поднять) сервере в DO. Цепочка фильтров такая Noise Reduction (выпиливает фоновый шум) -> Truncate Silence (удаляет неловкие паузы) -> Compressor -> Limiter -> Normalize -> De-Esser. Точные параметры не привожу, потому что все равно ручки придется много крутить, в частности Compressor в последних версиях Audacity стал работать немного не так как в нашей версии.
Так выпуск типично выглядит до обработки:

А так после:

Главным образом вы тут видите эффект от компрессии. Компрессированный звук одинаково хорошо слышно в метро, в самолете и так далее. Если забить на компрессию, в метро ничего слышно не будет. Обязательно проверяйте, как звучит выпуск в разных наушниках и на разных устройствах - не должно быть посторонних шумов, режущих слух свистящих звуков (для этого и нужен De-Esser) и так далее.
Оттуда экспорт в wav, на него потом натравляется скрипт который при помощи sox накладывает унцу, делает fade-in и fade-out в начале/конце, прилепляет обложку, кодирует в mp3 и потом заливает это на сервер. Вот скрипт:
#!/usr/bin/env perl
use 5.018;
use strict;
use warnings;
my $infile = shift;
my $timestamp = shift;
my $episode = shift;
if(!$infile || !$timestamp || !$episode) {
die "Usage: $0 <infile> <timestamp> <episode>\nExample: $0 output.wav 2016-08-28 56\n";
}
unless($timestamp =~ /^(\d{4})-\d{2}-\d{2}$/) {
die "Unexpected timestamp format, expected something like 2015-08-28";
}
my $year = $1;
if($year < 2017)
{
die "Year = $year is very unlikely!";
}
my $path_to_silence_1sec = "/home/ubuntu/devzen-files/silence1sec.wav";
my $path_to_silence_10sec = "/home/ubuntu/devzen-files/silence10sec.wav";
my $path_to_background_music = "/home/ubuntu/devzen-files/background-quiet.wav";
my $path_to_artwork = "/home/ubuntu/devzen-files/devzen.png";
my $tmpdir = "/tmp/devzen-$episode";
my $outfile = "";
msg("Deleting directory $tmpdir (if exists)");
run("rm -r $tmpdir || true");
msg("Creating directory $tmpdir");
run("mkdir -p $tmpdir");
msg("Adding 1 second of silence in the beginning and 10 secods in the end");
$outfile = "$tmpdir/with-silence.wav";
run("sox $path_to_silence_1sec $infile $path_to_silence_10sec $outfile");
$infile = $outfile;
msg("Adding background music with fade in and fade out");
$outfile = "$tmpdir/with-background.wav";
run("sox $path_to_background_music $tmpdir/background.wav repeat 1000 trim 0 `soxi -D $infile` fade t 1 0 10");
run("sox -m $infile $tmpdir/background.wav $outfile gain -n -0.1");
$infile = $outfile;
msg("Encoding to mp3 with artwork");
$outfile = "$tmpdir/episode.mp3";
run("lame -b 80 --ti $path_to_artwork $infile $outfile");
$infile = $outfile;
msg("Setting title, artist, etc");
my $track_number_int = ($episode + 0); # string with trailing zeros to int
my $track_number_str = sprintf "%04d", $track_number_int;
run("id3v2 -a 'DevZen Podcast' -A 'DevZen Podcast' -T $track_number_int -g 'Podcast' -y $year -t 'Episode $track_number_str' $infile");
msg("Renaming");
my $full_hash = run("md5sum $infile");
my ($short_hash) = $full_hash =~ /^(.{16})/;
$outfile = "$tmpdir/devzen-$track_number_str-$timestamp-$short_hash.mp3";
run("mv $infile $outfile");
$infile = $outfile;
msg("Creating folder /var/www/$year @ devzen-download server (if not exists)");
run("ssh devzen-download 'mkdir -p /var/www/$year'");
msg("Uploading $infile to devzen-download server");
run("scp $infile devzen-download:/var/www/$year/");
msg("Done!");
msg("Result: $infile");
msg("Download URL: http://devzen.ru/download/$year/devzen-$track_number_str-$timestamp-$short_hash.mp3");
sub msg {
my ($msg) = @_;
my $tstamp = localtime();
say "$tstamp $msg";
}
sub run {
my ($cmd) = @_;
my $result = `$cmd`;
if($?) {
die "Command `$cmd` terminated with non-zero status $?"
}
chomp($result);
$result;
}
Занимает это все сразу после выпуска залить натравить цепочку - минут 5 от силы. Утром натравить скрипт и опубликовать выпуск - еще минут 10-15. Шоуноты у нас генерируются автоматически. Раньше использовали самопальную апликуху, сейчас перехали на Trello. Скрипт генерации шоунотов по карточкам в Trello написала @SBozhko на Scala, живет он в Heroku сейчас. Света обещает его скоро в опенсорс выложить.
По поводу стриминга - его настраивал @sumerman, говорит что по этой статье.
Скрипт live.liq:
#!/usr/bin/liquidsoap
set("init.daemon",true)
pulse_audio_bot = mean(input.pulseaudio(device = "live_stream.monitor"))
backdrop = single("~/devzen-files/background-quiet.wav")
mix = add(weights = [1, 2], [backdrop, pulse_audio_bot])
output.icecast(%mp3(stereo=false, bitrate=80),
host = "localhost",
port = 8000,
password = "SECRET-SECRET-SECRET",
mount = "stream",
name = "DevZenLive ",
description = "DevZen live stream",
genre = "podcast",
url = "http://devzen.ru/live/",
mix)
Да, еще у нас есть скрипт, который считает статистику скачиваний:
#!/usr/bin/env perl
use strict;
use warnings;
use JSON::PP qw/encode_json/;
my $date = `TZ=UTC date +'%Y-%m-%dT%H:%M:%SZ'`;
chomp($date);
my %report = ("time" => $date);
my $logs = `zcat -f /var/log/nginx/access.log* | cut -d ' ' -f 7,10 | grep -v tolkodlyavedushih | grep '\.mp3'`;
my @lines = split /[\r\n]/, $logs;
my %stat;
for my $line (@lines) {
my ($file, $downloaded) = split/ /, $line;
next unless $downloaded =~ /^\d+$/g;
$file =~ s!/{2,}!/!g; # path//to///file -> path/to/file
$stat{$file} += $downloaded;
}
for my $file (sort keys %stat) {
my $file_size = -s "/var/www/$file";
next unless defined $file_size;
next unless $file_size > 0;
my $downloads = $stat{$file} / $file_size;
$report{downloads}{$file} = $downloads;
}
my $encoder = JSON::PP->new();
$encoder->pretty(1);
$encoder->sort_by(sub { $JSON::PP::a cmp $JSON::PP::b });
print $encoder->encode(\%report);
Еще, раз заговорили про автоматизацию, Trello интегрируется со Slack (наш внутренний чат) и Gitter (наш паблик чат). В первый постит карточки с новыми темами, во второй - карточки, переносимые в колонку in discussion. Все это в пару кликов настраивается через браузер.
Ну и по железу. Вам нужен USB конденсаторный микрофон с кардиоидной направленностью + к нему поп-фильтр и, желательно, шок-маунт ("паук"). Хорошо ли звучит микрофон нужно слушать в его обзорах. Audio-Technica AT2020USB+ например неплохой и сравнительно бюджетный микрофон, имеет мониторинг (вы слышите что говорите через наушники). Я лично сейчас записываюсь на SHURE PG42USB - он подороже, но и звук у него почище, в комплекте идет подставка на стол и шок-маунт.
Вроде бы, все рассказал. Если о чем-то забыл, спрашивайте, не стесняйтесь.