private void AddAudioOverlay(HtmlDom pageDom, string pageDocName)
{
var spansWithIds = pageDom.RawDom.SafeSelectNodes(".//span[@id]").Cast<XmlElement>();
var spansWithAudio =
spansWithIds.Where(x =>GetOrCreateCompressedAudioIfWavExists(x.Attributes["id"].Value) != null);
if (!spansWithAudio.Any())
return;
var overlayName = GetOverlayName(pageDocName);
_manifestItems.Add(overlayName);
string smilNamespace = "http://www.w3.org/ns/SMIL";
XNamespace smil = smilNamespace;
string epubNamespace = "http://www.idpf.org/2007/ops";
XNamespace epub = epubNamespace;
var seq = new XElement(smil+"seq",
new XAttribute("id", "id1"), // all <seq> I've seen have this, not sure whether necessary
new XAttribute(epub + "textref", pageDocName),
new XAttribute(epub + "type", "bodymatter chapter") // only type I've encountered
);
var root = new XElement(smil + "smil",
new XAttribute( "xmlns", smilNamespace),
new XAttribute(XNamespace.Xmlns + "epub", epubNamespace),
new XAttribute("version", "3.0"),
new XElement(smil + "body",
seq));
int index = 1;
TimeSpan pageDuration = new TimeSpan();
foreach (var span in spansWithAudio)
{
var spanId = span.Attributes["id"].Value;
var path = GetOrCreateCompressedAudioIfWavExists(spanId);
var dataDurationAttr = span.Attributes["data-duration"];
if (dataDurationAttr != null)
{
pageDuration += TimeSpan.FromSeconds(Double.Parse(dataDurationAttr.Value));
}
else
{
//var durationSeconds = TagLib.File.Create(path).Properties.Duration.TotalSeconds;
//duration += new TimeSpan((long)(durationSeconds * 1.0e7)); // argument is in ticks (100ns)
// Haven't found a good way to get duration from MP3 without adding more windows-specific
// libraries. So for now we'll figure it from the wav if we have it. If not we do a very
// crude estimate from file size. Hopefully good enough for BSV animation.
var wavPath = Path.ChangeExtension(path, "wav");
if (RobustFile.Exists(wavPath))
{
#if __MonoCS__
pageDuration += new TimeSpan(new FileInfo(path).Length); // TODO: this needs to be fixed for Linux/Mono
#else
using (WaveFileReader wf = RobustIO.CreateWaveFileReader(wavPath))
pageDuration += wf.TotalTime;
#endif
}
else
{
NonFatalProblem.Report(ModalIf.All, PassiveIf.All,
"Bloom could not find one of the expected audio files for this book, nor a precomputed duration. Bloom can only make a very rough estimate of the length of the mp3 file.");
// Crude estimate. In one sample, a 61K mp3 is 7s long.
// So, multiply by 7 and divide by 61K to get seconds.
// Then, to make a TimeSpan we need ticks, which are 0.1 microseconds,
// hence the 10000000.
pageDuration += new TimeSpan(new FileInfo(path).Length*7*10000000/61000);
}
}
var epubPath = CopyFileToEpub(path);
seq.Add(new XElement(smil+"par",
new XAttribute("id", "s" + index++),
new XElement(smil + "text",
new XAttribute("src", pageDocName + "#" + spanId)),
new XElement(smil + "audio",
// Note that we don't need to preserve any audio/ in the path.
// We now mangle file names so as to replace any / (with _2f) so all files
// are at the top level in the ePUB. Makes one less complication for readers.
new XAttribute("src", Path.GetFileName(epubPath)))));
}
_pageDurations[GetIdOfFile(overlayName)] = pageDuration;
var overlayPath = Path.Combine(_contentFolder, overlayName);
using (var writer = XmlWriter.Create(overlayPath))
root.WriteTo(writer);
}