Last active
May 4, 2020 01:56
-
-
Save hyuki/76edfdff6ffddcdbee83ac1883e515ce to your computer and use it in GitHub Desktop.
txt-to-mp3: Convert txt to mp3 with Amazon Polly. txt-to-mp3 (テキストファイルを分割してAmazon Pollyでmp3に変換し、ffmpegを使って合成するRubyスクリプト)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MP3DIR: ./mp3 | |
WHO: Mizuki |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- opening.mp3 --> | |
おはようございます。 | |
<!-- chime1.mp3 --> | |
こんにちは。 | |
<!-- chime2.mp3 --> | |
おやすみなさい。 | |
<!-- ending.mp3 --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'fileutils' | |
require 'tmpdir' | |
require 'pp' | |
require 'yaml' | |
APPNAME = 'txt-to-mp3' | |
if ARGV.length != 3 | |
puts "Usage: txt-to-mp3 config.yaml input.txt output.mp3" | |
puts | |
puts "input.txt の例" | |
puts <<-"EOD" | |
■input.txt の例: | |
<!-- opening.mp3 --> | |
おはようございます。 | |
<!-- chime1.mp3 --> | |
こんにちは。 | |
<!-- chime2.mp3 --> | |
おやすみなさい。 | |
<!-- ending.mp3 --> | |
■config.yaml の例: | |
MP3DIR: ./mp3 | |
WHO: Mizuki | |
EOD | |
abort | |
end | |
CONFIG = YAML.load_file(ARGV[0]) | |
def extract_mp3(line) | |
if line.match(/^<!-- (.+\.mp3) -->/) | |
$1 | |
else | |
nil | |
end | |
end | |
def split_txt_to_sections(input_txt) | |
mp3dir = CONFIG['MP3DIR'] | |
if not Dir.exist?(mp3dir) | |
abort "#{APPNAME}: #{mp3dir} is not found." | |
end | |
sections = [] | |
lines = IO.readlines(input_txt) | |
cursection = '' | |
lines.each do |line| | |
mp3 = extract_mp3(line) | |
if mp3 | |
sections << { :type => :text, :content => cursection } | |
cursection = '' | |
sections << { :type => :mp3, :content => "#{mp3dir}/#{mp3}" } | |
else | |
cursection += line | |
end | |
end | |
if cursection.size > 0 | |
sections << { :type => :text, :content => cursection } | |
end | |
sections | |
end | |
def sections_to_files(sections, tmpdir) | |
index = 1 | |
files = [] | |
sections.each do |section| | |
if section[:type] == :text | |
filename = sprintf("#{tmpdir}/_%d.txt", index) | |
open(filename, "w") do |f| | |
f.print section[:content] | |
end | |
files << { :type => :text, :filename => filename } | |
index += 1 | |
elsif section[:type] == :mp3 | |
files << { :type => :mp3, :filename => section[:content] } | |
else | |
abort "#{APPNAME}: sections_to_files: invalid type: " + section[:type].to_s | |
end | |
end | |
files | |
end | |
def convert_files_to_mp3files(files) | |
mp3files = [] | |
files.each do |file| | |
if file[:type] == :text | |
filename = file[:filename] | |
mp3filename = filename + '.mp3' | |
puts "Converting: " + filename | |
cmd = "aws polly synthesize-speech --output-format mp3 --text file://#{filename} --voice-id #{CONFIG['WHO']} #{mp3filename}" | |
puts cmd | |
err = system(cmd) | |
puts err | |
mp3files << mp3filename | |
elsif file[:type] == :mp3 | |
mp3files << file[:filename] | |
else | |
abort "#{APPNAME}: convert_txt_to_mp3: invalid type: " + file[:type].to_s | |
end | |
end | |
mp3files | |
end | |
def main(input_txt, output_mp3) | |
puts "Input text file: #{input_txt}" | |
puts "Output mp3 file: #{output_mp3}" | |
puts "MP3 dir: #{CONFIG['MP3DIR']}" | |
Dir.mktmpdir do |tmpdir| | |
sections = split_txt_to_sections(input_txt) | |
files = sections_to_files(sections, tmpdir) | |
mp3files = convert_files_to_mp3files(files) | |
mp3s = mp3files.join('|') | |
cmd = "ffmpeg -y -i \"concat:#{mp3s}\" #{output_mp3}" | |
puts cmd | |
result = system(cmd) | |
puts result | |
end | |
end | |
main(ARGV[1], ARGV[2]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment