DRViD.ca » Audio

By: Drvid  09-12-2011

For anyone that doesn’t know, the CNIB runs a huge library of braille, e-text and digital talking (audio) books for it’s clients. This was an interesting place to work and I took pride in helping their aims of providing accessible information to visually impaired users across Canada.


# batch_transform.py
# runs 'insert notice' XSLT transformations on a folder of valid DAISY 2.02 books
# copies all markup and moves all audio files into 'withSectionsAddded' subfolders

## to do: recalculate ncc:kByteSize & ncc:files to make it valid?

import daisy_files
import daisy_html
import subprocess
import shutil
import os

# set folder to transform here
fresh = r'E:\RNIB'

# list of which found book versions will be transformed
transformable = ('decoded','merged','DAISY')

# set dependancy paths and global variables
xslt = r'E:\DaisyTools\daisy_rnib_mod.xsl'
header = r'E:\RNIB\about_this_daisy_64.mp3'
footer = r'E:\RNIB\this_concludes_64.mp3'
saxon8 = r'C:\Program Files\Saxon\saxon8.jar'
folder = 'withSectionsAdded'

output_plant = open(os.path.join(fresh,r'joblist_dtb_planter.xml'),'a+')
output_regen = open(os.path.join(fresh,r'joblist_regenerator.xml'),'a+')

books = 0
skipped = 0
transformed = 0

print "\n Transforming DAISY books with '%s'\n" % xslt

for book in daisy_files.get_folders(fresh):

  books += 1
  target_exists = False
  versions = daisy_files.get_versions(book)

  if len(versions) > 0:
    for version in versions:

      smil_temp = os.path.join(version,'temp.xml')
      ncc_source = os.path.join(version,'ncc.html')
      target = os.path.join(version,folder)

      if os.path.isdir(target) and len(os.listdir(target)) > 0:
        skipped += 1
        target_exists = True
        print '\n Skipping..\n\t',target

      if folder in os.path.basename(version) and len(os.listdir(version)) > 1:
        target_exists = True

      if version.endswith(transformable) and not target_exists:
        target_about = os.path.join(target,'about_this_daisy.mp3')
        target_concludes = os.path.join(target,'this_concludes.mp3')
        planter = os.path.join(target,'regenerated')
        print "\n Creaing '%s' in ..\n\t%s" % (folder, version)

        if not os.path.exists(target):

        # spawn saxon java transformer process
        transformer = subprocess.call(['java','-classpath',saxon8,'-jar',saxon8,'-o',smil_temp,ncc_source,xslt],shell=True)

        # copy or move all audio files to output
        for file in os.listdir(version):

          if file.endswith('.mp3'):
            print '\t',file
            source = os.path.join(version,file)

          if file.endswith('.wav'):
            print '\t',file
            source = os.path.join(version,file)

        print '\t',os.path.basename(target_about)
        shutil.copyfile(header, target_about)

        print '\t',os.path.basename(target_concludes)
        shutil.copyfile(footer, target_concludes)

        # append job lines to XML batch files
        print >> output_regen, (r'%s' % os.path.join(target,'ncc.html'))
        print >> output_plant, (r'' % os.path.join(planter,'ncc.html'))

        transformed += 1


print '\n %s of %s books found were skipped' % (skipped,books)
print ' %s of %s books found were transformed' % (transformed,books)

Another sample below, simply decodes DAISY books from MP3 to WAV and updates references in SMIL/HTML files accordingly. Part of our goal was to re-encode RNIB’s higher bitrate DTBs to a smaller 56Kbps.


#!/usr/bin/env python

# batch_decode.py
# decompresses a whole folder of DAISYs (MP3 to WAV) and updates
# their SMIL files accordingly, skipping those already decoded
# to do:
  # encoded list works on first filesystem match
  # instead of the first item in the list

import os, shutil, re, daisy_files

# set the folder of DAISYs to decode/decompress here..
decodes = r'E:\RNIB-Odd'

# priority list of which found versions will be decoded, use caution
encoded = ['withSectionsAdded','merged']

print '\n Decoding compressed DAISY books from MP3 to WAVE\n'

def update_smil(i, o):
  input = open(i)
  output = open(o, 'w')
  for line in input:
    output.write(re.sub('.mp3"', '.wav"', line))

for book in daisy_files.get_folders(decodes):

  versions = daisy_files.get_versions(book)
  target_exists = True
  target = os.path.join(book,'unencoded')
  decode = False
  source = []

  # find decode version candidate
  for candidate in encoded:
    for version in versions:
      if candidate in version:
        source = daisy_files.get_paths(version)
        decode = True
        target_exists = False

  # skip already decoded books
  if os.path.exists(target) and len(os.listdir(target)) > 0:
    target_exists = True
    decode = False
    print ' Skipping..','\t',target

  if not target_exists and decode:
    print " Creating..\t" + target

  for item in source:

    if os.path.isfile(item) and item.endswith('.mp3'):
      wave = os.path.join(target, os.path.basename(item[:-4] + '.wav'))
      print '\n Decoding..\t' + item
      print ' ->\t\t' + wave
      ## os.spawnl(os.P_WAIT, 'lame.exe', 'lame.exe', '--decode', '--verbose', '"' + item + '"', '"' + wave + '"')
      os.spawnl(os.P_WAIT, r'madplay.exe', r'madplay.exe', '-m', '-v', '--sample-rate=22050', '"' + item + '"', '"-owave:' + wave + '"')

    elif os.path.isfile(item):
      if item.endswith('.smil') and os.path.basename(item) != 'master.smil':
        smile = os.path.join(target, os.path.basename(item))
        print ' Updating..\t' + smile
        update_smil(item, smile)
        copied = os.path.join(target,os.path.basename(item))
        print ' Copying..\t' + copied
        shutil.copy(item, target)

These scripts won’t actually do much on their own so I’ve created a zip including all of the dependancies. Keep in mind I left it as a work in progress with the e-Publishing and Recording studio departments. The last semi-complete feature was simple TSV style database tracking of the titles I was working on.

Other products and services from Drvid


DRViD.ca » Reviews

I’ve finally decided to cancel my MobileMe account because, these days Apple doesn’t really need another hundred bucks from me every year. As my renewal term is coming in the next week I’ve been thinking, researching and testing free alternatives to most of these services. Replace all your MobileMe services for free, and terminate your billing renewal because Apple shouldn’t be charging money for this.


DRViD.ca » Design

Most of the icons were modelled in Maya and vector rendered, but the last few I’ve been faking the 3D look directly in Illustrator. I’ve also had to retrace much of the line art since the Maya Vector Renderer didn’t really give me the cleanest lines. After completing the base theme I went on to make a few other colour-scheme variations of it. I mocked up a basic layout in Fireworks to find a basic look and feel including colours.


DRViD.ca » Code

This will make copies of your resized images next to the originals in that same folder (Windows users may have problems using the * for batch file manipulations however). If you had images in a different format, and/or wanted to convert them to smaller, lossy versions, you could try something like this. This really is the quickest and easiest way to resample a whole bunch of images. This makes commands like convert and mogrify available.


DRViD.ca » Animation

It’s a few years old I know, but watching it again and the layers of detail in the plots are still astounding me. Dramatic characters in a grim metropolis and did I mention the amazing designs, voice acting and animation. Full of existentialist themes, information warfare, cyber-crime, robotic empathy, trans-humanism….