1# -*- Mode: Org -*-
2* Previous Issues
3** DONE Fix WAVE/MP3 formats to support MP3 audio in a WAVE container
4   Instead of WaveAudio generating a nasty "compression not supported" error,
5   its is_type() classmethod should return False.
6   And, MP3Audio's is_type() classmethod should check for MP3 compressed
7   RIFF WAVE containers.
8   This isn't likely to mess up decoding, but may confuse ID3 tagging.
9** DONE Allow file template to be specified on the command line
10   When making new files with track2track, cd2track, etc.
11** DONE Update the website with the latest documentation
12** DONE Allow a unified %(album_track_number)s file template field
13   If there's no album number, it's simply 2 digits of track number.
14   Otherwise, it's a combination of the two fields.
15   For example, album_number = 2 and track_number = 13 results in
16   "213" for a value.
17** DONE Update trackcat to take a cuesheet argument when outputting FLACs
18   Thus, one can perform:
19   trackcat --cuesheet=file.cue file1.wav file2.wav file3.wav -t flac -o cd.flac
20   which will embed "file.cue" into "cd.flac" using metaflac.
21   Though no other format I'm aware of supports this kind of cuesheet handling,
22   being able to easily build solid disc images of a single FLAC file
23   is much of the reason for trackcat/tracksplit.
24** DONE Don't remove undone tracklint entries from its undo DB
25   Since its checksum will change anyway and no longer match,
26   explicitly removing the entry is no longer necessary
27** DONE Support FLAC padding
28   If changes to FLAC metadata are small enough, write over the
29   padding (if present) rather than rewrite the whole file - like metaflac.
30   This approach should speed up tagging considerably.
31** DONE Fix image support in ID3v2
32   Very large images can take a very long time to load.
33** DONE Fix programs to key on album number and track number
34   Certain programs, such as trackcmp, work on tracks across two directories
35   and key on track number to determine which to compare to which.
36   These need to be updated to use both track number and album number.
37** DONE Adjust wave conversions to use album number, if present
38   For example, converting track_number 15 and album_number 2 to WAVE
39   should make a file "track215.cdda.wav" which then properly converts
40   back to track_number 15 and album_number 2 if read.
41** DONE Improve XMCD handling
42   Support for XMCD files often breaks down if one or more tracks
43   are missing.
44   In some cases, there's no fix to be had (track2xmcd)
45   but in most instances it should be made to work correctly.
46** DONE Perform type inference wherever possible
47   Anything with a single output file (trackcat and record2track)
48   should be able to infer its output type from the track suffix, if possible.
49** DONE Add "comment" field support to all metadata types
50   Don't forget to add unit tests for comment field.
51*** TODO Sort "comment" fields correctly across all metadata types
52*** TODO Add --comment support to tracktag
53** DONE Fix ID3v2 image support to handle Unicode descriptions
54   The current implementation falls down on UTF-16 input,
55   but I should have a solution from the COMM frame handler.
56** DONE Limit ID3v2.2/2.3 to UCS-2 encoding
57   The current implementation treats UCS-2 the same as UTF-16.
58   This needs to be fixed so that really high unicode characters
59   (above U+FFFF) are replaced with something within spec.
60** DONE Unify ID3v2 frame handling
61   In the beginning, there were text frames and Everything Else.
62   Text frames were unicode strings,
63   and Everything Else was a binary string of whatever.
64   Now that ID3 is cluttered with APIC frames and COMM frames that need
65   special treatment, ID3v2 needs an overhaul to more resemble FlacMetaData.
66*** DONE Ensure unknown frames are displayed correctly
67    Anything that's not text, images or comments should get some sort
68    of proper display instead of a Python object string.
69** DONE Add app testing to the unit test suite
70   Though not everything is unit-testable
71   (such as the CD handling programs or anything X11)
72   a lot of the batch programs are to some degree:
73- [X] coverdump
74- [X] track2track
75- [X] trackcat
76- [X] trackcmp
77- [X] tracklength
78- [X] tracklint
79- [X] trackrename
80- [X] tracksplit
81- [X] tracktag
82** DONE Add verbosity levels to programs
83   Every batch program should support a -V --verbose flag
84   with options for "silence","normal" (the default) and "debug".
85   Silence shuts off everything but error messages.
86   Normal is standard output behavior.
87   Debug for additional debugging output.
88- [X] cd2track
89- [X] cd2xmcd
90- [X] coverdump
91- [X] record2track
92- [X] track2cd (this will need to forward verbosity to cdrecord)
93- [X] track2track
94- [X] track2xmcd
95- [X] trackcmp
96- [X] trackrename
97- [X] tracksplit
98- [X] tracktag
99- [X] tracklint
100** DONE Add compression percentage display to trackinfo
101   Though not massively useful, it'd be neat to see just how
102   compressed audio tracks are, as a percentage of their original size.
103** DONE Add support for W??? frames to ID3v2
104   The various W??? frames are really just URLs and don't need to
105   be displayed as hex-encoded blobs.
106** DONE Add CUE/TOC support to track2cd
107   It should be possible to burn a selection of tracks, or a disc image,
108   from a cuesheet with all its indexes/ISRC/catalog data intact
109   by passing --cue to track2cd.
110** DONE Unify CUE/TOC support
111   Cuesheets and cdrdao TOC files are largely interchangeable.
112   They both feature a listing of track offsets and, optionally,
113   CD-TEXT data, ISRCs and so on.
114   These formats should be unified such that any program will
115   handle them both automatically.
116- [X] tracksplit
117- [X] trackcat
118- [X] tracktag
119*** DONE Update docs to mention CUE/TOC interchangeability
120- [X] tracksplit
121- [X] trackcat
122- [X] tracktag
123*** DONE Support cuesheet from FlacMetaData directly
124    Since we're parsing CUE/TOC files anyway, this data can be used
125    to build FLAC CUESHEET blocks directly instead of punting
126    this task to metaflac.
127*** DONE Add unit tests for TOC/CUE files, as well as embedded FLAC cuesheets
128** DONE Update copyright text for 2009
129** DONE Preserve metadata when using trackcat
130   Any fields shared by all tracks should be merged into metadata
131   for the newly combined track.
132** DONE Don't route data though WAVE files unless necessary
133   Currently, track2track routes through WAVE if both ends happen to
134   support foreign RIFF chunks, whether the files have such chunks or not.
135   This behavior needs to be modified such that only source files
136   which actually have foreign chunks, and a target format that supports them,
137   results in a pass through RIFF WAVE.
138** DONE Convert editxmcd to PyGTK
139   Although the dialog(1)-based version works in terminals and is curses-based,
140   it's extremely hokey, error-prone and doesn't support any cut & paste.
141   This needs to be reimplemented in PyGTK (since coverview already uses it)
142   and made into a stable app someone would want to use.
143*** DONE Update XMCD support
144    The current handling of XMCD files treats them only as very primative
145    AlbumMetaData implementations.  This must be updated into something
146    round-trippable if editxmcd is to be modernized.
147**** DONE Add XMCD unit tests
148**** DONE Update XMCD API documentation
149** DONE Require Python 2.5
150   Since Python 2.4 is in bugfix-only mode and barely supported,
151   it's best to move the minimum version to Python 2.5 or better
152   (which has already been superceded by Python 2.6).
153   This reduces the amount of Python versions to test on
154   and allows the use of more modern Python features which
155   makes the code less clunky.
156*** TODO Update documentation to mention Python 2.5 requirement.
157** DONE Expand WavPack's APEv2 tag coverage
158   WavPack's official specification defines APEv2 tags such as
159   "Cuesheet" and "Cover Art" which the APEv2 standard does not.
160   It would be helpful to make WavPack's APEv2 tags a superset of regular APEv2.
161*** DONE Add image support to WavePackAPEv2
162*** DONE Add cuesheet support to WavePackAPEv2
163** DONE Build unified cuesheet interface
164   Once both FLAC and WavPack support embedded cuesheets,
165   there will need to be a unified interface to support them.
166   I expect this will be a simple pair of get_cuesheet/set_cuesheet
167   methods, probably attached to the AudioFiles themselves
168   rather than to MetaData objects.
169*** DONE Alter FLAC-specific cuesheet documentation to be more general
170*** DONE Ensure cuesheets are transferred properly when transcoding
171*** DONE Update trackcat to use the interface
172*** DONE Update tracksplit to use the interface
173*** DONE Update track2cd to use the interface
174*** DONE Document cuesheet interface
175*** DONE Add unit tests for embedded cuesheets across all formats
176*** DONE Add cuesheet import option to tracktag
177    This can also use the --cue flag,
178    for consistency with other image-handling programs like tracksplit.
179    If given with a single, album-length track, --cue will import a cuesheet.
180    If given with multiple tracks or a single track that's too short,
181    --cue will function like --xmcd and act as a metadata source.
182*** DONE Update track2xmcd to support getting an XMCD file from CD image
183** DONE Convert to Muspack SV8
184   Now that Musepack SV8 is finalized, it should be the new default.
185   The old SV7 command-line tools aren't well supported and don't
186   seem to work outside of x86 platforms.
187   SV7 streams are, in theory, backwards compatible so switching
188   shouldn't be a problem.
189** DONE Update coverview to look more standard
190   It's currently a haphazard assortment of widgets
191   rather than anything like a proper GTK app.
192   It should be tweaked to look better.
193** DONE Improve transcoding robustness
194   Just about all of the to_pcm() and from_pcm() methods expect
195   that their subprocess calls will work as expected.
196   Though rare in practice, these need to be checked in case
197   the child processes fail for any reason.
198*** DONE Check for invalid input/output files/permissions errors
199    If an output file can't be read/written to for some reason
200    (invalid permissions, etc.) generate a proper error message
201    instead of throwing ugly IOExceptions or confusing errors.
202- [X] cd2track
203- [X] cd2xmcd
204- [X] coverdump
205- [X] editxmcd
206- [X] record2track
207- [X] track2cd
208- [X] track2track
209- [X] track2xmcd
210- [X] trackcat
211- [X] trackcmp
212- [X] trackinfo
213- [X] tracklength
214- [X] tracklint
215- [X] trackplay
216- [X] trackrename
217- [X] tracksplit
218- [X] tracktag
219** DONE Make text output consistent
220   Currently, command-line programs generate output using a
221   selection of scattered print statements - often accompanied by
222   if blocks when verbosity is indicated - and haphazardly
223   filtered through unicode.
224   This should be replaced by a unified message system similar
225   to Python's built-in logging module which can abstract away
226   these difficulties.
227*** DONE Convert tty output to gettext-based strings
228    This will not only make output messages more consistent across the tools,
229    but will also allow for foreign language translations in the future.
230- [X] cd2track
231- [X] cd2xmcd
232- [X] coverdump
233- [X] record2track
234- [X] track2cd
235- [X] track2track
236- [X] track2xmcd
237- [X] trackcat
238- [X] trackcmp
239- [X] trackinfo
240- [X] tracklength
241- [X] tracklint
242- [X] trackplay
243- [X] trackrename
244- [X] tracksplit
245- [X] tracktag
246**** DONE Convert output from audiotools module to gettext-based strings
247- [X] __aiff__.py
248- [X] __ape__.py
249- [X] __au__.py
250- [X] cue.py
251- [X] __flac__.py
252- [X] __freedb__.py
253- [X] __id3__.py
254- [X] __id3v1__.py
255- [X] __image__.py
256- [X] __init__.py
257- [X] __m4a__.py
258- [X] __mp3__.py
259- [X] __musepack__.py
260- [X] __speex__.py
261- [X] toc.py
262- [X] __vorbiscomment__.py
263- [X] __vorbis__.py
264- [X] __wavpack__.py
265- [X] __wav__.py
266*** DONE Add unit tests for tty output
267    All programs which generate output should be unit tested
268    so that all code paths are assured of printing the messages they're
269    supposed to print, at the streams they're supposed to print on,
270    and in the proper encoding settings.
271- [X] coverdump
272- [X] track2track
273- [X] track2xmcd
274- [X] trackcat
275- [X] trackcmp
276- [X] trackinfo
277- [X] tracklength
278- [X] tracklint
279- [X] trackrename
280- [X] tracksplit
281- [X] tracktag
282*** DONE Convert --help output to gettext-based strings
283*** DONE Convert GUI programs to gettext-based strings
284- [X] coverview
285- [X] editxmcd
286*** DONE Convert "Usage" output to gettext-based strings
287** DONE Update tracksplit's man page
288   It now supports more of track2track's options
289** DONE Support total tracks/total albums metadata fields
290*** DONE Add support for fields in the metadata tags
291- [X] Add support in Vorbis Comments
292- [X] Add support in ID3v2
293- [X] Add support in M4A
294- [X] Add support in APEv2
295*** DONE Add support in utilities
296- [X] Add support in tracktag
297- [X] Add support in cd2track
298- [X] Add support in tracksplit
299- [X] Add support in trackcat
300*** DONE Add unit tests
301*** DONE Add fields to --format output
302*** DONE Update man pages with fields information
303** DONE Integrate better MetaData merging
304   There's a few areas in which MetaData from multiple sources must
305   be merged in an intelligent manner,
306   such as where tracksplit takes a source track an XMCD file.
307   Now that a preliminary MetaData.merge() classmethod is in place,
308   this process must be integrated consistently.
309- [X] track2track
310- [X] trackrename
311- [X] tracksplit
312- [X] tracktag
313*** DONE Add unit tests for MetaData merging process
314- [X] track2track
315- [X] trackrename
316- [X] tracksplit
317- [X] tracktag
318** DONE Improve M4A metadata handling
319*** DONE Make M4A metadata updating less destructive
320   Like FLAC, not all fields need to be wiped out when overwriting
321   old metadata with new.
322*** DONE Add more M4A-specific unit tests
323** DONE Add more system information to audiotools-config
324   All BIN-referenced binaries should be accounted for.
325   Thumbnailing status and requirements should be shown.
326** DONE Add cdinfo utility
327   Analagous to trackinfo, but for an inserted CD.
328   This would be a better location for cd2xmcd's "-i" option.
329*** DONE Add cdinfo man page
330*** DONE Link cdinfo man page to other utility man pages
331** DONE Add manual page for audiotools.cfg
332   It'll be easier to check what the options are from a man page
333   rather than having to check the website or PDF doc.
334** DONE Convert vorbiscomment dependency to Python
335   This would remove the last app-based MetaData-setting utility
336   and may pave the way for adding cover art to Ogg Vorbis
337   (assuming I can find the standard for a secondary stream of image data)
338** DONE Add metadata deletion capability
339   It would be helpful to have the low-level capability of deleting
340   either part of a MetaData tag or the entire tag altogether.
341   For example, deleting the "track_name" field would delete
342   a Vorbis comment's "TITLE" field.
343   Or, deleting the MetaData from MP3 would remove all the ID3v2/ID3v1 tags.
344*** DONE Add delattr to ID3v1
345** DONE Integrate pyconstruct as a submodule
346** DONE Add undo capability to editxmcd
347** DONE Add --cue option to track2xmcd
348   One should be able to pull metadata from CD images
349   without having to embed the cuesheet.
350*** DONE Add unit tests for track2xmcd's --cue option
351*** DONE Update man page
352** DONE Group --help output more intelligently
353   For tools with a large number of options (such as track2track or tracktag)
354   the --help output is particularly jumbled.
355   Use more of optparse's features to make this output clearer.
356- [X] cd2track
357- [X] cd2xmcd
358- [X] track2track
359- [X] track2xmcd
360- [X] tracksplit
361- [X] tracktag
362** DONE Check for FLAC metadata chunk overflow
363   Although APEv2 and ID3 tags support very large objects (hundreds of MB),
364   FLAC metadata chunks have a maxmimum of about 16MB per chunk,
365   which may be hit accidentally.
366** DONE Fix or replace Python's built-in aifc module
367   The current implementation suffers from bugs.
368*** DONE Document AIFF better
369** DONE Add MusicBrainz support
370   It would be helpful to have external metadata support beyond FreeDB,
371   since FreeDB is very primitive.
372*** DONE Ensure that MusicBrainz is interchangeable with FreeDB/XMCD
373**** DONE Unify track2xmcd/track2mb, cd2xmcd/cd2mb
374     Based on preliminary testing, MusicBrainz's output is better than FreeDB's
375     but its album coverage is not as broad.
376     In addition, nobody wants to run their albums through two separate tools
377     in order to extract metadata for tagging.  The best solution is
378     for tools to try both and output the one that's most complete.
379**** DONE Extend editxmcd to MusicBrainz XML
380     Although editxmcd was originally designed specifically for XMCD files
381     and MusicBrainz's XML format differs radically, no one should have to
382     know whether an album metadata file is one or the other.
383     Therefore, editxmcd should be extended with additional fields
384     to handle XML backend data if necessary.
385**** DONE Handle multiple Release entries with single Disc ID
386**** DONE Allow MusicBrainz XML output for new editxmcd files
387     FreeDB output should also be an option, however.
388*** DONE Add MusicBrainz protocol/format documentation
389*** DONE Add MusicBrainz-specific unit tests
390- [X] track2track
391- [X] track2xmcd
392- [X] trackrename
393- [X] tracksplit
394- [X] tracktag
395*** DONE Update --help text to indicate MusicBrainz compatibility
396- [X] cd2xmcd
397- [X] editxmcd
398- [X] track2track
399- [X] track2xmcd
400- [X] trackrename
401- [X] tracksplit
402- [X] tracktag
403*** DONE Update man pages to indiciate MusicBrainz compatibility
404- [X] cd2xmcd
405- [X] editxmcd
406- [X] track2track
407- [X] track2xmcd
408- [X] trackrename
409- [X] tracksplit
410- [X] tracktag
411*** DONE Update documentation with MusicBrainz config file fields
412*** DONE Ensure missing XML fields are handled correctly
413    The MusicBrainz XML spec allows most fields to be missing altogether
414    (such as <title>).  editxmcd should add these fields in the proper place
415    if necessary.
416**** DONE Add unit tests for improperly reordered XML fields
417** DONE Ensure .glade files are found
418   Not all systems place Python data files in the same locations.
419** DONE Convert to_pcm()/from_pcm() to FrameList-based I/O
420   Passing specifically-sized blobs of binary data between conversion
421   routines worked well when those routines are little more than
422   subprocess black-boxes.  However, this approach works less well
423   whenever actual sample values are required, or when processing is needed.
424   In those cases, going from integers to strings, converting the strings
425   back to integers for processing, then bouncing them into strings once
426   again becomes needless work.
427   A more sensible approach is to keep all data as FrameList-compatible
428   objects (stored as C-based lists of int32s behind-the-scenes)
429   and convert that data to/from strings only at the beginning and end
430   of processing.
431*** DONE Build C-based audiotools.pcm.FrameList object
432    This needs to closely match audiotools.FrameList's functionality
433    and combine all the PCM conversion features from audiotools.pcmreader
434**** DONE Integrate audiotools.pcm.FrameList with i_array structures
435**** DONE Make audiotools.pcm.FrameList into a standalone object
436     So standalone test codecs can use them, such as "flacenc"
437**** DONE Unit test audiotools.pcm.FrameList
438*** DONE Convert FLAC encoder/decoder to use FrameList objects
439    - [X] flacenc
440    - [X] audiotools.decoders.FlacDecoder
441    - [X] audiotools.encoders.encode_flac
442*** DONE Convert to_pcm()/from_pcm() routines to use FrameList objects
443    - [X] AAC
444    - [X] AIFF
445    - [X] Sun AU
446    - [X] FLAC
447    - [X] M4A
448    - [X] MP2
449    - [X] MP3
450    - [X] Ogg FLAC
451    - [X] Ogg Speex
452    - [X] WAVE
453    - [X] WavPack
454*** DONE Convert CDTrackReader/OffsetCDTrackReader to use FrameList objects
455*** DONE Convert PCMConverter to use FrameList objects
456*** DONE Convert ReplayGainReader to use FrameList objects
457*** DONE Ensure integrated FrameList passes all unit tests
458*** DONE Remove deprecated audiotools.FrameList object
459*** DONE Remove deprecated pcmstream.PCMStreamReader object
460*** DONE Convert pcmstream module to resample module
461*** DONE Avoid importing audiotools.pcm so often
462    Other C libraries often import audiotools.pcm via Python callbacks
463    This library importing should be cached when possible.
464    - [X] cdiomodule
465    - [X] pcmreader
466    - [X] replaygain
467    - [X] resample
468*** DONE Check for memory leaks
469*** DONE Add FrameList and FloatFrameList programming documentation
470*** DONE Remove .copy() method
471    Since FrameLists are now immutable, there's no need for it
472*** DONE Make pcm objects self-documenting
473    For example, their methods and functions should give useful info
474    when checked with "help()"
475** DONE Add native ReplayGain handling routines
476*** DONE Add native ReplayGain handling to FlacAudio/OggFlacAudio
477*** DONE Ensure add_replay_gain()'s exceptions are caught
478    Errors during calculation may raise ValueError,
479    which must be caught anywhere the function is called
480*** DONE Add ReplayGain unit tests
481*** DONE Ensure ReplayGain works properly on 8bps and 24bps output
482*** DONE Ensure ReplayGain is applied consistently
483    Although cd2track and tracksplit are guaranteed
484    to generate only one album at a time, track2track and tracktag are not.
485    If multiple albums are applied gain at once,
486    add_replay_gain must be called on an album-by-album basis
487    rather than on the entire set.
488*** DONE Double-check ReplayGainReader
489    Ensure its output is consistent with other implementations.
490** DONE Fix multi-channel audio handling
491   It's important that channel mapping information be preserved
492   when transcoding between sources with 3+ channels.
493   This likely means another flag for PCMReader so that
494   from_pcm() can build a file with the proper channel mask set.
495   However, it may also be necessary to build some sort of
496   channel reordering mechanism in the event that formats differ
497   on how channels are to be ordered in the file.
498*** Channel Counts and Ordering
499| Format     | Maximum Channels | Ordering               |
500|------------+------------------+------------------------|
501| AAC        |               48 | stereo-only (via faac) |
502| AIFF       |             2^16 | predefined             |
503| Sun AU     |             2^32 | mostly undefined       |
504| FLAC       |                8 | as WAVE                |
505| M4A        |               48 | as WAVE?               |
506| MP2        |                2 | stereo-only            |
507| MP3        |                2 | stereo-only            |
508| Musepack   |                2 | stereo-only            |
509| Ogg FLAC   |                8 | as WAVE                |
510| Ogg Vorbis |              255 | predefined             |
511| Ogg Speex  |             2^32 | stereo-only            |
512| RIFF WAVE  |             2^16 | predefined             |
513| WavPack    |               16 | as WAVE                |
514|------------+------------------+------------------------|
515*** DONE Fix AudioFile definitions to support channel_mask()
516    - [X] AACAudio
517    - [X] AiffAudio
518    - [X] AuAudio
519    - [X] FlacAudio
520    - [X] M4AAudio
521    - [X] MP2Audio
522    - [X] MP3Audio
523    - [X] OggFlacAudio
524    - [X] VorbisAudio
525    - [X] SpeexAudio
526    - [X] WaveAudio
527    - [X] WavPackAudio
528*** DONE Fix to_pcm() methods to support channel_mask
529    - [X] AACAudio
530    - [X] AiffAudio
531    - [X] AuAudio
532    - [X] FlacAudio
533    - [X] M4AAudio
534    - [X] MP2Audio
535    - [X] MP3Audio
536    - [X] OggFlacAudio
537    - [X] VorbisAudio
538    - [X] SpeexAudio
539    - [X] WaveAudio
540    - [X] WavPackAudio
541*** DONE Fix from_pcm() classmethods to support channel_mask
542    - [X] AACAudio
543    - [X] AiffAudio
544    - [X] AuAudio
545    - [X] FlacAudio
546    - [X] M4AAudio
547    - [X] MP2Audio
548    - [X] MP3Audio
549    - [X] OggFlacAudio
550    - [X] VorbisAudio
551    - [X] SpeexAudio
552    - [X] WaveAudio
553    - [X] WavPackAudio
554*** DONE Fix alternate PCMReaders to support channel_mask
555    - [X] BufferedPCMReader
556    - [X] PCMConverter
557    - [X] ReplayGainReader
558    - [X] CDTrackReader
559    - [X] OffsetCDTrackReader
560    - [X] PCMCat
561*** DONE Handle undefined channel masks in a sane way
562**** DONE Fix to_pcm() methods to output undefined ChannelMasks
563     If a format has not defined channel assignments for a given
564     channel count, its to_pcm() method should return undefined ChannelMasks.
565     - [X] AACAudio
566     - [X] AiffAudio
567     - [X] AuAudio
568     - [X] FlacAudio
569     - [X] M4AAudio
570     - [X] MP2Audio
571     - [X] MP3Audio
572     - [X] OggFlacAudio
573     - [X] VorbisAudio
574     - [X] SpeexAudio
575     - [X] WaveAudio
576     - [X] WavPackAudio
577**** DONE Fix from_pcm() classmethods to accept undefined ChannelMasks
578     So long as the number of channels is acceptable,
579     audio formats are free to place undefined ChannelMasks
580     in whatever arrangement they'd like.
581     - [X] AACAudio
582     - [X] AiffAudio
583     - [X] AuAudio
584     - [X] FlacAudio
585     - [X] M4AAudio
586     - [X] MP2Audio
587     - [X] MP3Audio
588     - [X] OggFlacAudio
589     - [X] VorbisAudio
590     - [X] SpeexAudio
591     - [X] WaveAudio
592     - [X] WavPackAudio
593*** DONE Unit test multichannel encoding and channel_mask handling
594**** DONE Ensure all AudioFile types have a working channel_mask() method
595     Even 2 channel audio should yield something valid.
596**** DONE Ensure all to_pcm() methods yield a matching channel_mask attribute
597**** DONE Ensure channel_mask is preserved between from_pcm(to_pcm()) calls
598**** DONE Ensure channel_mask is preserved between to_wave()/from_wave() calls
599**** DONE Ensure channels are actually stored in the proper order
600     This is less of an issue for .wav, .flac, .oga or .wv
601     which already store channels in RIFF WAVE order
602     and more of an issue for Ogg Vorbis and other formats that do not.
603**** DONE Ensure UnsupportedChannelMask is raised when necessary
604     This includes calls to from_pcm() and from_wave()
605*** DONE Ensure PCMReader.channel_mask is always an integer
606** DONE Fix the unit test error messages
607** DONE Make the programming documentation web-capable
608    It should render consistently with the regular Python reference docs
609    and be placed both in the source tree and on the website
610    for better accessability.
611*** DONE Document audiotools
612     - [X] AudioFile
613     - [X] BufferedPCMReader
614     - [X] ChannelMask
615     - [X] ExecQueue
616     - [X] Image
617     - [X] MetaData
618     - [X] PCMConverter
619     - [X] PCMReader
620     - [X] PCMCat
621     - [X] ReorderedPCMReader
622     - [X] ReplayGain
623     - [X] ReplayGainReader
624     - [X] Messenger
625     - [X] AlbumMetaData
626     - [X] CDTrackLog
627     - [X] CDDA
628     - [X] CDTrackReader
629     - [X] calculate_replay_gain
630     - [X] filename_to_type
631     - [X] find_glade_file
632     - [X] group_tracks
633     - [X] open
634     - [X] open_directory
635     - [X] open_files
636     - [X] pcm_cmp
637     - [X] pcm_split
638     - [X] read_metadata_file
639     - [X] read_sheet
640     - [X] stripped_pcm_cmp
641     - [X] transfer_data
642     - [X] transfer_framelist_data
643     - [X] BIN
644     - [X] TYPE_MAP
645     - [X] VERSION
646     - [X] AVAILABLE_TYPES
647*** DONE Document audiotools.pcm
648     - [X] FloatFrameList
649     - [X] FrameList
650     - [X] from_channels
651     - [X] from_float_channels
652     - [X] from_float_frames
653     - [X] from_frames
654     - [X] from_list
655*** DONE Document audiotools.resample
656     - [X] Resampler
657*** DONE Document audiotools.replaygain
658     - [X] ReplayGain
659*** DONE Document audiotools.cdio
660     - [X] CDDA
661     - [X] set_read_callback
662*** DONE Document audiotools.cue
663     - [X] Cuesheet
664     - [X] read_cuesheet
665     - [X] CueException
666*** DONE Document audiotools.toc
667     - [X] TOCFile
668     - [X] read_tocfile
669     - [X] TOCException
670** DONE Make reference documentation render consistently
671*** DONE Ensure documents render in letter and A4 size
672*** DONE Add Creative Commons licensing to source code and doc itself
673*** DONE Add internal PDF linkage
674    The file should have working bookmarks
675    and internal links so one can click directly to a chapter
676*** DONE Add new introduction
677*** DONE Basics
678**** DONE Hexadecimal
679**** DONE Endianness
680**** DONE Signed values
681     How to decode/encode signed integers should be properly explained
682**** DONE Character Encodings
683**** DONE PCM
684*** DONE .wav
685**** DONE the RIFF WAVE stream
686**** DONE the fmt chunk
687**** DONE the WAVEFORMATEXTENSIBLE fmt chunk
688**** DONE the data chunk
689**** DONE channel mapping
690*** DONE .aiff
691**** DONE the AIFF stream
692**** DONE the COMM chunk
693***** TODO 80 bit IEEE standard 754 floating point
694**** DONE the SSND chunk
695*** DONE .au
696**** DONE the AU stream
697**** DONE the AU header
698*** DONE .flac
699**** DONE the FLAC file stream
700**** DONE FLAC metadata
701***** DONE the PADDING metadata block
702***** DONE the APPLICATION metadata block
703***** DONE the SEEKTABLE metadata block
704***** DONE the VORBIS_COMMENT metadata block
705***** DONE the PICTURE metadata block
706***** DONE the CUESHEET metadata block
707**** DONE FLAC decoding
708***** DONE the CONSTANT subframe
709***** DONE the VERBATIM subframe
710***** DONE the FIXED subframe
711***** DONE the LPC subframe
712***** DONE the Residual
713****** DONE Rice Encoding
714***** DONE Channels
715***** DONE Wasted bits per sample
716**** DONE FLAC encoding
717***** DONE Metadata header
718***** DONE the STREAMINFO metadata block
719***** DONE Frame header
720***** DONE Channel assignment
721***** DONE Subframe header
722***** DONE the CONSTANT subframe
723***** DONE the VERBATIM subframe
724***** DONE the FIXED subframe
725***** DONE the LPC subframe
726****** DONE Windowing
727****** DONE Computing autocorrelation
728****** DONE LP coefficient calculation
729****** DONE Best order estimation
730****** DONE Best order exhaustive search
731****** DONE Quantizing coefficients
732****** DONE Calculation Residual
733***** DONE the Residual
734****** DONE Residual Values
735**** DONE the Checksums
736***** TODO CRC-8
737***** TODO CRC-16
738*** DONE .ape
739**** DONE the Monkey's Audio stream
740**** DONE the APE Descriptor
741**** DONE the APE Header
742**** DONE the APEv2 tag
743**** DONE the APEv2 tag header/footer
744**** DONE the APEv2 flags
745*** DONE .wv
746**** DONE the WavPack file stream
747**** DONE a WavPack block header
748**** DONE a WavPack sub-block header
749*** DONE .mp3
750**** DONE the MP3 file stream
751**** DONE an MPEG frame header
752***** DONE the Xing header
753**** DONE the ID3v1 tag
754***** DONE ID3v1
755***** DONE ID3v1.1
756**** DONE the ID3v2 tag
757***** DONE the ID3v2 stream
758***** DONE ID3v2.2
759****** DONE the ID3v2.2 Header
760****** DONE an ID3v2.2 Frame
761****** DONE ID3v2.2 Frame IDs
762****** DONE the PIC Frame
763***** DONE ID3v2.3
764****** DONE the ID3v2.3 Header
765****** DONE an ID3v2.3 Frame
766****** DONE ID3v2.3 Frame IDs
767****** DONE the APIC Frame
768***** DONE ID3v2.4
769****** DONE the ID3v2.4 Header
770****** DONE an ID3v2.4 Frame
771****** DONE ID3v2.4 Frame IDs
772****** DONE the APIC Frame
773*** DONE .ogg
774**** DONE the Ogg file stream
775**** DONE an Ogg Page
776**** DONE Ogg packets
777**** DONE the Identification packet
778**** DONE the Comment packet
779**** DONE Channel assignment
780*** DONE .spx
781**** DONE the Header packet
782**** DONE the Comment packet
783*** DONE .oga
784**** DONE the Ogg FLAC file stream
785**** DONE the STREAMINFO metadata packet
786**** DONE the Metadata packets
787*** DONE .m4a
788**** DONE the QuickTime file stream
789**** DONE a QuickTime atom
790**** DONE Container atoms
791**** DONE M4A atoms
792***** DONE the ftyp atom
793***** DONE the mvhd atom
794***** DONE the tkhd atom
795***** DONE the mdhd atom
796***** DONE the hdlr atom
797***** DONE the smhd atom
798***** DONE the dref atom
799***** DONE the stsd atom
800***** DONE the mp4a atom
801***** DONE the stts atom
802***** DONE the stsc atom
803***** DONE the stsz atom
804***** DONE the stco atom
805***** DONE the meta atom
806****** DONE the trkn sub-atom
807****** DONE the disk sub-atom
808*** DONE .mpc
809**** DONE Musepack SV7
810***** DONE the Musepack SV7 file stream
811***** DONE the Musepack SV7 header
812**** DONE Musepack SV8
813***** DONE the Musepack SV8 file stream
814***** DONE Nut-encoded values
815***** DONE the SH packet
816***** DONE the SE packet
817***** DONE the RG packet
818***** DONE the EI packet
819*** DONE FreeDB
820**** DONE Native Protocol
821***** DONE the Disc ID
822***** DONE Initial Greeting
823***** DONE Client-Server Handshake
824***** DONE Set Protocol Level
825***** DONE Query Database
826***** DONE Read XMCD Data
827***** DONE Close Connection
828**** DONE Web Protocol
829**** DONE XMCD
830*** DONE MusicBrainz
831**** DONE Searching Releases
832***** DONE The Disc ID
833***** DONE Server Query
834***** DONE Release XML
835**** DONE MusieBrainz XML
836*** DONE ReplayGain
837**** DONE Applying ReplayGain
838**** DONE Calculating ReplayGain
839***** DONE the Equal Loudness Filter
840      This should be re-documented to be closer to the actual implementation
841****** TODO the Yule Filter
842****** TODO the Buffer Filter
843****** TODO a Filtering Example
844***** DONE RMS Energy Blocks
845***** DONE Statistical Processing and Calibration
846*** DONE References
847*** DONE Remove old troff reference documentation
848*** DONE Add title and author to PDF documentation
849** DONE Ensure make(1) from doc/ directory builds both doc trees
850** DONE Seperate unreadable files from unknown files
851   Files we're unable to read should be handled differently
852   from files we're unable to understand.
853*** DONE Update audiotools.open() to raise IOErrors
854*** DONE Update audiotools.open_files() to handle IOErrors
855*** DONE Update audiotools.open_directory() to handle IOErrors
856*** DONE Update internal calls to open()/open_files() to handle IOErrors
857    - [X] __flac__.py
858    - [X] __m4a__.py
859    - [X] __mp3__.py
860    - [X] __vorbis__.py
861    - [X] __wavpack__.py
862    - [X] __wav__.py
863*** DONE Update tools which use open()/open_files() to handle IOErrors
864    - [X] coverdump
865    - [X] editxmcd
866    - [X] track2cd
867    - [X] track2track
868    - [X] track2xmcd
869    - [X] trackcat
870    - [X] trackcmp
871    - [X] trackinfo
872    - [X] tracklength
873    - [X] tracklint
874    - [X] trackplay
875    - [X] trackrename
876    - [X] tracksplit
877    - [X] tracktag
878*** DONE Add unit tests to demonstrate new behavior
879*** DONE Update programming documentation with new behavior
880** DONE Ensure embedded cuesheets aren't clobbered by adding more metadata
881** DONE Adjust %(album_track_number)s to accomodate more than 9 albums
882   For example, track 2 of 7, album 5 of 11 in format
883   "%(album_track_number)s - %(track_name)s.%(suffix)s should be:
884   "0502 - name.suffix"
885** DONE editxmcd's "New" command should work with embedded cuesheets
886   Selecting a single disc image with embedded cuesheets should
887   fill in the proper XMCD/MusicBrainz fields
888** DONE Ensure add_replay_gain used on hi-def tracks doesn't raise errors
889** DONE Add Shorten support
890   I don't expect people have a lot of shn files lying around
891   and nobody should be using it for new data
892   (though I'll add a rudimentary encoder for completeness' sake)
893   but it's interesting to document for historical reasons.
894*** DONE Build complete decoder
895    - [X] DIFF0
896    - [X] DIFF1
897    - [X] DIFF2
898    - [X] DIFF3
899    - [X] QUIT
900    - [X] BLOCKSIZE
901    - [X] BITSHIFT
902    - [X] QLPC
903    - [X] ZERO
904    - [X] VERBATIM
905    I'll likely limit support to Shorten version2/3
906    since generating older versions will be a challenge
907    and this format is obscure enough as it is.
908*** DONE Build partial encoder
909    - [X] DIFF0
910    - [X] DIFF1
911    - [X] DIFF2
912    - [X] QUIT
913    - [X] BLOCKSIZE
914    - [X] ZERO
915    - [X] VERBATIM
916**** DONE Convert partial encoder for standalone use
917     To ensure there aren't any memory leaks
918*** DONE Add ShortenAudio type to audiotools Python core
919*** DONE Add Shorten-specific unit tests
920*** DONE Document Shorten
921    - [X] Shorten data types
922    - [X] the Shorten file stream
923    - [X] the decoding process
924    - [X] the encoding process
925** DONE trackcmp should give exact PCM frame/byte of first mismatch
926*** DONE update unit tests to cover new behavior
927    - [X] test_trackcmp
928    - [X] test_trackcmp1
929    - [X] test_trackcmp2
930    - [X] test_trackcmp3
931    - [X] test_trackcmp4
932*** DONE update manual page to cover new behavior
933** DONE Add analyzers for built-in decoders
934   Analagous to flac(1)'s --analyze option,
935   this will be an .analyze_frame() method that returns a Python dict
936   of ints/lists/dicts containing frame data on each pass,
937   or None at the stream's end.
938   This will provide both an easier way to visualize the file's internals,
939   and also a debugging aid.
940*** DONE FlacDecoder
941*** DONE SHNDecoder
942*** DONE ALACDecoder
943** DONE Add more examples
944   A lot of handy new features aren't documented with examples and walkthroughs.
945   Examples to add include:
946- [X] a full multi-CD example, detailing the use of --album-number
947- [X] an image embedding walkthrough
948- [X] a CD image creation, splitting, burning example involving TOC/CUE files
949- [X] an XMCD walkthrough with fetching, editing and tagging
950** DONE Update documentation to cover concrete MetaData classes
951   - [X] ApeTag
952   - [X] FlacMetaData
953   - [X] ID3v1Comment
954   - [X] ID3v22Comment
955   - [X] ID3v23Comment
956   - [X] ID3v24Comment
957   - [X] ID3CommentPair
958   - [X] M4AMetaData
959   - [X] VorbisComment
960** DONE Ensure man pages install correctly on Mac OS X
961** DONE add a %(basename)s --format attribute
962   For example, given the path: "/foo/bar/01 - track name.mp3"
963   the %(basename)s attribute would be: "01 - track name"
964   allowing one to ignore its internal metadata entirely
965   and use original names.
966*** DONE Update AudioFile.track_name to support attribute
967*** DONE Update tools that call AudioFile.track_name
968    - [X] cd2track
969    - [X] track2track
970    - [X] trackrename
971    - [X] tracksplit
972*** DONE Update man pages for tools that call AudioFile.track_name
973    - [X] track2track.1
974    - [X] trackrename.1
975    - [X] audiotools.cfg.5
976*** DONE Add unit test support for new attribute
977*** DONE Add documentation for new field to programming reference
978*** DONE Add documentation for format strings to programming reference
979** DONE Update AudioFile.track_name classmethod
980   the previous behavior was a kludge cobbled together over time
981   Its new call method is:  track_name(file_path, metadata, format)
982   so that track_number and album_number can be pulled from file_path
983   directly instead of passed in from outside.
984*** DONE Update tools to support new calling method
985    - [X] track2track
986    - [X] cd2track
987    - [X] trackrename
988    - [X] tracksplit
989*** DONE Update programming documentation
990*** DONE Add unit tests
991*** DONE Update old unit tests with new behavior
992*** DONE unit test suffix field
993** DONE Update Python code to support PEP 8
994   Following accepted Python style should make the code more
995   accessible and maintainable in the long run.
996   It's also a good opportunity to clean up and simplify code
997   without changing the actual API interface.
998*** DONE Update core modules
999**** DONE __aiff__.py
1000***** DONE adjust syntax
1001***** DONE add docstrings
1002      - [X] IEEE_Extended
1003      - [X] AiffException
1004      - [X] AiffAudio
1005      - [X] AiffAudio.bits_per_sample
1006      - [X] AiffAudio.channels
1007      - [X] AiffAudio.channel_mask
1008      - [X] AiffAudio.lossless
1009      - [X] AiffAudio.total_frames
1010      - [X] AiffAudio.sample_rate
1011      - [X] AiffAudio.is_type
1012      - [X] AiffAudio.chunks
1013      - [X] AiffAudio.comm_chunk
1014      - [X] AiffAudio.chunk_files
1015      - [X] AiffAudio.get_metadata
1016      - [X] AiffAudio.set_metadata
1017      - [X] AiffAudio.delete_metadata
1018      - [X] AiffAudio.to_pcm
1019      - [X] AiffAudio.from_pcm
1020      - [X] AIFFChannelMask
1021      - [X] AIFFChannelMask.channels
1022**** DONE __ape__.py
1023***** DONE adjust syntax
1024***** DONE add docstrings
1025      - [X] ApeTagItem
1026      - [X] ApeTagItem.build
1027      - [X] ApeTagItem.binary
1028      - [X] ApeTagItem.external
1029      - [X] ApeTagItem.string
1030      - [X] ApeTag
1031      - [X] ApeTag.converted
1032      - [X] ApeTag.merge
1033      - [X] ApeTag.supports_images
1034      - [X] ApeTag.add_image
1035      - [X] ApeTag.delete_image
1036      - [X] ApeTag.images
1037      - [X] ApeTag.read
1038      - [X] ApeTag.build
1039      - [X] ApeTaggedAudio
1040      - [X] ApeTaggedAudio.get_metadata
1041      - [X] ApeTaggedAudio.set_metadata
1042      - [X] ApeTaggedAudio.delete_metadata
1043      - [X] ApeAudio
1044      - [X] ApeAudio.is_type
1045      - [X] ApeAudio.lossless
1046      - [X] ApeAudio.supports_foreign_riff_chunks
1047      - [X] ApeAudio.has_foreign_riff_chunks
1048      - [X] ApeAudio.bits_per_sample
1049      - [X] ApeAudio.channels
1050      - [X] ApeAudio.total_frames
1051      - [X] ApeAudio.sample_rate
1052      - [X] ApeAudio.to_wave
1053      - [X] ApeAudio.from_wave
1054**** DONE __au__.py
1055***** DONE adjust syntax
1056***** DONE add docstrings
1057      - [X] AuAudio
1058      - [X] AuAudio.is_type
1059      - [X] AuAudio.lossless
1060      - [X] AuAudio.bits_per_sample
1061      - [X] AuAudio.channels
1062      - [X] AuAudio.channel_mask
1063      - [X] AuAudio.sample_rate
1064      - [X] AuAudio.total_frames
1065      - [X] AuAudio.to_pcm
1066      - [X] AuAudio.from_pcm
1067      - [X] AuAudio.track_name
1068**** DONE __flac__.py
1069***** DONE adjust syntax
1070***** DONE add docstrings
1071      - [ ] FlacException
1072      - [X] FlacMetaDataBlockTooLarge
1073      - [X] FlacMetaDataBlock
1074      - [X] FlacMetaDataBlock.build_block
1075      - [X] FlacMetaData
1076      - [X] FlacMetaData.converted
1077      - [X] FlacMetaData.merge
1078      - [X] FlacMetaData.add_image
1079      - [X] FlacMetaData.delete_image
1080      - [X] FlacMetaData.images
1081      - [X] FlacMetaData.metadata_blocks
1082      - [X] FlacMetaData.build
1083      - [X] FlacMetaData.supports_images
1084      - [X] FlacVorbisComment
1085      - [X] FlacVorbisComment.build_block
1086      - [X] FlacVorbisComment.converted
1087      - [X] FlacPictureComment
1088      - [X] FlacPictureComment.converted
1089      - [X] FlacPictureComment.type_string
1090      - [X] FlacPictureComment.build
1091      - [X] FlacPictureComment.build_block
1092      - [X] FlacCueSheet
1093      - [X] FlacCueSheet.build_block
1094      - [X] FlacCueSheet.converted
1095      - [X] FlacCueSheet.catalog
1096      - [X] FlacCueSheet.ISRCs
1097      - [X] FlacCueSheet.indexes
1098      - [X] FlacCueSheet.pcm_lengths
1099      - [X] FlacAudio
1100      - [X] FlacAudio.is_type
1101      - [X] FlacAudio.channel_mask
1102      - [X] FlacAudio.lossless
1103      - [X] FlacAudio.supports_foreign_riff_chunks
1104      - [X] FlacAudio.get_metadata
1105      - [X] FlacAudio.set_metadata
1106      - [X] FlacAudio.metadata_length
1107      - [X] FlacAudio.delete_metadata
1108      - [X] FlacAudio.set_cuesheet
1109      - [X] FlacAudio.get_cuesheet
1110      - [X] FlacAudio.to_pcm
1111      - [X] FlacAudio.from_pcm
1112      - [X] FlacAudio.has_foreign_riff_chunks
1113      - [X] FlacAudio.riff_wave_chunks
1114      - [X] FlacAudio.to_wave
1115      - [X] FlacAudio.from_wave
1116      - [X] FlacAudio.bits_per_sample
1117      - [X] FlacAudio.channels
1118      - [X] FlacAudio.total_frames
1119      - [X] FlacAudio.sample_rate
1120      - [X] FlacAudio.add_replay_gain
1121      - [X] FlacAudio.can_add_replay_gain
1122      - [X] FlacAudio.lossless_replay_gain
1123      - [X] FlacAudio.replay_gain
1124      - [X] FlacAudio.sub_pcm_tracks
1125      - [X] OggFlacAudio
1126      - [X] OggFlacAudio.is_type
1127      - [X] OggFlacAudio.get_metadata
1128      - [X] OggFlacAudio.set_metadata
1129      - [X] OggFlacAudio.metadata_length
1130      - [X] OggFlacAudio.to_pcm
1131      - [X] OggFlacAudio.from_pcm
1132      - [X] OggFlacAudio.sub_pcm_tracks
1133      - [X] OggFlacAudio.supports_foreign_riff_chunks
1134**** DONE __freedb__.py
1135***** DONE adjust syntax
1136***** DONE add docstrings
1137      - [X] XMCDException
1138      - [X] XMCD
1139      - [X] XMCD.key_digits
1140      - [X] XMCD.build
1141      - [X] XMCD.read
1142      - [X] XMCD.read_data
1143      - [X] XMCD.from_files
1144      - [X] XMCD.from_cuesheet
1145      - [X] XMCD.metadata
1146      - [X] DiscID
1147      - [X] DiscID.from_cdda
1148      - [X] DiscID.add
1149      - [X] DiscID.offsets
1150      - [X] DiscID.length
1151      - [X] DiscID.idsuffix
1152      - [X] DiscID.freedb_id
1153      - [X] DiscID.toxmcd
1154      - [X] FreeDBException
1155      - [X] FreeDB
1156      - [X] FreeDB.connect
1157      - [X] FreeDB.close
1158      - [X] FreeDB.write
1159      - [X] FreeDB.read
1160      - [X] FreeDB.query
1161      - [X] FreeDB.read_data
1162      - [X] FreeDBWeb
1163      - [X] FreeDBWeb.connect
1164      - [X] FreeDBWeb.close
1165      - [X] FreeDBWeb.write
1166      - [X] FreeDBWeb.read
1167      - [X] FreeDBWeb.query
1168      - [X] FreeDBWeb.read_data
1169      - [X] get_xmcd
1170**** DONE __id3__.py
1171***** DONE adjust syntax
1172***** DONE add docstrings
1173      - [X] UCS2Codec
1174      - [X] UCS2Codec.fix_char
1175      - [X] UCS2Codec.encode
1176      - [X] UCS2Codec.decode
1177      - [X] UnsupportedID3v2Version
1178      - [X] Syncsafe32
1179      - [X] UBInt24
1180      - [X] WidecharCStringAdapter
1181      - [X] UCS2CString
1182      - [X] UTF16CString
1183      - [X] UTF16BECString
1184      - [X] ID3v22Frame
1185      - [X] ID3v22Frame.build
1186      - [X] ID3v22Frame.parse
1187      - [X] ID3v22TextFrame
1188      - [X] ID3v22TextFrame.total
1189      - [X] ID3v22TextFrame.from_unicode
1190      - [X] ID3v22TextFrame.build
1191      - [X] ID3v22ComFrame
1192      - [X] ID3v22ComFrame.from_unicode
1193      - [X] ID3v22ComFrame.build
1194      - [X] ID3v22PicFrame
1195      - [X] ID3v22PicFrame.type_string
1196      - [X] ID3v22PicFrame.build
1197      - [X] ID3v22PicFrame.converted
1198      - [X] ID3v22Comment
1199      - [X] ID3v22Comment.add_image
1200      - [X] ID3v22Comment.delete_image
1201      - [X] ID3v22Comment.images
1202      - [X] ID3v22Comment.parse
1203      - [X] ID3v22Comment.converted
1204      - [X] ID3v22Comment.merge
1205      - [X] ID3v22Comment.build
1206      - [X] ID3v22Comment.skip
1207      - [X] ID3v22Comment.read_id3v2_comment
1208      - [X] ID3v23Frame
1209      - [X] ID3v23Frame.build
1210      - [X] ID3v23Frame.parse
1211      - [X] ID3v23TextFrame
1212      - [X] ID3v23TextFrame.total
1213      - [X] ID3v23TextFrame.from_unicode
1214      - [X] ID3v23TextFrame.build
1215      - [X] ID3v23PicFrame
1216      - [X] ID3v23PicFrame.build
1217      - [X] ID3v23PicFrame.converted
1218      - [X] ID3v23ComFrame
1219      - [X] ID3v23ComFrame.from_unicode
1220      - [X] ID3v23ComFrame.build
1221      - [X] ID3v23Comment
1222      - [X] ID3v23Comment.add_image
1223      - [X] ID3v23Comment.delete_image
1224      - [X] ID3v23Comment.images
1225      - [X] ID3v23Comment.build
1226      - [X] ID3v24Frame
1227      - [X] ID3v24Frame.build
1228      - [X] ID3v24Frame.parse
1229      - [X] ID3v24TextFrame
1230      - [X] ID3v24TextFrame.total
1231      - [X] ID3v24TextFrame.from_unicode
1232      - [X] ID3v24TextFrame.build
1233      - [X] ID3v24PicFrame
1234      - [X] ID3v24PicFrame.build
1235      - [X] ID3v24PicFrame.converted
1236      - [X] ID3v24ComFrame
1237      - [X] ID3v24ComFrame.from_unicode
1238      - [X] ID3v24ComFrame.build
1239      - [X] ID3v24Comment
1240      - [X] ID3v24Comment.build
1241      - [X] ID3CommentPair
1242      - [X] ID3CommentPair.converted
1243      - [X] ID3CommentPair.merge
1244      - [X] ID3CommentPair.images
1245      - [X] ID3CommentPair.add_image
1246      - [X] ID3CommentPair.delete_image
1247      - [X] ID3CommentPair.supports_images
1248**** DONE __id3v1__.py
1249***** DONE adjust syntax
1250***** DONE add docstrings
1251      - [X] ID3v1Comment
1252      - [X] ID3v1Comment.read_id3v1_comment
1253      - [X] ID3v1Comment.build_id3v1
1254      - [X] ID3v1Comment.supports_images
1255      - [X] ID3v1Comment.converted
1256      - [X] ID3v1Comment.build_tag
1257      - [X] ID3v1Comment.images
1258**** DONE __image__.py
1259***** DONE adjust syntax
1260***** DONE add docstrings
1261      - [X] image_metrics
1262      - [X] ImageMetrics
1263      - [X] InvalidImage
1264      - [X] InvalidJPEG
1265      - [X] InvalidPNG
1266      - [X] InvalidBMP
1267      - [X] InvalidGIF
1268      - [X] InvalidTIFF
1269      - [X] can_thumbnail
1270      - [X] thumbnail_formats
1271      - [X] thumbnail_image
1272**** DONE __init__.py
1273***** DONE adjust syntax
1274***** DONE add docstrings
1275      - [X] __init__.py
1276      - [X] RawConfigParser
1277      - [X] RawConfigParser.get_default
1278      - [X] RawConfigParser.getint_default
1279      - [X] find_glade_file
1280      - [X] OptionParser
1281      - [X] Messenger
1282      - [X] str_width
1283      - [X] VerboseMessenger
1284      - [X] VerboseMessenger.output
1285      - [X] VerboseMessenger.partial_output
1286      - [X] VerboseMessenger.new_row
1287      - [X] VerboseMessenger.blank_row
1288      - [X] VerboseMessenger.divider_row
1289      - [X] VerboseMessenger.output_column
1290      - [X] VerboseMessenger.output_rows
1291      - [X] VerboseMessenger.info
1292      - [X] VerboseMessenger.partial_info
1293      - [X] VerboseMessenger.error
1294      - [X] VerboseMessenger.warning
1295      - [X] VerboseMessenger.usage
1296      - [X] VerboseMessenger.filename
1297      - [X] VerboseMessenger.ansi
1298      - [X] VerboseMessenger.ansi_err
1299      - [X] SilentMessenger
1300      - [X] SilentMessenger.output
1301      - [X] SilentMessenger.partial_output
1302      - [X] SilentMessenger.warning
1303      - [X] SilentMessenger.info
1304      - [X] SilentMessenger.partial_info
1305      - [X] UnsupportedFile
1306      - [X] InvalidFile
1307      - [X] InvalidFormat
1308      - [X] EncodingError
1309      - [X] UnsupportedChannelMask
1310      - [X] UnsupportedChannelCount
1311      - [X] DecodingError
1312      - [X] open
1313      - [X] open_files
1314      - [X] open_directory
1315      - [X] UnknownAudioType
1316      - [X] AmbiguousAudioType
1317      - [X] filename_to_type
1318      - [X] ChannelMask
1319      - [X] ChannelMask.defined
1320      - [X] ChannelMask.undefined
1321      - [X] ChannelMask.channels
1322      - [X] ChannelMask.index
1323      - [X] ChannelMask.from_fields
1324      - [X] ChannelMask.from_channels
1325      - [X] PCMReader
1326      - [X] PCMReader.read
1327      - [X] PCMReader.close
1328      - [X] PCMReaderError
1329      - [X] PCMReaderError.read
1330      - [X] PCMReaderError.close
1331      - [X] ReorderedPCMReader
1332      - [X] ReorderedPCMReader.read
1333      - [X] ReorderedPCMReader.close
1334      - [X] transfer_data
1335      - [X] transfer_framelist_data
1336      - [X] threaded_transfer_framelist_data
1337      - [X] pcm_cmp
1338      - [X] stripped_pcm_cmp
1339      - [X] pcm_frame_cmp
1340      - [X] PCMCat
1341      - [X] PCMCat.read
1342      - [X] PCMCat.close
1343      - [X] BufferedPCMReader
1344      - [X] BufferedPCMReader.close
1345      - [X] BufferedPCMReader.read
1346      - [X] pcm_split
1347      - [X] PCMConverter
1348      - [X] PCMConverter.read
1349      - [X] PCMConverter.close
1350      - [X] applicable_replay_gain
1351      - [X] calculate_replay_gain
1352      - [X] InterruptableReader
1353      - [X] InterruptableReader.stop
1354      - [X] InterruptableReader.send_data
1355      - [X] InterruptableReader.read
1356      - [X] ignore_sigint
1357      - [X] make_dirs
1358      - [X] MetaData
1359      - [X] MetaData.converted
1360      - [X] MetaData.supports_images
1361      - [X] MetaData.images
1362      - [X] MetaData.front_covers
1363      - [X] MetaData.back_covers
1364      - [X] MetaData.leaflet_pages
1365      - [X] MetaData.media_images
1366      - [X] MetaData.other_images
1367      - [X] MetaData.add_image
1368      - [X] MetaData.delete_image
1369      - [X] MetaData.merge
1370      - [X] AlbumMetaData
1371      - [X] AlbumMetaData.metadata
1372      - [X] MetaDataFileException
1373      - [X] Image
1374      - [X] Image.suffix
1375      - [X] Image.type_string
1376      - [X] Image.new
1377      - [X] Image.thumbnail
1378      - [X] ReplayGain
1379      - [X] UnsupportedTracknameField
1380      - [X] AudioFile
1381      - [X] AudioFile.is_type
1382      - [X] AudioFile.bits_per_sample
1383      - [X] AudioFile.channels
1384      - [X] AudioFile.channel_mask
1385      - [X] AudioFile.lossless
1386      - [X] AudioFile.set_metadata
1387      - [X] AudioFile.get_metadata
1388      - [X] AudioFile.delete_metadata
1389      - [X] AudioFile.total_frames
1390      - [X] AudioFile.cd_frames
1391      - [X] AudioFile.sample_rate
1392      - [X] AudioFile.to_pcm
1393      - [X] AudioFile.from_pcm
1394      - [X] AudioFile.to_wave
1395      - [X] AudioFile.from_wave
1396      - [X] AudioFile.supports_foreign_riff_chunks
1397      - [X] AudioFile.has_foreign_riff_chunks
1398      - [X] AudioFile.track_number
1399      - [X] AudioFile.album_number
1400      - [X] AudioFile.track_name
1401      - [X] AudioFile.add_replay_gain
1402      - [X] AudioFile.can_add_replay_gain
1403      - [X] AudioFile.lossless_replay_gain
1404      - [X] AudioFile.replay_gain
1405      - [X] AudioFile.set_cuesheet
1406      - [X] AudioFile.get_cuesheet
1407      - [X] AudioFile.has_binaries
1408      - [X] DummyAudioFile
1409      - [X] DummyAudioFile.get_metadata
1410      - [X] DummyAudioFile.cd_frames
1411      - [X] DummyAudioFile.track_number
1412      - [X] DummyAudioFile.sample_rate
1413      - [X] DummyAudioFile.total_frames
1414      - [X] SheetException
1415      - [X] read_sheet
1416      - [X] parse_timestamp
1417      - [X] build_timestamp
1418      - [X] sheet_to_unicode
1419      - [X] at_a_time
1420      - [X] CDDA
1421      - [X] RawCDDA
1422      - [X] RawCDDA.length
1423      - [X] RawCDDA.close
1424      - [X] RawCDDA.first_sector
1425      - [X] RawCDDA.last_sector
1426      - [X] OffsetCDDA
1427      - [X] OffsetCDDA.close
1428      - [X] CDTrackLog
1429      - [X] CDTrackReader
1430      - [X] CDTrackReader.offset
1431      - [X] CDTrackReader.length
1432      - [X] CDTrackReader.log
1433      - [X] CDTrackReader.read
1434      - [X] CDTrackReader.close
1435      - [X] OffsetCDTrackReader
1436      - [X] OffsetCDTrackReader.offset
1437      - [X] OffsetCDTrackReader.length
1438      - [X] OffsetCDTrackReader.log
1439      - [X] OffsetCDTrackReader.read
1440      - [X] OffsetCDTrackReader.close
1441      - [X] read_metadata_file
1442      - [X] ExecQueue
1443      - [X] ExecQueue.execute
1444      - [X] ExecQueue.run
1445      - [X] BitstreamReader
1446      - [X] BitstreamReader.byte_align
1447      - [X] BitstreamReader.read
1448      - [X] BitstreamReader.unread
1449      - [X] BitstreamReader.read_signed
1450      - [X] BitstreamReader.unary
1451      - [X] BitstreamReader.tell
1452      - [X] BitstreamReader.close
1453      - [X] BitstreamWriter
1454      - [X] BitstreamWriter.byte_align
1455      - [X] BitstreamWriter.write
1456      - [X] BitstreamWriter.write_signed
1457      - [X] BitstreamWriter.unary
1458      - [X] BitstreamWriter.tell
1459      - [X] BitstreamWriter.close
1460**** DONE __m4a_atoms__.py
1461***** DONE adjust syntax
1462***** DONE add docstrings
1463      - [X] VersionLength
1464      - [X] AtomAdapter
1465      - [X] Atom
1466      - [X] AtomListAdapter
1467      - [X] AtomContainer
1468      - [X] AtomWrapper
1469**** DONE __m4a__.py
1470***** DONE adjust syntax
1471***** DONE add docstrings
1472      - [X] M4AAudio_faac
1473      - [X] M4AAudio_faac.channel_mask
1474      - [X] M4AAudio_faac.is_type
1475      - [X] M4AAudio_faac.lossless
1476      - [X] M4AAudio_faac.channels
1477      - [X] M4AAudio_faac.bits_per_sample
1478      - [X] M4AAudio_faac.sample_rate
1479      - [X] M4AAudio_faac.cd_frames
1480      - [X] M4AAudio_faac.total_frames
1481      - [X] M4AAudio_faac.get_metadata
1482      - [X] M4AAudio_faac.set_metadata
1483      - [X] M4AAudio_faac.delete_metadata
1484      - [X] M4AAudio_faac.to_pcm
1485      - [X] M4AAudio_faac.from_pcm
1486      - [X] M4AAudio_faac.can_add_replay_gain
1487      - [X] M4AAudio_faac.lossless_replay_gain
1488      - [X] M4AAudio_faac.add_replay_gain
1489      - [X] M4AAudio_nero
1490      - [X] M4AAudio_nero.to_pcm
1491      - [X] M4AAudio_nero.from_pcm
1492      - [X] M4AAudio_nero.to_wave
1493      - [X] M4AAudio_nero.from_wave
1494      - [X] ILST_Atom
1495      - [X] M4AMetaData
1496      - [X] M4AMetaData.binary_atom
1497      - [X] M4AMetaData.text_atom
1498      - [X] M4AMetaData.trkn_atom
1499      - [X] M4AMetaData.disk_atom
1500      - [X] M4AMetaData.covr_atom
1501      - [X] M4AMetaData.images
1502      - [X] M4AMetaData.add_image
1503      - [X] M4AMetaData.delete_image
1504      - [X] M4AMetaData.converted
1505      - [X] M4AMetaData.merge
1506      - [X] M4AMetaData.to_atom
1507      - [X] M4AMetaData.supports_images
1508      - [X] M4ACovr
1509      - [X] M4ACovr.converted
1510      - [X] ALACAudio
1511      - [X] ALACAudio.is_type
1512      - [X] ALACAudio.total_frames
1513      - [X] ALACAudio.channel_mask
1514      - [X] ALACAudio.cd_frames
1515      - [X] ALACAudio.lossless
1516      - [X] ALACAudio.to_pcm
1517      - [X] ALACAudio.from_pcm
1518      - [X] ALACAudio.to_wave
1519      - [X] ALACAudio.from_wave
1520      - [X] ADTSException
1521      - [X] AACAudio
1522      - [X] AACAudio.is_type
1523      - [X] AACAudio.bits_per_sample
1524      - [X] AACAudio.channels
1525      - [X] AACAudio.lossless
1526      - [X] AACAudio.total_frames
1527      - [X] AACAudio.sample_rate
1528      - [X] AACAudio.to_pcm
1529      - [X] AACAudio.from_pcm
1530      - [X] AACAudio.aac_frames
1531      - [X] AACAudio.aac_frame_count
1532**** DONE __mp3__.py
1533***** DONE adjust syntax
1534***** DONE add docstrings
1535      - [X] MP3Exception
1536      - [X] MP3Audio
1537      - [X] MP3Audio.is_type
1538      - [X] MP3Audio.lossless
1539      - [X] MP3Audio.to_pcm
1540      - [X] MP3Audio.from_pcm
1541      - [X] MP3Audio.bits_per_sample
1542      - [X] MP3Audio.channels
1543      - [X] MP3Audio.sample_rate
1544      - [X] MP3Audio.get_metadata
1545      - [X] MP3Audio.set_metadata
1546      - [X] MP3Audio.delete_metadata
1547      - [X] MP3Audio.cd_frames
1548      - [X] MP3Audio.total_frames
1549      - [X] MP3Audio.can_add_replay_gain
1550      - [X] MP3Audio.lossless_replay_gain
1551      - [X] MP3Audio.add_replay_gain
1552      - [X] MP2Audio
1553      - [X] MP2Audio.is_type
1554      - [X] MP2Audio.from_pcm
1555**** DONE __musepack__.py
1556***** DONE adjust syntax
1557***** DONE add docstrings
1558      - [X] NutValue
1559      - [X] Musepack8StreamReader
1560      - [X] Musepack8StreamReader.packets
1561      - [X] MusepackAudio
1562      - [X] MusepackAudio.from_pcm
1563      - [X] MusepackAudio.is_type
1564      - [X] MusepackAudio.sample_rate
1565      - [X] MusepackAudio.total_frames
1566      - [X] MusepackAudio.channels
1567      - [X] MusepackAudio.bits_per_sample
1568      - [X] MusepackAudio.lossless
1569**** DONE __musicbrainz__.py
1570***** DONE adjust syntax
1571***** DONE add docstrings
1572      - [X] get_xml_nodes
1573      - [X] get_xml_text_node
1574      - [X] reorder_xml_children
1575      - [X] MBDiscID
1576      - [X] MBDiscID.from_cdda
1577      - [X] MBDiscID.offsets
1578      - [X] MusicBrainz
1579      - [X] MusicBrainz.connect
1580      - [X] MusicBrainz.close
1581      - [X] MusicBrainz.read_data
1582      - [X] MBXMLException
1583      - [X] MusicBrainzReleaseXML
1584      - [X] MusicBrainzReleaseXML.read
1585      - [X] MusicBrainzReleaseXML.read_data
1586      - [X] MusicBrainzReleaseXML.metadata
1587      - [X] MusicBrainzReleaseXML.from_files
1588      - [X] MusicBrainzReleaseXML.from_cuesheet
1589      - [X] MusicBrainzReleaseXML.build
1590      - [X] get_mbxml
1591**** DONE __shn__.py
1592***** DONE adjust syntax
1593***** DONE add docstrings
1594      - [X] ShortenAudio
1595      - [X] ShortenAudio.is_type
1596      - [X] ShortenAudio.bits_per_sample
1597      - [X] ShortenAudio.channels
1598      - [X] ShortenAudio.channel_mask
1599      - [X] ShortenAudio.lossless
1600      - [X] ShortenAudio.total_frames
1601      - [X] ShortenAudio.sample_rate
1602      - [X] ShortenAudio.to_pcm
1603      - [X] ShortenAudio.from_pcm
1604      - [X] ShortenAudio.to_wave
1605      - [X] ShortenAudio.from_wave
1606      - [X] ShortenAudio.supports_foreign_riff_chunks
1607      - [X] ShortenAudio.has_foreign_riff_chunks
1608**** DONE __speex__.py
1609***** DONE adjust syntax
1610***** DONE add docstrings
1611      - [X] UnframedVorbisComment
1612      - [X] SpeexAudio
1613      - [X] SpeexAudio.is_type
1614      - [X] SpeexAudio.to_pcm
1615      - [X] SpeexAudio.from_pcm
1616      - [X] SpeexAudio.set_metadata
1617      - [X] SpeexAudio.can_add_replay_gain
1618**** DONE __vorbiscomment__.py
1619***** DONE adjust syntax
1620***** DONE add docstrings
1621      - [X] VorbisComment
1622      - [X] VorbisComment.supports_images
1623      - [X] VorbisComment.images
1624      - [X] VorbisComment.converted
1625      - [X] VorbisComment.merge
1626      - [X] VorbisComment.build
1627**** DONE __vorbis__.py
1628***** DONE adjust syntax
1629***** DONE add docstrings
1630      - [X] OggStreamReader
1631      - [X] OggStreamReader.close
1632      - [X] OggStreamReader.packets
1633      - [X] OggStreamReader.pages
1634      - [X] OggStreamReader.pages_to_packets
1635      - [X] OggStreamReader.calculate_ogg_checksum
1636      - [X] OggStreamWriter
1637      - [X] OggStreamWriter.close
1638      - [X] OggStreamWriter.write_page
1639      - [X] OggStreamWriter.build_pages
1640      - [X] VorbisAudio
1641      - [X] VorbisAudio.is_type
1642      - [X] VorbisAudio.lossless
1643      - [X] VorbisAudio.bits_per_sample
1644      - [X] VorbisAudio.channels
1645      - [X] VorbisAudio.channel_mask
1646      - [X] VorbisAudio.total_frames
1647      - [X] VorbisAudio.sample_rate
1648      - [X] VorbisAudio.to_pcm
1649      - [X] VorbisAudio.from_pcm
1650      - [X] VorbisAudio.set_metadata
1651      - [X] VorbisAudio.get_metadata
1652      - [X] VorbisAudio.delete_metadata
1653      - [X] VorbisAudio.add_replay_gain
1654      - [X] VorbisAudio.can_add_replay_gain
1655      - [X] VorbisAudio.lossless_replay_gain
1656      - [X] VorbisAudio.replay_gain
1657      - [X] VorbisChannelMask
1658      - [X] VorbisChannelMask.channels
1659**** DONE __wavpack__.py
1660***** DONE adjust syntax
1661***** DONE add docstrings
1662      - [X] SymlinkPCMReader
1663      - [X] SymlinkPCMReader.read
1664      - [X] SymlinkPCMReader.close
1665      - [X] SymlinkPCMReader.new
1666      - [X] WavePackAPEv2
1667      - [X] WavePackAPEv2.converted
1668      - [X] WavPackAudio
1669      - [X] WavPackAudio.is_type
1670      - [X] WavPackAudio.lossless
1671      - [X] WavPackAudio.supports_foreign_riff_chunks
1672      - [X] WavPackAudio.channel_mask
1673      - [X] WavPackAudio.get_metadata
1674      - [X] WavPackAudio.has_foreign_riff_chunks
1675      - [X] WavPackAudio.frames
1676      - [X] WavPackAudio.sub_frames
1677      - [X] WavPackAudio.bits_per_sample
1678      - [X] WavPackAudio.channels
1679      - [X] WavPackAudio.total_frames
1680      - [X] WavPackAudio.sample_rate
1681      - [X] WavPackAudio.from_pcm
1682      - [X] WavPackAudio.to_wave
1683      - [X] WavPackAudio.to_pcm
1684      - [X] WavPackAudio.from_wave
1685      - [X] WavPackAudio.add_replay_gain
1686      - [X] WavPackAudio.can_add_replay_gain
1687      - [X] WavPackAudio.replay_gain
1688      - [X] WavPackAudio.get_cuesheet
1689      - [X] WavPackAudio.set_cuesheet
1690**** DONE __wav__.py
1691***** DONE adjust syntax
1692***** DONE add docstrings
1693      - [X] WaveReader
1694      - [X] WaveReader.read
1695      - [X] WaveReader.close
1696      - [X] TempWaveReader
1697      - [X] TempWaveReader.close
1698      - [X] WavException
1699      - [X] WaveAudio
1700      - [X] WaveAudio.is_type
1701      - [X] WaveAudio.lossless
1702      - [X] WaveAudio.supports_foreign_riff_chunks
1703      - [X] WaveAudio.has_foreign_riff_chunks
1704      - [X] WaveAudio.channel_mask
1705      - [X] WaveAudio.to_pcm
1706      - [X] WaveAudio.from_pcm
1707      - [X] WaveAudio.to_wave
1708      - [X] WaveAudio.from_wave
1709      - [X] WaveAudio.total_frames
1710      - [X] WaveAudio.sample_rate
1711      - [X] WaveAudio.channels
1712      - [X] WaveAudio.bits_per_sample
1713      - [X] WaveAudio.can_add_replay_gain
1714      - [X] WaveAudio.lossless_replay_gain
1715      - [X] WaveAudio.add_replay_gain
1716      - [X] WaveAudio.track_name
1717      - [X] WaveAudio.fmt_chunk_to_channel_mask
1718      - [X] WaveAudio.chunk_ids
1719      - [X] WaveAudio.chunks
1720      - [X] WaveAudio.wave_from_chunks
1721      - [X] WaveAudio.pcm_split
1722**** DONE cue.py
1723***** DONE adjust syntax
1724***** DONE add docstrings
1725      - [X] cue.py
1726      - [X] CueException
1727      - [X] tokens
1728      - [X] get_value
1729      - [X] parse
1730      - [X] Cuesheet
1731      - [X] Cuesheet.catalog
1732      - [X] Cuesheet.single_file_type
1733      - [X] Cuesheet.indexes
1734      - [X] Cuesheet.pcm_lengths
1735      - [X] Cuesheet.ISRCs
1736      - [X] Cuesheet.file
1737      - [X] Track
1738      - [X] Track.ISRC
1739      - [X] read_cuesheet
1740**** DONE toc.py
1741***** DONE adjust syntax
1742***** DONE add docstrings
1743      - [X] toc.py
1744      - [X] TOCException
1745      - [X] parse
1746      - [X] TOCFile
1747      - [X] TOCFile.catalog
1748      - [X] TOCFile.indexes
1749      - [X] TOCFile.pcm_lengths
1750      - [X] TOCFile.ISRCs
1751      - [X] TOCFile.file
1752      - [X] Track
1753      - [X] Track.ISRC
1754      - [X] read_tocfile
1755*** DONE Update utilities
1756    - [X] audiotools-config
1757    - [X] cd2track
1758    - [X] cd2xmcd
1759    - [X] cdinfo
1760    - [X] coverdump
1761    - [X] coverview
1762    - [X] editxmcd
1763    - [X] record2track
1764    - [X] track2cd
1765    - [X] track2track
1766    - [X] track2xmcd
1767    - [X] trackcat
1768    - [X] trackcmp
1769    - [X] trackinfo
1770    - [X] tracklength
1771    - [X] tracklint
1772    - [X] trackplay
1773    - [X] trackrename
1774    - [X] tracksplit
1775    - [X] tracktag
1776*** DONE Update tests
1777    - [X] test.py
1778    - [X] test_streams.py
1779** DONE Update C code to support PEP 7
1780   - [X] array.c
1781   - [X] array.h
1782   - [X] bitstream_r.c
1783   - [X] bitstream_r.h
1784   - [X] bitstream_w.c
1785   - [X] bitstream_w.h
1786   - [X] cdiomodule.c
1787   - [X] decoders.c
1788   - [X] decoders.h
1789   - [X] encoders.c
1790   - [X] encoders.h
1791   - [X] md5.c
1792   - [X] md5.h
1793   - [X] pcm.c
1794   - [X] pcm.h
1795   - [X] pcmreader.c
1796   - [X] pcmreader.h
1797   - [X] replaygain.c
1798   - [X] replaygain.h
1799   - [X] resample.c
1800   - [X] resample.h
1801   - [X] decoders/alaac.c
1802   - [X] decoders/alac.h
1803   - [X] decoders/flac.c
1804   - [X] decoders/flac_crc.c
1805   - [X] decoders/flac_crc.h
1806   - [X] decoders/flac.h
1807   - [X] decoders/shn.c
1808   - [X] decoders/shn.h
1809   - [X] encoders/alac.c
1810   - [X] encoders/alac.h
1811   - [X] encoders/alac_lpc.c
1812   - [X] encoders/alac_lpc.h
1813   - [X] encoders/flac.c
1814   - [X] encoders/flac.h
1815   - [X] encoders/flac_lpc.c
1816   - [X] encoders/flac_lpc.h
1817   - [X] encoders/shn.c
1818   - [X] encoders/shn.h
1819** DONE Add ALAC support
1820*** DONE Build ALAC decoder
1821**** DONE Add audiotools.decoders.AlacDecoder object
1822**** DONE Add frame header parsing
1823**** DONE Add subframe header parsing
1824**** DONE Add wasted-bits block parsing
1825**** DONE Add residual decoding
1826**** DONE Ensure frame footer is checked
1827**** DONE Decode subframes
1828**** DONE Perform channel decorrelation
1829     - [X] leftweight zero
1830     - [X] leftweight nonzero
1831**** DONE Handle wasted-bits samples
1832**** DONE Return pcm.FrameList objects on calls to read()
1833**** DONE Ensure PCMReader-compatible fields are present
1834     - [X] sample_rate
1835     - [X] channels
1836     - [X] channel_mask
1837     - [X] bits_per_sample
1838**** DONE Ensure 16/24 bps streams work correctly
1839     - [X] 16bps
1840     - [X] 24bps
1841**** DONE Ensure 1/2 channel streams work correctly
1842     - [X] 1 channel
1843     - [X] 2 channels
1844**** DONE Ensure different sample rates work correctly
1845     - [X] 44100Hz
1846     - [X] 48000Hz
1847     - [X] 96000Hz
1848     - [X] 192000Hz
1849     (need to figure out what other sample rates ALAC supports)
1850**** DONE Update source to more closely match documentation
1851**** DONE Optimize for speed
1852*** DONE Build ALAC encoder
1853**** DONE Add audiotools.encoders.encode_alac function
1854**** DONE Ensure verbatim ALAC file is written correctly
1855**** DONE Determine ALAC stream tunables
1856***** wasted bits for 24bps streams
1857***** interlacing shift
1858***** interlacing leftweight
1859***** prediction quantitization
1860***** coefficients
1861**** DONE extract wasted bits for 24bps streams
1862**** DONE correlate stereo samples
1863**** DONE determine coefficients/quantitization
1864**** DONE calculate residual values
1865**** DONE write residuals based on initial history/history multiplier/etc.
1866**** DONE handle random noise with uncompressed frames
1867**** DONE handle atypical sample rates properly
1868**** DONE Update source to more closely match documentation
1869**** DONE make interlacing shift and interlacing leftweight range options
1870**** DONE optimize for speed
1871*** DONE Add ALACAudio type to audiotools Python core
1872*** DONE Enable TestAlacAudio core tests
1873*** DONE Add ALAC-specific unit tests
1874**** DONE test_streams
1875**** DONE test_small_files
1876**** DONE test_full_scale_deflection
1877**** DONE test_sines
1878     eliminate DeprecationWarning at construct/core.py:541
1879     which seems to be caused by 96000Hz input
1880**** DONE test_wasted_bps
1881**** DONE test_blocksizes
1882**** DONE test_frame_header_variations
1883**** DONE test_noise
1884**** DONE test_fractional
1885*** DONE Document ALAC
1886**** DONE the ALAC file stream
1887**** DONE ALAC decoding
1888***** DONE Frame header
1889***** DONE Subframe header
1890***** DONE Residual decoding
1891***** DONE Residual decoding example
1892***** DONE Subframe decoding
1893      Simplify this to make it easier to understand
1894***** DONE Subframe decoding example
1895***** DONE Channel decorrelation
1896***** DONE Channel decorrelation example
1897***** DONE Wasted bits
1898***** DONE Wasted bits example
1899**** DONE ALAC encoding
1900***** DONE Figure out compliant atom contents
1901      - [X] ftyp
1902      - [X] moov->mvhd
1903      - [X] moov->trak->tkhd
1904      - [X] moov->trak->mdia->mdhd
1905      - [X] moov->trak->mdia->hdlr
1906      - [X] moov->trak->mdia->minf->smhd
1907      - [X] moov->trak->mdia->dinf->dref
1908      - [X] moov->trak->mdia->stbl->stsd->alac
1909      - [X] moov->trak->mdia->stbl->stts
1910      - [X] moov->trak->mdia->stbl->stsc
1911      - [X] moov->trak->mdia->stbl->stsz
1912      - [X] moov->trak->mdia->stbl->stco
1913      - [X] moov->udta->meta
1914      - [X] free
1915***** DONE Add forward references in "alac" atom description
1916***** DONE figure out if "meta" has required "----" sub-atoms
1917***** DONE extracting wasted bits for 24bps streams
1918***** DONE correlating stereo samples
1919***** DONE determining coefficients/quantitization
1920****** DONE Windowing
1921****** DONE Computing autocorrelation
1922****** DONE LP coefficient calculation
1923****** DONE Best order estimation
1924****** DONE Quantizing coefficients
1925***** DONE calculating residual values
1926      Simplify this to make it easier to understand
1927***** DONE writing residuals based on initial history/history multiplier/etc.
1928***** DONE Fix bitstream figs to be monospace font for binary digits
1929**** DONE verify A4 layout is correct
1930*** DONE Update M4A metadata routines to exploit "free" atoms
1931    As with FLAC, rewriting the entire file should be avoided
1932*** DONE Fix InvalidImage exceptions when reading test ALACs
1933*** DONE Ensure UnsupportedChannels exception is handled by user-level tools
1934*** DONE Ensure new ALACs work in iTunes
1935*** DONE Ensure new ALACs work on iPods
1936*** DONE Have M4A files group properly on iTunes/iPods
1937** DONE Remove xdelta requirement
1938   It's doesn't compile well on some platforms and is only used by tracklint
1939*** DONE Build trivial binary delta routine
1940    Since most metadata formats make use of padding,
1941    we can use a simple XOR over their contents to generate
1942    a bidirectional patch that's optimized for tracklint's behavior.
1943    Since the bulk of such a patch should be NULLs,
1944    we can compress it with zlib/bz2 and achieve
1945    excellent compression.
1946** DONE Tweak documentation
1947*** DONE Update indexes to account for warm-up samples
1948    The i index on the left and right hand sides must match.
1949    - [X] FLAC FIXED decoding
1950    - [X] FLAC LPC decoding
1951    - [X] FLAC FIXED encoding
1952    - [X] FLAC LPC encoding
1953*** DONE Rewrite the FLAC channel assignment section
1954    Reference the side channel extra bit and add examples.
1955*** DONE Add FLAC channel assignment encoding documentation
1956    Show its channel calculations and include an example.
1957** DONE indicate ReplayGain capabilities/binaries
1958   audiotools-config(1) should show what ReplayGain binaries are present
1959   and all audio classes that support it
1960** DONE update trackcmp to share trackverify's output interface
1961** DONE Add unary jump tables with a max value
1962   Since both Apple Lossless *and* WavPack have unary reading
1963   with a maximum upper limit of read bits, it makes sense
1964   to build a proper jump table for it.
1965   The size of our state limits the maximum to 8 bits,
1966   so larger maximums will be supported at a read_limited_unary() level.
1967
1968   The trouble is, we need to differentiate between normal exits
1969   (we've hit the stop bit) and exits that hit the maximum value.
1970   So, the jump table itself must have a different syntax
1971   (probably an extra bit for "maximum value reached")
1972   and read_limited_unary() will likely return -1 in that event.
1973** DONE Add little-endian bitstream readers
1974*** DONE integrate read functions into Bitstream struct
1975*** DONE add bitstream.py jump tables for little-endian reading
1976    - [X] read_bits_table_le.h
1977    - [X] read_unary_table_le.h
1978    - [X] read_limited_unary_table_le.h
1979    - [X] unread_bit_table_le.h
1980*** DONE add little-endian Bitstream functions
1981    - [X] bs_read_bits_le
1982    - [X] bs_read_bits64_le
1983    - [X] bs_unread_bit_le
1984    - [X] bs_read_unary_le
1985    - [X] bs_read_limited_unary_le
1986*** DONE have bs_open() attach the proper endian functions
1987*** DONE update Python BitstreamReader for little-endian operation
1988    This should function similar to the C one,
1989    with alignment specified at init-time.
1990    Or, perhaps replace Python reader with C-based one altogether.
1991*** DONE add some basic Bitstream reader unit testing
1992*** DONE allow endianness swapping
1993** DONE Add little-endian bitstream writers
1994*** DONE add bitstream.py jump tables for little-endian writing
1995    - [X] write_bits_table_le.h
1996    - [X] write_unary_table_le.h
1997*** DONE Add little-endian Bitstream functions
1998    - [X] write_bits_actual_le
1999    - [X] write_signed_bits_actual_le
2000    - [X] write_bits64_actual_le
2001    - [X] write_unary_actual_le
2002    - [X] byte_align_w_actual_le
2003*** DONE have bs_open() attach the proper endian functions
2004*** DONE update Python BitstreamWriter for little-endian operation
2005    Convert to a C type, similar to BitstreamReader
2006*** DONE add some basic Bitstream writer unit testing
2007*** DONE allow endianness swapping
2008** DONE integrate new endianness routines into existing routines
2009*** DONE Convert Ogg verifier to proper little-endian operation
2010*** DONE Swap endianness for proper FLAC VORBISCOMMENT writing
2011*** DONE have bs_set_endianness create instruction in recorder-mode
2012    So that if we're recording an endianness shift,
2013    it gets set properly when "played back" to an actual writer.
2014    In fact, it may be a good idea to attach the set_endianness()
2015    function to the bitstream writer itself.
2016** DONE Build proper AlbumMetaDataFile class
2017   This is a superclass of FreeDB's XMCD and MusicBrainz's XML
2018   which wraps metadata containers into a consistent interface
2019   for use by editxmcd and command-line utilities.
2020*** DONE Convert XMCD to AlbumMetaDataFile subclass
2021*** DONE Add unit tests for XMCD
2022*** DONE Convert MusicBrainzReleaseXML to AlbumMetaDataFile subclass
2023*** DONE Add unit tests for MusicBrainzReleaseXML
2024*** DONE Update utilities to use new interface
2025    - [X] tracksplit
2026    - [X] track2track
2027    - [X] track2xmcd
2028    - [X] trackrename
2029    - [X] tracktag
2030    - [ ] editxmcd
2031    - [X] cd2track
2032    - [X] cd2xmcd
2033*** DONE Update old unit tests to new interface
2034*** DONE Fix get_track to return blank artist name if not present
2035    Don't pull from the class artist name;
2036    have track_metadata() figure that out as appropriate instead.
2037**** DONE Fix unit tests for proper behavior
2038*** DONE Document AlbumMetaDataFile
2039** DONE Update ALAC to handle multichannel audio
2040   What David Hammerton's reverse-engineered decoder described as
2041   a 3-bit frame footer isn't; it's actually a "stop" delimiter
2042   analagous to WavPack's block header stop bit.
2043   If it's not 0x7, keep reading frames and combine them
2044   channel-wise into a single multichannel chunk of output.
2045
2046   iTunes and iPods still won't be able to handle such files,
2047   but XLD should be able to.
2048*** DONE Update ALACDecoder's analyze_frame() method for multichannel
2049*** DONE Update ALACDecoder's read() method for multichannel
2050*** DONE Update encode_alac function for multichannel
2051*** DONE Unit test multichannel ALAC encoding and decoding
2052*** DONE Update decoding documentation describing multichannel handling
2053*** DONE Update encoding documentation describing multichannel handling
2054** DONE Add higher sampling rate support to ReplayGain module
2055   Extract the higher rates from wvgain.c
2056   - [X] 8000Hz
2057   - [X] 11025Hz
2058   - [X] 12000Hz
2059   - [X] 16000Hz
2060   - [X] 18900Hz
2061   - [X] 22050Hz
2062   - [X] 24000Hz
2063   - [X] 32000Hz
2064   - [X] 37800Hz
2065   - [X] 44100Hz
2066   - [X] 48000Hz
2067   - [X] 56000Hz
2068   - [X] 64000Hz
2069   - [X] 88200Hz
2070   - [X] 96000Hz
2071   - [X] 112000Hz
2072   - [X] 128000Hz
2073   - [X] 144000Hz
2074   - [X] 176400Hz
2075   - [X] 192000Hz
2076*** DONE Unit test sample rates
2077** DONE Allow audio type defaults to be selectable
2078*** DONE Update tools to pull -t from config file
2079    - [X] cd2track
2080    - [X] record2track
2081    - [X] track2track
2082    - [X] trackcat
2083    - [X] tracksplit
2084*** DONE Update tools to pull -q from config file
2085    - [X] cd2track
2086    - [X] record2track
2087    - [X] track2track
2088    - [X] trackcat
2089    - [X] tracksplit
2090*** DONE Update audio formats to pull default quality from config file (if any)
2091    for all calls to from_pcm() and from_wave()
2092    - [X] aac
2093    - [X] flac
2094    - [X] m4a (Nero)
2095    - [X] m4a (faac)
2096    - [X] mp2
2097    - [X] mp3
2098    - [X] oga
2099    - [X] ogg
2100    - [X] spx
2101    - [X] wv
2102*** DONE Update audiotools-config to display default type
2103*** DONE Update audiotools-config to display default qualities
2104*** DONE Update audiotools-config to select default type
2105*** DONE Update audiotools-config to select default quality for a type
2106*** DONE Document new configuration options in man pages
2107    - [X] audiotools-config
2108    - [X] cd2track
2109    - [X] record2track
2110    - [X] track2track
2111    - [X] trackcat
2112    - [X] tracksplit
2113    - [X] audiotools.cfg
2114** DONE Allow default verbosity to be selectable
2115*** DONE Update audiotools-config to display default verbosity
2116*** DONE Update audiotools-config to select default verbosity
2117*** DONE Update tools to use default verbosity
2118    - [X] cd2track
2119    - [X] cd2xmcd
2120    - [X] coverdump
2121    - [X] record2track
2122    - [X] track2cd
2123    - [X] track2track
2124    - [X] track2xmcd
2125    - [X] trackcmp
2126    - [X] tracklint
2127    - [X] trackrename
2128    - [X] tracksplit
2129    - [X] tracktag
2130    - [X] trackverify
2131*** DONE Document new configuration option in man pages
2132    - [X] audiotools-config
2133    - [X] audiotools.cfg
2134** DONE Shift common decoder/encoder routines to a common/ directory
2135   - [X] flac_crc.h
2136   - [X] flac_crc.c
2137   - [X] misc.h
2138   - [X] misc.c
2139   - [X] md5.h
2140   - [X] md5.c
2141** DONE Get coverview and editxmcd working on Mac OS X
2142*** DONE editxmcd
2143**** DONE Update man page
2144*** DONE Update coverview for dual PyGTK/Tkinter operation
2145    It's a simple enough app that it should be able to conditionally do both,
2146    especially since audiotools does most of the heavy lifting.
2147    This allows it to look like a proper app under X11
2148    and work at all everywhere else.
2149
2150    It should look and function approximately the same on both.
2151**** DONE Remove glade requirement
2152     I'm sick of glade, and coverview should be small enough
2153     that it's not a problem to lay it out internally.
2154**** DONE Update coverview for PyGTK
2155**** DONE Update coverview for Tkinter
2156***** DONE Fixup error messages, if possible
2157**** DONE Add --gtk/--tkinter switches for conditional launch
2158     For testing purposes
2159**** DONE Cleanup conditional classes/helper functions
2160**** DONE Check for init-time errors
2161     Both in loading audiofiles and in import problems
2162     such as Mac OS's 32-bit problem
2163**** DONE Update man page
2164** DONE More graceful handling of broken files
2165   A lot of the track handlers assume that once the start of the file is good,
2166   the rest of it is following the spec.  This is not always the case.
2167*** DONE All audio formats need to implement the error specification
2168    This means that:
2169    classmethod.is_type() must never error
2170    __init__() must raise InvalidFile if the filename's contents are invalid
2171    to_pcm() must return PCMReaderError if the decoder can't be built
2172    classmethod.from_pcm() must raise EncodingError if it can't encode file
2173    to_wave() must raise EncodingError if a wave can't be written
2174    classmethod.from_wave() must raise EncodingError if it can't encode file
2175    a failed to_wave() mustn't leave half-encoded .wav files behind
2176    a failed from_wave() mustn't leave half-encoded .wav files behind
2177    a failed from_pcm() mustn't leave a partially encoded file behind
2178    PCMReaders may raise IOError and ValueError on read()
2179    PCMReaders may raise DecodingError on close()
2180**** DONE AACAudio
2181     - [X] is_type() doesn't error
2182     - [X] __init__() raises InvalidFile
2183     - [X] to_pcm() returns PCMReaderError on decoder error
2184     - [X] from_pcm() raises EncodingError on encoder error
2185     - [X] from_wave() raises EncodingError on encoder error
2186     - [X] failed from_wave() deletes partial file
2187     - [X] failed from_pcm() deletes partial file
2188     - [X] PCMReader raises IOError/ValueError on read() if necessary
2189     - [X] PCMReader raises DecodingError on close() if necessary
2190**** DONE AiffAudio
2191     - [X] is_type() doesn't error
2192     - [X] __init__() raises InvalidFile
2193     - [X] to_pcm() returns PCMReaderError on decoder error
2194     - [X] from_pcm() raises EncodingError on encoder error
2195     - [X] to_wave() raises EncodingError on decoder error
2196     - [X] from_wave() raises EncodingError on encoder error
2197     - [X] failed to_wave() deletes partial file
2198     - [X] failed from_wave() deletes partial file
2199     - [X] failed from_pcm() deletes partial file
2200     - [X] PCMReader raises IOError/ValueError on read() if necessary
2201     - [X] PCMReader raises DecodingError on close() if necessary
2202**** DONE ALACAudio
2203     - [X] is_type() doesn't error
2204     - [X] __init__() raises InvalidFile
2205     - [X] to_pcm() returns PCMReaderError on decoder error
2206     - [X] from_pcm() raises EncodingError on encoder error
2207     - [X] to_wave() raises EncodingError on decoder error
2208     - [X] from_wave() raises EncodingError on encoder error
2209     - [X] failed to_wave() deletes partial file
2210     - [X] failed from_wave() deletes partial file
2211     - [X] failed from_pcm() deletes partial file
2212     - [X] PCMReader raises IOError/ValueError on read() if necessary
2213     - [X] PCMReader raises DecodingError on close() if necessary
2214**** DONE AuAudio
2215     - [X] is_type() doesn't error
2216     - [X] __init__() raises InvalidFile
2217     - [X] to_pcm() returns PCMReaderError on decoder error
2218     - [X] from_pcm() raises EncodingError on encoder error
2219     - [X] to_wave() raises EncodingError on decoder error
2220     - [X] from_wave() raises EncodingError on encoder error
2221     - [X] failed to_wave() deletes partial file
2222     - [X] failed from_wave() deletes partial file
2223     - [X] failed from_pcm() deletes partial file
2224     - [X] PCMReader raises IOError/ValueError on read() if necessary
2225     - [X] PCMReader raises DecodingError on close() if necessary
2226**** DONE FlacAudio
2227     - [X] is_type() doesn't error
2228     - [X] __init__() raises InvalidFile
2229     - [X] to_pcm() returns PCMReaderError on decoder error
2230     - [X] from_pcm() raises EncodingError on encoder error
2231     - [X] to_wave() raises EncodingError on decoder error
2232     - [X] from_wave() raises EncodingError on encoder error
2233     - [X] failed to_wave() deletes partial file
2234     - [X] failed from_wave() deletes partial file
2235     - [X] failed from_pcm() deletes partial file
2236     - [X] PCMReader raises IOError/ValueError on read() if necessary
2237     - [X] PCMReader raises DecodingError on close() if necessary
2238**** DONE M4AAudio_faac
2239     - [X] is_type() doesn't error
2240     - [X] __init__() raises InvalidFile
2241     - [X] from_pcm() raises EncodingError on encoder error
2242     - [X] from_wave() raises EncodingError on encoder error
2243     - [X] failed from_wave() deletes partial file
2244     - [X] failed from_pcm() deletes partial file
2245**** DONE M4AAudio_nero
2246     - [X] is_type() doesn't error
2247     - [X] __init__() raises InvalidFile
2248     - [X] to_pcm() returns PCMReaderError on decoder error
2249     - [X] from_pcm() raises EncodingError on encoder error
2250     - [X] to_wave() raises EncodingError on decoder error
2251     - [X] from_wave() raises EncodingError on encoder error
2252     - [X] failed to_wave() deletes partial file
2253     - [X] failed from_wave() deletes partial file
2254     - [X] failed from_pcm() deletes partial file
2255     - [X] PCMReader raises IOError/ValueError on read() if necessary
2256     - [X] PCMReader raises DecodingError on close() if necessary
2257**** DONE MP2Audio
2258     - [X] is_type() doesn't error
2259     - [X] __init__() raises InvalidFile
2260     - [X] to_pcm() returns PCMReaderError on decoder error
2261     - [X] from_pcm() raises EncodingError on encoder error
2262     - [X] to_wave() raises EncodingError on decoder error
2263     - [X] from_wave() raises EncodingError on encoder error
2264     - [X] failed to_wave() deletes partial file
2265     - [X] failed from_wave() deletes partial file
2266     - [X] failed from_pcm() deletes partial file
2267     - [X] PCMReader raises IOError/ValueError on read() if necessary
2268     - [X] PCMReader raises DecodingError on close() if necessary
2269**** DONE MP3Audio
2270     - [X] is_type() doesn't error
2271     - [X] __init__() raises InvalidFile
2272     - [X] to_pcm() returns PCMReaderError on decoder error
2273     - [X] from_pcm() raises EncodingError on encoder error
2274     - [X] to_wave() raises EncodingError on decoder error
2275     - [X] from_wave() raises EncodingError on encoder error
2276     - [X] failed to_wave() deletes partial file
2277     - [X] failed from_wave() deletes partial file
2278     - [X] failed from_pcm() deletes partial file
2279     - [X] PCMReader raises IOError/ValueError on read() if necessary
2280     - [X] PCMReader raises DecodingError on close() if necessary
2281**** DONE OggFlacAudio
2282     - [X] is_type() doesn't error
2283     - [X] __init__() raises InvalidFile
2284     - [X] to_pcm() returns PCMReaderError on decoder error
2285     - [X] from_pcm() raises EncodingError on encoder error
2286     - [X] to_wave() raises EncodingError on decoder error
2287     - [X] from_wave() raises EncodingError on encoder error
2288     - [X] failed to_wave() deletes partial file
2289     - [X] failed from_wave() deletes partial file
2290     - [X] failed from_pcm() deletes partial file
2291     - [X] PCMReader raises IOError/ValueError on read() if necessary
2292     - [X] PCMReader raises DecodingError on close() if necessary
2293**** DONE ShortenAudio
2294     - [X] is_type() doesn't error
2295     - [X] __init__() raises InvalidFile
2296     - [X] to_pcm() returns PCMReaderError on decoder error
2297     - [X] from_pcm() raises EncodingError on encoder error
2298     - [X] to_wave() raises EncodingError on decoder error
2299     - [X] from_wave() raises EncodingError on encoder error
2300     - [X] failed to_wave() deletes partial file
2301     - [X] failed from_wave() deletes partial file
2302     - [X] failed from_pcm() deletes partial file
2303     - [X] PCMReader raises IOError/ValueError on read() if necessary
2304     - [X] PCMReader raises DecodingError on close() if necessary
2305**** DONE SpeexAudio
2306     - [X] is_type() doesn't error
2307     - [X] __init__() raises InvalidFile
2308     - [X] from_pcm() raises EncodingError on encoder error
2309     - [X] failed from_wave() deletes partial file
2310     - [X] failed from_pcm() deletes partial file
2311**** DONE VorbisAudio
2312     - [X] is_type() doesn't error
2313     - [X] __init__() raises InvalidFile
2314     - [X] to_pcm() returns PCMReaderError on decoder error
2315     - [X] from_pcm() raises EncodingError on encoder error
2316     - [X] to_wave() raises EncodingError on decoder error
2317     - [X] from_wave() raises EncodingError on encoder error
2318     - [X] failed to_wave() deletes partial file
2319     - [X] failed from_wave() deletes partial file
2320     - [X] failed from_pcm() deletes partial file
2321     - [X] PCMReader raises IOError/ValueError on read() if necessary
2322     - [X] PCMReader raises DecodingError on close() if necessary
2323**** DONE WaveAudio
2324     - [X] is_type() doesn't error
2325     - [X] __init__() raises InvalidFile
2326     - [X] to_pcm() returns PCMReaderError on decoder error
2327     - [X] from_pcm() raises EncodingError on encoder error
2328     - [X] to_wave() raises EncodingError on decoder error
2329     - [X] from_wave() raises EncodingError on encoder error
2330     - [X] failed to_wave() deletes partial file
2331     - [X] failed from_wave() deletes partial file
2332     - [X] failed from_pcm() deletes partial file
2333     - [X] PCMReader raises IOError/ValueError on read() if necessary
2334     - [X] PCMReader raises DecodingError on close() if necessary
2335**** DONE WavPackAudio
2336     - [X] is_type() doesn't error
2337     - [X] __init__() raises InvalidFile
2338     - [X] to_pcm() returns PCMReaderError on decoder error
2339     - [X] from_pcm() raises EncodingError on encoder error
2340     - [X] to_wave() raises EncodingError on decoder error
2341     - [X] from_wave() raises EncodingError on encoder error
2342     - [X] failed to_wave() deletes partial file
2343     - [X] failed from_wave() deletes partial file
2344     - [X] failed from_pcm() deletes partial file
2345     - [X] PCMReader raises IOError/ValueError on read() if necessary
2346     - [X] PCMReader raises DecodingError on close() if necessary
2347*** DONE Check for invalid files at the tool level
2348    If an invalid file is encountered, display a proper
2349    user-readable error message explaining what's wrong
2350    with the file.
2351**** DONE track2cd
2352**** DONE track2track
2353**** DONE trackcat
2354**** DONE trackcmp
2355**** DONE trackplay
2356**** DONE tracksplit
2357*** DONE Document ValueError/IOError behavior
2358    Both in docstrings and in the rst documentation.
2359** DONE Add a convert() method to AudioFile subclasses
2360   Something like:
2361
2362   audiofile.convert(target_path,
2363                     target_class,
2364                     quality=None)
2365
2366   so one could perform a call like:
2367
2368   audiotools.open("infile.flac").convert("outfile.mp3", audiotools.MP3Audio)
2369
2370   Which would perform the proper to_pcm()/from_pcm()/to_wave()/from_wave()
2371   calls as necessary and could be overloaded to handle specific
2372   conversion processes, like AIFF->Shorten with a different set of
2373   IFF chunks.
2374*** DONE Deprecate wave-specific methods from base AudioFile class
2375    These methods might still be present, but only on classes that
2376    have specific need of them.
2377    - [X] to_wave()
2378    - [X] from_wave()
2379    - [X] supports_foreign_riff_chunks()
2380    - [X] has_foreign_riff_chunks()
2381*** DONE Convert classes to proper WaveContainer/AiffContainer subclasses
2382    - [X] FlacAudio
2383    - [X] OggFlacAudio (should *not* be a WaveContainer/AiffContainer subclass)
2384    - [X] WaveAudio
2385    - [X] AiffAudio
2386    - [X] WavPackAudio
2387    - [X] ShortenAudio
2388*** DONE Update old tests to reflect removed methods
2389*** DONE Update audio formats with convert() short-circuiting as needed
2390*** DONE Add unit tests for convert() methods
2391**** DONE Add unit tests which convert() every type to every other type
2392**** DONE Add unit tests to ensure foreign chunks are converted
2393*** DONE Update track2track to exploit convert() method
2394*** DONE Update documentation
2395**** DONE Add convert() documentation
2396**** DONE Remove wave-specific documentation
2397** DONE Update trackverify for multiprocess support
2398   I'll need some interprocess communication (probably pipes and select)
2399   to return the results from child processes for totals calculation.
2400*** DONE Update man page
2401*** DONE Document ExecQueue2
2402** DONE Update trackcmp for multiprocess support
2403*** DONE Update man page
2404*** DONE Update unit tests to account for -j flag
2405** DONE Update encoders for thread nonblocking
2406*** DONE encode_alac
2407*** DONE encode_flac
2408*** DONE encode_shn
2409*** DONE encode_wavpack
2410** DONE get trackplay working on Mac OS X
2411   There must be some way to pipe PCM data to its audio system
2412   without the need for additional libraries.
2413** DONE Remove -v option to mv(1) for file renaming
2414   This can be emulated in software.
2415** DONE Update audiotools.CDDA class
2416*** DONE Support reading offsets
2417    Without the horrible hack of reading the whole disc at once
2418*** DONE Document audiotools.CDDA class
2419*** DONE Document PCMReaderWindow class
2420** DONE Add support for System->cdrom_offset
2421   This will automatically apply offset samples when reading CDs
2422   so that rips will have the appropate amount of null samples.
2423*** DONE Add offset support when ripping
2424    Automatically apply the configfile's cdrom_offset value to tracks
2425    during reading.
2426*** DONE Add offset support when burning?
2427    cd2track and track2cd should round-trip properly
2428    If cd2track applies a sample offset when reading, does track2cd
2429    need to apply that same offset when writing?
2430    One would presume a drive's read offset and write offset are the same,
2431    but that may not be correct.
2432** DONE Add support for more lame encoding options
2433   Although the numerical presets are recommended,
2434   one should also be able to use the --preset values
2435   - [X] medium
2436   - [X] standard
2437   - [X] extreme
2438   - [X] insane
2439** DONE Remove wavegain for applying ReplayGain to .wav files
2440   This should be done internally instead.
2441** DONE Add more verbosity to --quality settings
2442   For formats with varying quality, the "-q help" option
2443   should indicate what those settings represent.
2444   - [X] FlacAudio
2445   - [X] M4AAudio_Nero
2446   - [X] MP2Audio
2447   - [X] MP3Audio
2448   - [X] OggFlacAudio
2449   - [X] SpeexAudio
2450   - [X] VorbisAudio
2451   - [X] WavPackAudio
2452*** DONE Update tools to indicate quality settings
2453    - [X] audiotools-config
2454    - [X] cd2track
2455    - [X] dvda2track
2456    - [X] record2track
2457    - [X] track2track
2458    - [X] trackcat
2459    - [X] tracksplit
2460** DONE Have coverdump build leading directories as needed
2461** DONE Add -T / --thumbnail option to tracktag
2462*** DONE Add option to tracktag man page
2463** DONE Convert ReplayGainReader to C
2464   The Python implementation simply uses too many CPU cycles,
2465   which can cause trackplay to stutter on hi-def files.
2466*** DONE Add audiotools.replaygain.ReplayGainReader object
2467**** DONE Add sample_rate attribute
2468**** DONE Add channels attribute
2469**** DONE Add channel_mask attribute
2470**** DONE Add bits_per_sample attribute
2471**** DONE Add read() method
2472**** DONE Add close() method
2473*** DONE Convert references to audiotools.ReplayGainReader
2474*** DONE Update documentation
2475** DONE Have the *2xmcd utilities delete .xmcd files if cancelled
2476   - [X] cd2xmcd
2477   - [X] dvda2xmcd
2478   - [X] track2xmcd
2479*** DONE Fix the *2xmcd utilities to use the proper mode on output files
2480** DONE Add software-based routines for bitstream reading/writing
2481   These should be optional, at least, for the bitstream writer especially.
2482   But if fast enough, they could replace the jump tables entirely.
2483*** DONE bitstream_w.h
2484    - [X] write_bits_actual_be
2485    - [X] write_bits_actual_le
2486    - [X] write_unary_actual_be
2487    - [X] write_unary_actual_le
2488** DONE Add individual tag item removal option to tracktag
2489   - [X] --remove-name
2490   - [X] --remove-artist
2491   - [X] --remove-performer
2492   - [X] --remove-composer
2493   - [X] --remove-conductor
2494   - [X] --remove-album
2495   - [X] --remove-catalog
2496   - [X] --remove-number
2497   - [X] --remove-track-total
2498   - [X] --remove-album-number
2499   - [X] --remove-album-total
2500   - [X] --remove-ISRC
2501   - [X] --remove-media-type
2502   - [X] --remove-year
2503   - [X] --remove-date
2504   - [X] --remove-copyright
2505   - [X] --remove-comment
2506*** DONE Unit test tag item addition/removal
2507*** DONE Update man page with new options
2508** DONE Add progess indicator to various utilities
2509   This will likely require an ExecQueue update which can
2510   receive progress output from multiple subprocesses
2511   so that the total progress can be generated.
2512   - [X] track2track
2513   - [X] cd2track
2514   - [X] dvda2track
2515   - [X] tracksplit
2516   - [X] trackcat
2517   - [X] trackcmp
2518   - [X] trackverify
2519   - [X] tracktag (for ReplayGain)
2520*** DONE Update AudioFile.convert() with a progress callback option
2521*** DONE Update AudioFile.add_replay_gain() with a progress callback option
2522    - [X] AudioFile
2523    - [X] FlacAudio
2524    - [X] M4AAudio
2525    - [X] MP3Audio
2526    - [X] VorbisAudio
2527    - [X] WavPackAudio
2528    - [X] WaveAudio
2529*** DONE Update programming documentation with progress callback option
2530*** DONE Update AudioFile.verify() with a progress callback option
2531    - [X] AACAudio
2532    - [X] ALACAudio
2533    - [X] AiffAudio
2534    - [X] AuAudio
2535    - [X] FlacAudio
2536    - [X] M4AAudio
2537    - [X] MP2Audio
2538    - [X] MP3Audio
2539    - [X] OggFlacAudio
2540    - [X] ShortenAudio
2541    - [X] SpeexAudio
2542    - [X] VorbisAudio
2543    - [X] WavPackAudio
2544    - [X] WaveAudio
2545*** DONE Add documentation for progress indicators and infrastructure
2546** DONE Add tool list and documentation to website
2547   I should probably find a way to either format the man pages
2548   to HTML directly (though groff's html output has been unfortunate
2549   in the past) or find an intermediate format that generates
2550   both man pages and web pages.
2551   - [X] audiotools-config
2552   - [X] audiotools.cfg
2553   - [X] cd2xmcd
2554   - [X] cdinfo
2555   - [X] cdplay
2556   - [X] cd2track
2557   - [X] coverdump
2558   - [X] coverview
2559   - [X] dvda2track
2560   - [X] dvda2xmcd
2561   - [X] dvdainfo
2562   - [X] editxmcd
2563   - [X] track2cd
2564   - [X] track2track
2565   - [X] track2xmcd
2566   - [X] trackcat
2567   - [X] trackcmp
2568   - [X] trackinfo
2569   - [X] tracklength
2570   - [X] tracklint
2571   - [X] trackplay
2572   - [X] trackrename
2573   - [X] tracksplit
2574   - [X] tracktag
2575   - [X] trackverify
2576*** DONE Build reStructuredText output
2577** DONE Add options for ID3v2/ID3v1 tagging
2578   Perhaps add config file options and corresponding audiotools-config options.
2579*** DONE Make ID3v2 version selectable
2580    Either ID3v2.2/ID3v2.3/ID3v2.4/none
2581*** DONE Make ID3v1 version selectable
2582    ID3v1.0/ID3v1.1/none
2583*** DONE Make ID3v2 track number formatting adjustable
2584    Allow leading 0s, since the spec doesn't forbid them.
2585*** DONE Display current ID3 info in audiotools-config
2586*** DONE Add ID3v2.2 option to audiotools-config
2587*** DONE Add ID3v1 option to audiotools-config
2588*** DONE Add ID3 number padding to audiotools-config
2589*** DONE Document new audiotools-config options in man page
2590*** DONE Document new audiotools config options on website
2591** DONE Add C-based FLAC encoder
2592*** DONE Use VERBATIM subframes when necessary
2593*** DONE Add significant initial padding blocks
2594    This will save a lot of time during retagging after FLAC creation
2595*** DONE Add a variety of unit tests
2596    - [X] test_stream.sh
2597    - [X] test_flac.sh
2598*** DONE Convert i_array size and data types to typedefs
2599*** DONE Convert f_array size and data types to typedefs
2600*** DONE Add more comprehensive encoding documentation
2601*** DONE Add a variety of assert() statements
2602    As with unit tests, these ensure everything is working during testing
2603    without a performance penalty at runtime.
2604*** DONE Handle foreign RIFF chunks
2605*** DONE Ensure FLACs work on a variety of other decoders
2606    Although decoding properly on the reference decoder *should* guarantee
2607    the file works everywhere, the only way to be certain is to test it.
2608*** DONE Ensure encoder raises the proper exceptions
2609*** DONE Support Rice2 partitions?
2610    The reference encoder uses these for more efficient handling of 24-bit audio
2611    but I'm not sure they're strictly necessary for my more basic encoder.
2612*** DONE Support wasted-bits-per-sample?
2613    I don't think I've ever seen these used on actual audio data
2614    that isn't artificial and hasn't been processed specifically for its use.
2615    As with Rice2, it's something that may get added later.
2616*** DONE Handle multi-channel PCM data correctly
2617    Anything higher than 2 channels needs to set a channel mask
2618    and the vorbis comment to the proper value.
2619    I expect this will be a long-term project
2620    coinciding with re-engineering the to_pcm()/from_pcm() methods.
2621*** DONE Remove external MD5 dependency
2622*** DONE Generate SEEKTABLE blocks
2623** DONE Add UTF-8 to FLAC documentation
2624*** DONE Explain how to decode a UTF-8 value
2625*** DONE Explain how to encode a UTF-8 value
2626** DONE Add C-based FLAC decoder
2627*** DONE Add a variety of unit tests
2628*** DONE Handle Rice escape codes
2629    Not sure how to test these, but they should be handled properly.
2630*** DONE Ensure decoder raises the proper exceptions
2631*** DONE Handle empty MD5 sums correctly
2632    If the MD5SUM is 00000000000000000000000000000000
2633    it's never a mismatch and should not trigger an error.
2634**** DONE Update tracklint to populate an empty MD5 sum
2635**** DONE Add unit tests
2636***** DONE Ensure an empty MD5 doesn't trigger an error at read time
2637***** DONE Ensure an empty MD5 doesn't trigger an error at verify time
2638***** DONE Ensure tracklint populates an empty MD5 correctly
2639** DONE Add substream support to bitstream reader
2640   Its function is to allow one to pull pieces out of a larger bitstream
2641   and process them separately.
2642   For example, while parsing an Ogg stream's pages with one bitstream,
2643   the extracted Vorbis packets could be processed with smaller substreams -
2644   including substreams that span one or more pages.
2645   It's also potentially helpful for MP3, Ogg FLAC, DVD-A and any other format
2646   with nontrivial wrappers that need to be parsed seperately
2647   from the main bitstream.
2648*** DONE Implement substream reader methods
2649**** DONE bs_read_bits_s_be
2650**** DONE bs_read_bits_s_le
2651**** DONE bs_read_signed_bits_s_be
2652**** DONE bs_read_signed_bits_s_le
2653**** DONE bs_read_bits64_s_be
2654**** DONE bs_read_bits64_s_le
2655**** DONE bs_skip_bits_s_be
2656**** DONE bs_skip_bits_s_le
2657**** DONE bs_read_unary_s_be
2658**** DONE bs_read_unary_s_le
2659**** DONE bs_read_limited_unary_s_be
2660**** DONE bs_read_limited_unary_s_le
2661**** DONE bs_set_endianness_s_be
2662**** DONE bs_set_endianness_s_le
2663**** DONE bs_read_huffman_code_s
2664**** DONE bs_mark_s
2665**** DONE bs_rewind_s
2666**** DONE bs_unmark_s
2667**** DONE bs_close_stream_s
2668**** DONE bs_substream_new
2669**** DONE bs_substream_f_be
2670**** DONE bs_substream_f_le
2671**** DONE bs_substream_p_be
2672**** DONE bs_substream_p_le
2673**** DONE bs_substream_s_be
2674**** DONE bs_substream_s_le
2675**** DONE bs_substream_append_f
2676**** DONE bs_substream_append_p
2677**** DONE bs_substream_append_s
2678*** DONE Add substream and substream_append to stream initializers
2679**** DONE bs_open
2680     - [X] substream
2681     - [X] substream_append
2682**** DONE bs_open_python
2683     - [X] substream
2684     - [X] substream_append
2685*** DONE Update current endianness setters with substream method
2686**** DONE bs_set_endianness_f_be
2687**** DONE bs_set_endianness_f_le
2688**** DONE bs_set_endianness_p_be
2689**** DONE bs_set_endianness_p_le
2690*** DONE Add garbage collection to substream
2691    It should be possible to create a substream, process it partway,
2692    then append more data to it without causing any overflow problems.
2693    That is, data at the beginning of the buffer will be recycled
2694    at append time if it hasn't been marked for rewinding back to.
2695*** DONE Add Python interface to substream
2696**** DONE audiotools.decoders.BitstreamReader.substream
2697**** DONE audiotools.decoders.BitstreamReader.substream_append
2698*** TODO Add unit tests to substream
2699** DONE Check objects for invalid init calls
2700   If an init fails, the subsequent dealloc call shouldn't segfault Python.
2701*** DONE audiotools.bitstream
2702    - [X] BitstreamAccumulator
2703    - [X] BitstreamReader
2704    - [X] BitstreamRecorder
2705    - [X] BitstreamWriter
2706*** DONE audiotools.cdio
2707    - [X] CDDA
2708    - [X] CDImage
2709*** DONE audiotools.decoders
2710    - [X] ALACDecoder
2711    - [X] AOBPCMDecoder
2712    - [X] FlacDecoder
2713    - [X] MLPDecoder
2714    - [X] OggFlacDecoder
2715    - [X] SHNDecoder
2716    - [X] Sine_Mono
2717    - [X] Sine_Simple
2718    - [X] Sine_Stereo
2719    - [X] WavPackDecoder
2720*** DONE audiotools.pcm
2721    - [X] FrameList
2722    - [X] FloatFrameList
2723*** DONE audiotools.resample
2724    - [X] Resampler
2725** DONE Update BitstreamWriter to work on Python objects
2726   That is, anything with a .write() and .close() method,
2727   similar to how BitstreamReader operates.
2728** DONE Calculate multi-album ReplayGain concurrently
2729   For instance, if one is calculating --replay-gain for four seperate
2730   albums and -j 4 is indicated, calculate each album's gain across
2731   its own core using the existing ProgressQueue facilities.
2732*** DONE track2track
2733*** DONE tracktag
2734**** DONE update man page with -j option
2735** DONE Adjust FlacMetaData to store blocks internally in order
2736   That is, they should output in the same order that they are stored on disk.
2737*** DONE Add size() method to FLAC blocks
2738    This returns the size of the block data, not including its 32-bit header
2739    - [X] Flac_STREAMINFO
2740    - [X] Flac_VORBISCOMMENT
2741    - [X] Flac_PICTURE
2742    - [X] Flac_APPLICATION
2743    - [X] Flac_SEEKTABLE
2744    - [X] Flac_CUESHEET
2745    - [X] Flac_PADDING
2746*** DONE FlacMetaData
2747    - [X] __init__
2748    - [X] __setattr__
2749    - [X] __getattr__
2750    - [X] __delattr__
2751    - [X] converted
2752    - [X] merge
2753    - [X] add_image
2754    - [X] delete_image
2755    - [X] images
2756    - [X] clean
2757    - [X] __repr__
2758    - [X] parse
2759    - [X] raw_info
2760    - [X] blocks
2761    - [X] build
2762*** DONE FlacAudio
2763    - [X] channel_mask
2764    - [X] get_metadata
2765    - [X] update_metadata
2766    - [X] set_metadata
2767    - [X] set_cuesheet
2768    - [X] get_cuesheet
2769    - [X] has_foreign_riff_chunks
2770    - [X] riff_wave_chunks
2771    - [X] from_wave
2772    - [X] has_foreign_aiff_chunks
2773    - [X] from_aiff
2774    - [X] aiff_chunks
2775    - [X] add_replay_gain
2776    - [X] replay_gain
2777    - [X] clean
2778*** DONE OggFlacMetaData
2779    - [X] converted
2780    - [X] __repr__
2781    - [X] parse
2782    - [X] build
2783*** DONE OggFlacAudio
2784    - [X] channel_mask
2785    - [X] update_metadata
2786    - [X] replay_gain
2787** DONE Have tracklint add channel mask info to hi-def FLAC files
2788   If omitted.  Fix cases in which the tag is missing
2789   from the VORBISCOMMENT block, or the VORBISCOMMENT block
2790   is absent altogether.
2791*** DONE Add unit test
2792*** DONE Add fix to man page
2793** DONE Update FLAC/Vorbis to support TOTALTRACKS field for track_total
2794   This seems to be catching on as a standard.
2795   The best solution may be to implement a set of "fallback" fields
2796   such that a single metadata field may use a number of
2797   different Vorbis comment names and the first match is used.
2798*** DONE Preserve TOTALTRACKS when updating track_total
2799*** DONE Add unit tests to ensure TOTALTRACKS works
2800** DONE Don't port ReplayGain tags with AudioFile.set_metadata()
2801   That is, if we perform:
2802
2803   >>> track1 = audiotools.open("file1")
2804   >>> track2 = audiotools.open("file2")
2805   >>> track1.set_metadata(track2.get_metadata())
2806   >>> track1.replay_gain() != track2.replay_gain()
2807   True
2808
2809   For all audio formats that store ReplayGain as embedded tags
2810*** DONE Update formats
2811    - [X] FlacAudio
2812    - [X] OggFlacAudio
2813    - [X] VorbisAudio
2814    - [X] WavPackAudio
2815*** DONE Add unit tests
2816    - [X] FlacAudio
2817    - [X] OggFlacAudio
2818    - [X] VorbisAudio
2819    - [X] WavPackAudio
2820** DONE Have MetaData.converted() always return a new object
2821   The following should hold for all metadata objects:
2822
2823   >>> a = MetaData(track_name=u"Foo")
2824   >>> b = MetaData.converted(a)
2825   >>> b.track_name = u"Bar"
2826   >>> a.track_name != b.track_name
2827   True
2828*** DONE Update MetaData classes
2829**** DONE ApeTag
2830**** DONE FlacMetaData
2831**** DONE ID3CommentPair
2832**** DONE ID3v1Comment
2833**** DONE ID3v22Comment
2834     - [X] ID3v22_Frame
2835     - [X] ID3v22_TXX_Frame
2836     - [X] ID3v22_COM_Frame
2837     - [X] ID3v22_PIC_Frame
2838**** DONE ID3v23Comment
2839     - [X] ID3v23_Frame
2840     - [X] ID3v23_TXXX_Frame
2841     - [X] ID3v23_COMM_Frame
2842     - [X] ID3v23_APIC_Frame
2843**** DONE ID3v24Comment
2844     - [X] ID3v24_Frame
2845     - [X] ID3v24_TXXX_Frame
2846     - [X] ID3v24_COMM_Frame
2847     - [X] ID3v24_APIC_Frame
2848**** DONE M4A_META_Atom
2849     - [X] M4A_Tree_Atom
2850     - [X] M4A_Leaf_Atom
2851     - [X] M4A_META_Atom
2852     - [X] M4A_FREE_Atom
2853     - [X] M4A_HDLR_Atom
2854     - [X] M4A_ILST_Leaf_Atom
2855     - [X] M4A_ILST_COVR_Data_Atom
2856     - [X] M4A_ILST_DISK_Data_Atom
2857     - [X] M4A_ILST_TRKN_Data_Atom
2858     - [ ] M4A_ILST_Unicode_Data_Atom
2859**** DONE MetaData
2860**** DONE OggFlacMetaData
2861**** DONE VorbisComment
2862*** DONE Update documentation for converted()'s behavior
2863*** DONE Add unit tests to all MetaData classes
2864    - [X] ApeTag
2865    - [X] FlacMetaData
2866    - [X] ID3CommentPair
2867    - [X] ID3v1Comment
2868    - [X] ID3v22Comment
2869    - [X] ID3v23Comment
2870    - [X] ID3v24Comment
2871    - [X] M4A_META_Atom
2872    - [X] MetaData
2873    - [X] OggFlacMetaData
2874    - [X] VorbisComment
2875** DONE Add better ID3v2 documentation
2876   Do a full explanation for frames one will find in nature.
2877   Don't be too concerned about obscure frames no one ever uses.
2878   | Frame                       | ID3v2.2 | ID3v2.3 | ID3v2.4 |
2879   |-----------------------------+---------+---------+---------|
2880   | all text                    | T__     | T___    | T___    |
2881   | all web                     | W__     | W___    | W___    |
2882   | picture                     | PIC     | APIC    | APIC    |
2883   | comment                     | COM     | COMM    | COMM    |
2884   | general encapsulated object | GEO     | GEOB    | GEOB    |
2885   | unsynchronized lyrics       | ULT     | USLT    | USLT    |
2886*** DONE ID3v2.2
2887    - [X] COM
2888    - [X] GEO
2889    - [X] PIC
2890    - [X] TAL
2891    - [X] TBP
2892    - [X] TCM
2893    - [X] TCO
2894    - [X] TCR
2895    - [X] TDA
2896    - [X] TDY
2897    - [X] TEN
2898    - [X] TFT
2899    - [X] TIM
2900    - [X] TKE
2901    - [X] TLA
2902    - [X] TLE
2903    - [X] TMT
2904    - [X] TOA
2905    - [X] TOF
2906    - [X] TOL
2907    - [X] TOR
2908    - [X] TOT
2909    - [X] TP1
2910    - [X] TP2
2911    - [X] TP3
2912    - [X] TP4
2913    - [X] TPA
2914    - [X] TPB
2915    - [X] TRC
2916    - [X] TRD
2917    - [X] TRK
2918    - [X] TSI
2919    - [X] TSS
2920    - [X] TT1
2921    - [X] TT2
2922    - [X] TT3
2923    - [X] TXT
2924    - [X] TXX
2925    - [X] TYE
2926    - [X] ULT
2927    - [X] WAF
2928    - [X] WAR
2929    - [X] WAS
2930    - [X] WCM
2931    - [X] WCP
2932    - [X] WPB
2933    - [X] WXX
2934*** DONE ID3v2.3
2935    - [X] APIC
2936    - [X] COMM
2937    - [X] GEOB
2938    - [X] TALB
2939    - [X] TBPM
2940    - [X] TCOM
2941    - [X] TCON
2942    - [X] TCOP
2943    - [X] TDAT
2944    - [X] TDLY
2945    - [X] TENC
2946    - [X] TEXT
2947    - [X] TFLT
2948    - [X] TIME
2949    - [X] TIT1
2950    - [X] TIT2
2951    - [X] TIT3
2952    - [X] TKEY
2953    - [X] TLAN
2954    - [X] TLEN
2955    - [X] TMED
2956    - [X] TOAL
2957    - [X] TOFN
2958    - [X] TOLY
2959    - [X] TOPE
2960    - [X] TORY
2961    - [X] TOWN
2962    - [X] TPE1
2963    - [X] TPE2
2964    - [X] TPE3
2965    - [X] TPE4
2966    - [X] TPOS
2967    - [X] TPUB
2968    - [X] TRCK
2969    - [X] TRDA
2970    - [X] TRSN
2971    - [X] TRSO
2972    - [X] TSIZ
2973    - [X] TSRC
2974    - [X] TSSE
2975    - [X] TXXX
2976    - [X] TYER
2977    - [X] USLT
2978    - [X] WCOM
2979    - [X] WCOP
2980    - [X] WOAF
2981    - [X] WOAR
2982    - [X] WOAS
2983    - [X] WORS
2984    - [X] WPAY
2985    - [X] WPUB
2986    - [X] WXXX
2987*** DONE ID3v2.4
2988    - [X] APIC
2989    - [X] COMM
2990    - [X] GEOB
2991    - [X] TALB
2992    - [X] TBPM
2993    - [X] TCOM
2994    - [X] TCON
2995    - [X] TCOP
2996    - [X] TDEN
2997    - [X] TDLY
2998    - [X] TDOR
2999    - [X] TDRC
3000    - [X] TDRL
3001    - [X] TDTG
3002    - [X] TENC
3003    - [X] TEXT
3004    - [X] TFLT
3005    - [X] TIPL
3006    - [X] TIT1
3007    - [X] TIT2
3008    - [X] TIT3
3009    - [X] TKEY
3010    - [X] TLAN
3011    - [X] TLEN
3012    - [X] TMCL
3013    - [X] TMED
3014    - [X] TMOO
3015    - [X] TOAL
3016    - [X] TOFN
3017    - [X] TOLY
3018    - [X] TOPE
3019    - [X] TOWN
3020    - [X] TPE1
3021    - [X] TPE2
3022    - [X] TPE3
3023    - [X] TPE4
3024    - [X] TPOS
3025    - [X] TPRO
3026    - [X] TPUB
3027    - [X] TRCK
3028    - [X] TRSN
3029    - [X] TRSO
3030    - [X] TSOA
3031    - [X] TSOP
3032    - [X] TSOT
3033    - [X] TSRC
3034    - [X] TSSE
3035    - [X] TSST
3036    - [X] TXXX
3037    - [X] USLT
3038    - [X] WCOM
3039    - [X] WCOP
3040    - [X] WOAF
3041    - [X] WOAR
3042    - [X] WOAS
3043    - [X] WORS
3044    - [X] WPAY
3045    - [X] WPUB
3046    - [X] WXXX
3047** DONE Adjust ID3v2 to store frames internally in order
3048   That is, they should output in the same order that they are stored on disk.
3049*** DONE Rebuild and simplify the ID3v2 frame handling
3050    Create a simple "frame protocol" which the frames can implement
3051    and the ID3v2 formats can use so that they parse/build consistently.
3052**** DONE is_latin_1
3053**** DONE UCS2Codec
3054**** DONE decode_syncsafe32
3055**** DONE encode_syncsafe32
3056**** DONE __padded_number_pair__
3057**** DONE __unpadded_number_pair__
3058**** DONE __number_pair__
3059**** DONE decode_ascii_c_string
3060**** DONE encode_ascii_c_string
3061**** DONE read_id3v2_comment()
3062     convert to standalone function
3063**** DONE skip_id3v2_comment()
3064     convert to standalone function
3065**** DONE ID3v22Comment
3066     - [X] __repr__
3067     - [X] parse
3068     - [X] build
3069     - [X] size
3070     - [X] __len__
3071     - [X] __getitem__
3072     - [X] __setitem__
3073     - [X] __delitem__
3074     - [X] keys
3075     - [X] values
3076     - [X] items
3077     - [X] __getattr__
3078     - [X] __setattr__
3079     - [X] __delattr__
3080     - [X] raw_info
3081     - [X] add_image
3082     - [X] delete_image
3083     - [X] images
3084     - [X] converted
3085     - [X] clean
3086***** DONE ID3v22_Frame
3087      - [X] __init__
3088      - [X] __repr__
3089      - [X] __eq__
3090      - [X] parse
3091      - [X] build
3092      - [X] size
3093      - [X] converted
3094      - [X] clean
3095      - [X] raw_info
3096***** DONE ID3v22_TXX_Frame
3097      - [X] __init__
3098      - [X] __repr__
3099      - [X] __eq__
3100      - [X] __unicode__
3101      - [X] parse
3102      - [X] build
3103      - [X] size
3104      - [X] converted
3105      - [X] number
3106      - [X] total
3107      - [X] raw_info
3108      - [X] clean
3109***** DONE ID3v22_PIC_Frame
3110      - [X] __init__
3111      - [X] __repr__
3112      - [X] __eq__
3113      - [X] __getattr__
3114      - [X] __setattr__
3115      - [X] parse
3116      - [X] build
3117      - [X] size
3118      - [X] converted
3119      - [X] raw_info
3120      - [X] clean
3121***** DONE ID3v22_COM_Frame
3122      - [X] __init__
3123      - [X] __repr__
3124      - [X] __eq__
3125      - [X] __unicode__
3126      - [X] parse
3127      - [X] build
3128      - [X] size
3129      - [X] converted
3130      - [X] raw_info
3131      - [X] clean
3132**** DONE ID3v23Comment
3133     - [X] __repr__
3134     - [X] parse
3135     - [X] build
3136     - [X] size
3137     - [X] __len__
3138     - [X] __getitem__
3139     - [X] __setitem__
3140     - [X] __delitem__
3141     - [X] keys
3142     - [X] values
3143     - [X] items
3144     - [X] __setattr__
3145     - [X] __getattr__
3146     - [X] __delattr__
3147     - [X] raw_info
3148     - [X] add_image
3149     - [X] delete_image
3150     - [X] images
3151     - [X] converted
3152     - [X] clean
3153***** DONE ID3v23_Frame
3154      - [X] __init__
3155      - [X] __repr__
3156      - [X] __eq__
3157      - [X] raw_info
3158      - [X] parse
3159      - [X] build
3160      - [X] size
3161      - [X] converted
3162      - [X] clean
3163***** DONE ID3v23_TXXX_Frame
3164      - [X] __init__
3165      - [X] __repr__
3166      - [X] __eq__
3167      - [X] __unicode__
3168      - [X] raw_info
3169      - [X] parse
3170      - [X] build
3171      - [X] size
3172      - [X] converted
3173      - [X] clean
3174      - [X] number
3175      - [X] total
3176***** DONE ID3v23_APIC_Frame
3177      - [X] __init__
3178      - [X] __repr__
3179      - [X] __eq__
3180      - [X] __getattr__
3181      - [X] __setattr__
3182      - [X] raw_info
3183      - [X] parse
3184      - [X] build
3185      - [X] size
3186      - [X] converted
3187      - [X] clean
3188***** DONE ID3v23_COMM_Frame
3189      - [X] __init__
3190      - [X] __repr__
3191      - [X] __eq__
3192      - [X] __unicode__
3193      - [X] raw_info
3194      - [X] parse
3195      - [X] build
3196      - [X] size
3197      - [X] converted
3198      - [X] clean
3199**** DONE ID3v24Comment
3200     - [X] __repr__
3201     - [X] parse
3202     - [X] build
3203     - [X] size
3204     - [X] __len__
3205     - [X] __getitem__
3206     - [X] __setitem__
3207     - [X] __delitem__
3208     - [X] keys
3209     - [X] values
3210     - [X] items
3211     - [X] __setattr__
3212     - [X] __getattr__
3213     - [X] __delattr__
3214     - [X] raw_info
3215     - [X] add_image
3216     - [X] delete_image
3217     - [X] images
3218     - [X] converted
3219     - [X] clean
3220***** DONE ID3v24_Frame
3221      - [X] __init__
3222      - [X] __repr__
3223      - [X] __eq__
3224      - [X] raw_info
3225      - [X] parse
3226      - [X] build
3227      - [X] size
3228      - [X] converted
3229      - [X] clean
3230***** DONE ID3v24_TXXX_Frame
3231      - [X] __init__
3232      - [X] __repr__
3233      - [X] __eq__
3234      - [X] __unicode__
3235      - [X] raw_info
3236      - [X] parse
3237      - [X] build
3238      - [X] size
3239      - [X] converted
3240      - [X] clean
3241      - [X] number
3242      - [X] total
3243***** DONE ID3v24_APIC_Frame
3244      - [X] __init__
3245      - [X] __repr__
3246      - [X] __eq__
3247      - [X] __getattr__
3248      - [X] __setattr__
3249      - [X] raw_info
3250      - [X] parse
3251      - [X] build
3252      - [X] size
3253      - [X] converted
3254      - [X] clean
3255***** DONE ID3v24_COMM_Frame
3256      - [X] __init__
3257      - [X] __repr__
3258      - [X] __eq__
3259      - [X] __unicode__
3260      - [X] raw_info
3261      - [X] parse
3262      - [X] build
3263      - [X] size
3264      - [X] converted
3265      - [X] clean
3266*** DONE Ensure they pass unit tests
3267*** DONE Ensure UCS-2 C strings are handled correctly
3268    For UCS-2/UTF-16 encoding, the string should end on 2 NULL bytes
3269    - [X] ID3v22_COM_Frame
3270    - [X] ID3v22_PIC_Frame
3271    - [X] ID3v23_COMM_Frame
3272    - [X] ID3v23_APIC_Frame
3273    - [X] ID3v24_COMM_Frame
3274    - [X] ID3v24_APIC_Frame
3275*** DONE Handle user-defined text frames properly
3276    - [X] ID3v22_TXX_Frame
3277    - [X] ID3v23_TXXX_Frame
3278    - [X] ID3v24_TXXX_Frame
3279*** DONE Handle web link frames properly
3280    - [X] ID3v22_W__Frame
3281    - [X] ID3v23_W___Frame
3282    - [X] ID3v24_W___Frame
3283*** DONE Handle user-defined web link frames properly
3284    - [X] ID3v22_WXX_Frame
3285    - [X] ID3v23_WXXX_Frame
3286    - [X] ID3v24_WXXX_Frame
3287*** DONE Handle text frames with embedded NULLs
3288    According to the spec, anything after the first decoded NULL
3289    should be ignored and not displayed.
3290    - [X] ID3v22_T__Frame
3291    - [X] ID3v22_TXX_Frame
3292    - [X] ID3v23_T___Frame
3293    - [X] ID3v23_TXXX_Frame
3294    - [X] ID3v24_T___Frame
3295    - [X] ID3v24_TXXX_Frame
3296*** DONE Update tests to always check required/prohibited leading zeroes
3297    - [X] ID3v22Comment
3298    - [X] ID3v23Comment
3299    - [X] ID3v24Comment
3300** DONE Eliminate the MetaData.merge() method
3301   The idea of this method is to provide some sort of precedence
3302   which combining metadata from two sources.
3303   For example, in track2track, we want provided XMCD metadata
3304   to override metadata inside the track itself.
3305   The .merge() method takes a raw MetaData object from XMCD
3306   and merges it with non-blank fields from the original file
3307   which means the XMCD metadata is the "base" of sorts.
3308
3309   A cleaner approach would be to take the raw MetaData object from XMCD,
3310   get a list of its non-empty fields and call __setattr__ on
3311   the track's metadata to update those fields.
3312
3313   MetaData with the highest priority would call __setattr__ last.
3314*** DONE Remove method from MetaData and subclasses
3315    - [X] ApeTag
3316    - [X] FlacMetaData
3317    - [X] ID3v22Comment
3318    - [X] ID3v23Comment
3319    - [X] ID3v24Comment
3320    - [X] ID3CommentPair
3321    - [X] M4A_META_Atom
3322    - [X] MetaData
3323    - [X] VorbisComment
3324*** DONE Remove method from tools
3325    - [X] track2track
3326    - [X] trackrename
3327    - [X] tracksplit
3328    - [X] tracktag
3329*** DONE Remove method from documentation
3330*** DONE Remove method from tests
3331** DONE Have trackinfo display metadata fields by default
3332   Instead of displaying the low-level info,
3333   show fields like "track name", "album name", etc.
3334   unless a -R/--raw flag is indicated.
3335*** DONE Replace MetaData.comment_pairs() with MetaData.raw_info()
3336    This should be even lower level than it is now, and unsorted.
3337    - [X] ApeTag
3338    - [X] FlacMetaData
3339    - [X] ID3CommentPair
3340    - [X] ID3v1Comment
3341    - [X] ID3v22Comment
3342    - [X] ID3v23Comment
3343    - [X] ID3v24Comment
3344    - [X] M4A_META_Atom
3345    - [X] OggFlacMetaData
3346    - [X] VorbisComment
3347**** DONE Document MetaData.raw_info() method
3348*** DONE Convert MetaData.__unicode__() to display fields by name
3349    This should always use the higher level implementation
3350    and sort fields by name.
3351*** DONE Update trackinfo to use MetaData's unicode output by default
3352*** DONE Add -L/--low-level option to trackinfo
3353*** DONE Add -L/--low-level option to trackinfo.1
3354*** DONE Add consistent embedded cuesheet display
3355*** DONE Update unit tests
3356** DONE Make metadata fully round-trippable
3357   For instance, the following should hold:
3358
3359   >>> track_data1 = open(track.filename).read()
3360   >>> track.update_metadata(track.get_metadata())
3361   >>> track_data2 = open(track.filename).read()
3362   >>> track_data1 == track_data2
3363   True
3364
3365   This requires metadata to not jumble fields around until required.
3366** DONE Don't populate empty track_name, ISRC values during tracksplit
3367** DONE Fix ALAC channel assignment
3368   Since the channel assignment is detailed in the specs,
3369   add proper support for them.
3370*** DONE Update ALACAudio.channel_mask() method
3371*** DONE Update ALACAudio.from_pcm() method
3372    Like FLAC, it should reject unsupported channel masks.
3373*** DONE Update py_decoders.ALACDecoder's .channel_mask attribute
3374*** DONE Update py_decoders.ALACDecoder's .read() method
3375    Convert ALAC channel order to Wave channel order internally
3376    rather than punt that task to a reordering wrapper.
3377*** DONE Update py_encoders.encode_mdat
3378    Convert Wave channel order to ALAC channel order internally
3379    rather than punt that task to a reordering wrapper.
3380*** DONE Update decoders.ALACDecoder's .channel_mask attribute
3381*** DONE Update decoders.ALACDecoder's .read() method
3382    Convert ALAC channel order to Wave channel order internally
3383    rather than punt that task to a reordering wrapper.
3384*** DONE Update encoders.encode_alac
3385    Convert Wave channel order to ALAC channel order internally
3386    rather than punt that task to a reordering wrapper.
3387*** DONE Add unit tests to ensure channel variants encode/decode correctly
3388    - [X] 1 channel
3389    - [X] 2 channels
3390    - [X] 3 channels
3391    - [X] 4 channels
3392    - [X] 5 channels
3393    - [X] 6 channels
3394    - [X] 7 channels
3395    - [X] 8 channels
3396**** DONE Add unit tests to ensure invalid channel counts aren't encoded
3397**** DONE Add unit tests to ensure invalid channel masks aren't encoded
3398** DONE Add Python-based file encoders
3399   Like the Python-based decoders,
3400   these provide simple reference implementations
3401   one can easily pull apart to see how the encoders work.
3402*** DONE py_encoders/encode_flac
3403*** DONE py_encoders/encode_shn
3404*** DONE py_encoders/encode_alac
3405*** DONE py_encoders/encode_wavpack
3406** DONE Cleanup Shorten for better accuracy
3407*** DONE Handle 8-bit files correctly
3408    waves are unsigned, aiffs are signed, Sun AU is ?
3409    - [X] update py_decoders.SHNDecoder
3410    - [X] update decoders.SHNDecoder
3411    - [X] update py_encoders.encode_shn
3412    - [X] update encoders.encode_shn
3413    - [X] update __shn__.py
3414    - [X] update documentation
3415*** DONE Handle multichannel Shn/AIFF files correctly
3416**** DONE Ensure multichannel files converted from AIFF work
3417     Their channels should be kept in whatever order AIFF happens to use
3418     rather than have them remapped to wave order.
3419**** DONE Ensure multichannel Shn/AIFF to PCM works
3420     Output channels should be rearranged to PCMReader order.
3421*** DONE Improve thread friendliness in decoder/encoder
3422    - [X] decoders/shn.c
3423    - [X] encoders/shn.h
3424** DONE Overhaul array.h
3425   Just as the bitstream module benefits from having a good design,
3426   the array module would also be better served by having
3427   a more elegant API.
3428
3429   The main goal is to have less function variants and
3430   data allocation/deallocation routines to remember.
3431   Something like:
3432
3433   array = new_int_array();
3434   array->append(array, value);
3435   array->del(array);
3436
3437   which always functions the same whether we're dealing with ints or
3438   floats or other arrays, yet is still type-checked at compile-time.
3439*** DONE Better unify FrameList <-> array_ia routines
3440*** DONE Remove old src/decoders/pcm.h src/decoders/pcm.c module
3441    Use the unified pcmconv.h module instead
3442    - [X] mlp.c
3443    - [X] shn.c
3444    - [X] sine.c
3445*** DONE Replace old array.h with new one in all source files
3446    - [X] decoders/mlp.h
3447    - [X] decoders/shn.h
3448    - [X] decoders/sine.h
3449    - [X] decoders/wavpack.h
3450    - [X] encoders/alac.h
3451    - [X] encoders/shn.h
3452    - [X] encoders/wavpack.h
3453*** DONE Remove old array.h
3454*** DONE Replace array2.h with array.h in all source files
3455** DONE Make second pass through updated documentation
3456*** DONE Update introduction
3457**** DONE re-explain endianness
3458**** DONE explain pseudocode with actual code examples
3459*** DONE Convert writes to a systax consistent with reads
3460    var <- read 10 unsigned bits
3461    var -> write 10 unsigned bits
3462    - [X] alac.tex
3463    - [X] flac.tex
3464    - [X] shorten.tex
3465    - [X] wavpack.tex
3466*** DONE Convert for loops to a consistent assignment syntax
3467    for i <- 0 to x do
3468       <code block>
3469    - [X] alac.tex
3470    - [X] dvda2.tex
3471    - [X] flac.tex
3472    - [X] shorten.tex
3473    - [X] wavpack.tex
3474*** DONE Double-check codecs for consistency
3475    - [X] alac.tex
3476    - [X] dvda2.tex
3477    - [X] flac.tex
3478    - [X] shorten.tex
3479    - [X] wavpack.tex
3480*** DONE Add hyperref linking
3481    If one's browsing the PDF,
3482    one should be able to click on operations directly
3483    and go to a specific part of the doc.
3484    - [X] alac.tex
3485    - [X] dvda2.tex
3486    - [X] flac.tex
3487    - [X] shorten.tex
3488    - [X] wavpack.tex
3489** DONE Tweak track labeling interactive mode for better usability
3490   make return key move to the next line
3491** DONE Combine and simplify DVD-A decoding and documentation
3492   The current routine bounces back and forth between Python and C
3493   several times in order to generate output, overcomplicating the design.
3494   I'd prefer to have a simpler, low-level, random-access reader hooked directly
3495   to the DVDATrack object - analagous to audiotools.cdda.CDDA.
3496** DONE Overhaul decoding/encoding documentation
3497   It needs to be cleaned up so one can better follow the entire
3498   decoding process, as well as the entire encoding process.
3499   Use a mix of pseudocode, bit diagrams, bit parsing diagrams
3500   and examples so that it can be followed with as little effort as possible.
3501*** DONE FLAC
3502**** DONE decoding
3503**** DONE encoding
3504*** DONE ALAC
3505**** DONE decoding
3506**** DONE encoding
3507*** DONE WavPack
3508**** DONE decoding
3509**** DONE encoding
3510*** DONE Shorten
3511**** DONE decoding
3512**** DONE encoding
3513*** DONE DVD-A
3514**** DONE decoding
3515* DONE Finish version 2.18
3516** DONE Ensure audiotools works on FreeBSD
3517   the unprotection module, in particular, needs additional testing
3518** DONE Ensure excessive zero residuals don't overflow output buffers
3519   Certain formats provide "escape code" blocks of zeroes.
3520   Ensure these routines don't generate more zeroes than are allowed
3521   (either accidentally or deliberately).
3522*** DONE ALACDecoder
3523*** DONE WavPackDecoder
3524** DONE Cleaup documentation layout
3525   Add subdirectories for format figures.
3526** DONE Spellcheck reference docs
3527   - [X] introduction.tex
3528   - [X] basics.tex
3529   - [X] wav.tex
3530   - [X] aiff.tex
3531   - [X] au.tex
3532   - [X] shorten.tex
3533   - [X] flac.tex
3534   - [X] wavpack.tex
3535   - [X] ape.tex
3536   - [X] mp3.tex
3537   - [X] m4a.tex
3538   - [X] alac.tex
3539   - [X] vorbis.tex
3540   - [X] oggflac.tex
3541   - [X] speex.tex
3542   - [X] musepack.tex
3543   - [X] dvda2.tex
3544   - [X] freedb.tex
3545   - [X] musicbrainz.tex
3546   - [X] replaygain.tex
3547** DONE Make ReplayGain a configurable option
3548   Even tag-based ReplayGain should be something
3549   users can turn on or off globally via audiotools-config
3550*** DONE add audiotools-config option
3551**** DONE document in audiotools-config man page
3552*** DONE update tools to use option
3553    - [X] cdtrack
3554    - [X] dvda2track
3555    - [X] track2track
3556    - [X] tracksplit
3557** DONE Add progress to track2cd audio file conversion
3558   - [X] CD quality, embedded cuesheet
3559   - [X] non-CD quality, embedded cuesheet
3560   - [X] CD quality, external cuesheet
3561   - [X] non-CD quality, external cuesheet
3562   - [X] CD quality, multiple files
3563   - [X] non-CD quality, multiple files
3564** DONE Improve .wav performance
3565   don't read entire data chunk by default
3566** DONE Integrate MusicBrainz/FreeDB lookup with cd2track/dvda2track
3567   The two step extraction process is a relic from when I'd do
3568   batch lookups via modem.
3569   Combining metadata querying with extraction lets me perform more
3570   powerful lookups than are possible by using XMCD/XML file intermediaries.
3571
3572   However, CD lookups may be still be wrong.
3573   Therefore, it's essential to have a simple, interactive
3574   track metadata editor so that this data is very easy to populate.
3575*** DONE Add multi-track interactive editing mode to tracktag
3576**** DONE Update man page
3577*** DONE Build unified metadata selection widget for interactive modes
3578    This is something that can be run while a disc is being extracted
3579    or a track is being split which will drop the user back
3580    into a progress indicator once completed and then
3581    tag/rename the resulting tracks.
3582*** DONE Add MusicBrainz/FreeDB lookup options to audiotools-config
3583    It should be possible to decide whether to query either or both
3584    as a config option.
3585*** DONE Query MusicBrainz for album_number/album_total info
3586    Given a particular disc ID, there must be some way to pull
3587    the disc's album_number/album_total off MusicBrainz's servers
3588    if it's one of a series of discs.
3589*** DONE Add pre-extraction metadata lookup to tracksplit
3590**** DONE Update man page
3591*** DONE Add pre-extraction metadata lookup to cd2track
3592**** DONE Update man page with new options
3593*** DONE Add pre-extraction metadata lookup to dvda2track
3594**** DONE Update man page with new options
3595*** DONE Update cdinfo to use new ID calculation routines
3596    Ensure they handle the FreeDB test disc properly.
3597*** DONE Add pre-play metadata lookup to cdplay
3598**** DONE Update man page
3599*** DONE Add album-number/album-total options to tracksplit
3600    Allow these values to be populated at split-time
3601    if none can be found in metadata services
3602**** DONE Update man page
3603*** DONE Update album-number/album-total options in cd2track
3604*** DONE Remove xmcd-specific options
3605    Automatic CD lookup should be folded into utilites as needed.
3606    - [X] cd2track
3607    - [X] cdplay
3608    - [X] dvda2track
3609    - [X] track2track
3610    - [X] trackrename
3611    - [X] tracksplit
3612    - [X] tracktag
3613*** DONE Remove xmcd-specific tools
3614    - [X] cd2xmcd
3615    - [X] dvda2xmcd
3616    - [X] editxmcd
3617    - [X] track2xmcd
3618*** DONE Update unit tests
3619    - [X] cd2track
3620    - [X] dvda2track
3621    - [X] tracksplit
3622*** DONE Deprecate xmcd-specific modules
3623    Since metadata lookup handles files internally,
3624    there's no need for metadata file handling classes/functions
3625    at the Python level.
3626**** DONE Update documentation to indicate deprecation
3627** DONE Adjust album_number/track_number heuristics
3628   these should be last-resort fields that apply
3629   *only* if a track has no metadata of any kind
3630** DONE Don't port cuesheets with AudioFile.set_metadata()
3631   This is mostly for WavPack since it embeds cuesheet data
3632   in the APEv2 tag.
3633   - [X] FlacAudio
3634   - [X] OggFlacAudio
3635   - [X] WavPackAudio
3636** DONE Ensure overly-long files are handled correctly
3637   That is, anything larger than a .wav can typically handle
3638*** DONE wave should fail with error
3639*** DONE aiff should fail with error
3640*** DONE au should fail with error
3641*** DONE flac should work
3642*** DONE ogg flac should work
3643*** DONE wavpack should work
3644*** DONE alac should work
3645*** DONE shorten shouldn't begin
3646    requires verbatim wave or aiff chunks
3647    which can't be created because
3648    the amount of PCM data is too large
3649** DONE Fix FLAC embedded cuesheets
3650** DONE Ensure files are PEP8-compliant
3651*** DONE check user-level scripts
3652    - [X] audiotools-config
3653    - [X] cd2track
3654    - [X] cdinfo
3655    - [X] cdplay
3656    - [X] coverdump
3657    - [X] coverview
3658    - [X] dvda2track
3659    - [X] dvdainfo
3660    - [X] record2track
3661    - [X] track2cd
3662    - [X] track2track
3663    - [X] trackcat
3664    - [X] trackcmp
3665    - [X] trackinfo
3666    - [X] tracklength
3667    - [X] tracklint
3668    - [X] trackplay
3669    - [X] trackrename
3670    - [X] tracksplit
3671    - [X] tracktag
3672    - [X] trackverify
3673    - [X] setup.py
3674*** DONE check audiotools module
3675    - [X] __aiff__.py
3676    - [X] __ape__.py
3677    - [X] __au__.py
3678    - [X] __dvda__.py
3679    - [X] __flac__.py
3680    - [X] __freedb__.py
3681    - [X] __id3__.py
3682    - [X] __id3v1__.py
3683    - [X] __image__.py
3684    - [X] __init__.py
3685    - [X] __m4a__.py
3686    - [X] __m4a_atoms__.py
3687    - [X] __mp3__.py
3688    - [X] __musepack__.py
3689    - [X] __musicbrainz__.py
3690    - [X] __ogg__.py
3691    - [X] __shn__.py
3692    - [X] __vorbis__.py
3693    - [X] __vorbiscomment__.py
3694    - [X] __wav__.py
3695    - [X] __wavpack__.py
3696    - [X] cue.py
3697    - [X] delta.py
3698    - [X] freedb.py
3699    - [X] musicbrainz.py
3700    - [X] player.py
3701    - [X] toc.py
3702    - [X] ui.py
3703*** DONE check audiotools.py_decoders module
3704    - [X] __init__.py
3705    - [X] alac.py
3706    - [X] flac.py
3707    - [X] shn.py
3708    - [X] wavpack.py
3709*** DONE check audiotools.py_encoders module
3710    - [X] __init__.py
3711    - [X] alac.py
3712    - [X] flac.py
3713    - [X] shn.py
3714    - [X] wavpack.py
3715*** DONE check test
3716    - [X] test.py
3717    - [X] test_core.py
3718    - [X] test_formats.py
3719    - [X] test_metadata.py
3720    - [X] test_streams.py
3721    - [X] test_utils.py
3722** DONE Double-check reference docs one last time
3723*** DONE Ensure pages break correctly in letter mode
3724*** DONE Ensure pages break correctly in A4 mode
3725** DONE Fix FLAC seektable generation
3726   Ensure new seektables are aligned properly.
3727*** DONE Ensure proper seektable written on from_pcm
3728*** DONE Add .offsets() method to FlacDecoder
3729    Instead of decoding the file, this walks through it
3730    and returns a list of absolute file offsets of all frames.
3731*** DONE Add method to generate Flac_SEEKTABLE from offset list
3732*** DONE Add tracklint check/fix for mis-aligned seektables
3733*** DONE Add unit tests for seektable errors
3734    - [X] empty seekpoints
3735    - [X] mis-ordered seekpoints
3736    - [X] bad seekpoint destinations
3737** DONE Shift lint to the AudioFile and MetaData subclasses
3738   Instead of having tracklint have to perform lots of built-in tests,
3739   it would be better to have clean() functions added to the classes
3740   themselves.
3741   For MetaData, it could return a new object with fixed fields.
3742   For AudioFile, it could function like convert() and build a new file
3743   with fixes applied.
3744*** DONE Add clean() method to MetaData and subclasses
3745**** DONE MetaData
3746**** DONE ApeTag
3747**** DONE WavPackAPEv2
3748**** DONE FlacMetaData
3749**** DONE ID3v22Comment
3750**** DONE ID3v23Comment
3751**** DONE ID3v24Comment
3752**** DONE ID3v1Comment
3753**** DONE ID3CommentPair
3754**** DONE M4AMetaData
3755**** DONE VorbisComment
3756***** DONE FlacVorbisComment
3757***** DONE UnframedVorbisComment
3758*** DONE Add clean() method to AudioFile and subclasses
3759    I expect a lot of these will do nothing in the short term.
3760**** DONE AudioFile
3761**** DONE AiffAudio
3762     - [X] Reorder streams in which the COMM chunk doesn't come before data
3763     - [X] remove duplicate COMM chunks
3764     - [X] remove duplicate SSND chunks
3765***** DONE add unit test for verify()
3766      - [X] multiple COMM chunks found
3767      - [X] multiple SSND chunks found
3768      - [X] SSND chunk before COMM chunk
3769***** DONE add unit test for clean()
3770      - [X] multiple COMM chunks found
3771      - [X] multiple SSND chunks found
3772      - [X] SSND chunk before COMM chunk
3773**** DONE FlacAudio
3774**** DONE WaveAudio
3775     - [X] reorder streams in which the fmt chunk doesn't come before data
3776     - [X] remove duplicate fmt chunks
3777     - [X] remove duplicate data chunks
3778***** DONE add unit test for verify()
3779      - [X] multiple fmt chunks found
3780      - [X] multiple data chunks found
3781      - [X] data chunk before fmt chunk
3782***** DONE add unit test for clean()
3783      - [X] multiple fmt chunks found
3784      - [X] multiple data chunks found
3785      - [X] data chunk before fmt chunk
3786*** DONE Document MetaData clean() method
3787*** DONE Document AudioFile clean() method
3788*** DONE Add more comprehensive unit tests for clean() methods
3789*** DONE Convert tracklint to use clean() methods
3790** DONE Ensure individual unit tests pass
3791*** DONE Lib
3792    - [X] core
3793    - [X] cuesheet
3794    - [X] freedb
3795    - [X] image
3796    - [X] musicbrainz
3797    - [X] pcm
3798    - [X] bitstream
3799    - [X] replaygain
3800    - [X] resample
3801    - [X] tocfile
3802    - [X] verify
3803    - [X] player
3804*** DONE Format
3805    - [X] audiofile
3806    - [X] lossless
3807    - [X] lossy
3808    - [X] aiff
3809    - [X] alac
3810    - [X] au
3811    - [X] dvda
3812    - [X] flac
3813    - [X] m4a
3814    - [X] mp2
3815    - [X] mp3
3816    - [X] oggflac
3817    - [X] shorten
3818    - [X] sines
3819    - [X] vorbis
3820    - [X] wave
3821    - [X] wavpack
3822*** DONE Metadata
3823    - [X] metadata
3824    - [X] flac
3825    - [X] wavpack
3826    - [X] id3v1
3827    - [X] id3v2
3828    - [X] vorbis
3829    - [X] m4a
3830*** DONE Util
3831    - [X] audiotools_config
3832    - [X] cd2track
3833    - [X] cdinfo
3834    - [X] cdplay
3835    - [X] coverdump
3836    - [X] coverview
3837    - [X] dvda2track
3838    - [X] dvdainfo
3839    - [X] record2track
3840    - [X] track2cd
3841    - [X] track2track
3842    - [X] trackcat
3843    - [X] trackcmp
3844    - [X] trackinfo
3845    - [X] tracklength
3846    - [X] tracklint
3847    - [X] trackplay
3848    - [X] trackrename
3849    - [X] tracksplit
3850    - [X] tracktag
3851    - [X] trackverify
3852** DONE Ensure complete unit test passes
3853   this includes some compound tests
3854   which may not be covered by individual tests
3855** DONE Cleanup bitstream module
3856   This is at the heart of a lot of things so it should be properly cleaned up.
3857*** DONE Merge bitstream_r and bitstream_w source files
3858*** DONE Make BitstreamReader and BitstreamWriter more symmetric
3859*** DONE Add placeholders to BitstreamWriter
3860    These allow one to put temporary values in the stream
3861    which can be automatically filled-in later on.
3862**** DONE Add write_placeholder method to BitstreamWriter
3863**** DONE Add fill_placeholder method to BitstreamWriter
3864**** DONE Add write_64_placeholder method to BitstreamWriter
3865**** DONE Add fill_64_placeholder method to BitstreamWriter
3866**** DONE Implement bw_write_placeholder_f
3867**** DONE Implement bw_write_64_placeholder_f
3868**** DONE Implement bw_fill_placeholder_f
3869**** DONE Implement bw_fill_64_placeholder_f
3870**** DONE Implement bw_write_placeholder_r
3871**** DONE Implement bw_write_64_placeholder_r
3872**** DONE Implement bw_fill_placeholder_r
3873**** DONE Implement bw_fill_64_placeholder_r
3874**** DONE Implement bw_write_placeholder_a
3875     Should manage the placeholder stack, but only keep track of bits sent
3876**** DONE Implement bw_write_64_placeholder_a
3877     Should manage the placeholder stack, but only keep track of bits sent
3878**** DONE Implement bw_fill_placeholder_a
3879     Should manage the placeholder stack, but not do anything else
3880**** DONE Implement bw_fill_64_placeholder_a
3881     Should manage the placeholder stack, but not do anything else
3882**** DONE Store filled placeholders in a stack for reuse
3883**** DONE Clean out allocated placeholders as close() time
3884***** DONE Trigger warnings if not all placeholders are used at close()
3885*** DONE Add C-based unit tests
3886    This should be something I can compile into a standalone file
3887    and run with no external dependencies.
3888    The idea is to build something valgrind-able to ensure
3889    there's no memory errors.
3890**** DONE BitstreamReader
3891***** DONE big-endian
3892      - [X] read
3893      - [X] read_signed
3894      - [X] read_64
3895      - [X] skip
3896      - [X] skip_bytes
3897      - [X] unread
3898      - [X] read_unary
3899      - [X] read_limited_unary
3900      - [X] read_huffman_code
3901      - [X] byte_align
3902      - [X] read_bytes
3903      - [X] set_endianness
3904      - [X] mark
3905      - [X] unmark
3906      - [X] rewind
3907***** DONE little-endian
3908      - [X] read
3909      - [X] read_signed
3910      - [X] read_64
3911      - [X] skip
3912      - [X] skip_bytes
3913      - [X] unread
3914      - [X] read_unary
3915      - [X] read_limited_unary
3916      - [X] read_huffman_code
3917      - [X] byte_align
3918      - [X] read_bytes
3919      - [X] set_endianness
3920      - [X] mark
3921      - [X] unmark
3922      - [X] rewind
3923***** DONE br_try/etry
3924      - [X] read
3925      - [X] read_signed
3926      - [X] read_64
3927      - [X] skip
3928      - [X] skip_bytes
3929      - [X] read_unary
3930      - [X] read_limited_unary
3931      - [X] read_huffman_code
3932      - [X] read_bytes
3933      - [X] substream_append
3934***** DONE Callbacks
3935      - [X] read
3936      - [X] read_signed
3937      - [X] read_64
3938      - [X] skip
3939      - [X] skip_bytes
3940      - [X] read_unary
3941      - [X] read_limited_unary
3942      - [X] read_huffman_code
3943      - [X] read_bytes
3944**** DONE Substream BitstreamReader
3945**** DONE BitstreamWriter
3946***** DONE big-endian
3947***** DONE little-endian
3948***** DONE callbacks
3949**** DONE BitstreamRecorder
3950**** DONE BitstreamAccumulator
3951*** DONE Shift BitstreamReader/BitstreamWriters to audiotools.bitstream
3952*** DONE Add documentation for audiotools.bitstream
3953**** DONE BitstreamReader
3954     - [X] add_callback
3955     - [X] byte_align
3956     - [X] call_callbacks
3957     - [X] close
3958     - [X] limited_unary
3959     - [X] mark
3960     - [X] parse
3961     - [X] pop_callback
3962     - [X] read
3963     - [X] read64
3964     - [X] read_bytes
3965     - [X] read_huffman_code
3966     - [X] read_signed
3967     - [X] read_signed64
3968     - [X] rewind
3969     - [X] set_endianness
3970     - [X] skip
3971     - [X] skip_bytes
3972     - [X] substream
3973     - [X] substream_append
3974     - [X] unary
3975     - [X] unmark
3976     - [X] unread
3977**** DONE BitstreamWriter
3978     - [X] add_callback
3979     - [X] build
3980     - [X] byte_align
3981     - [X] call_callbacks
3982     - [X] close
3983     - [X] flush
3984     - [X] pop_callback
3985     - [X] set_endianness
3986     - [X] unary
3987     - [X] write
3988     - [X] write64
3989     - [X] write_bytes
3990     - [X] write_signed
3991     - [X] write_signed64
3992**** DONE BitstreamRecorder
3993     - [X] add_callback
3994     - [X] bits
3995     - [X] build
3996     - [X] byte_align
3997     - [X] bytes
3998     - [X] call_callbacks
3999     - [X] close
4000     - [X] copy
4001     - [X] data
4002     - [X] flush
4003     - [X] pop_callback
4004     - [X] reset
4005     - [X] set_endianness
4006     - [X] split
4007     - [X] swap
4008     - [X] unary
4009     - [X] write
4010     - [X] write64
4011     - [X] write_bytes
4012     - [X] write_signed
4013     - [X] write_signed64
4014**** DONE BitstreamAccumulator
4015     - [X] bits
4016     - [X] build
4017     - [X] byte_align
4018     - [X] bytes
4019     - [X] close
4020     - [X] reset
4021     - [X] set_endianness
4022     - [X] unary
4023     - [X] write
4024     - [X] write64
4025     - [X] write_bytes
4026     - [X] write_signed
4027     - [X] write_signed64
4028**** DONE HuffmanTree
4029**** DONE Substream
4030**** DONE format_size
4031*** DONE Handle bitstream closing more consistently
4032    The basic handle cycle is:
4033
4034    h = open_handle(substream); /*allocates space for handle*/
4035    h->method(h);               /*performs reads/writes on handle*/
4036    ...
4037    h->close(h);                /*closes substream and deallocates handle*/
4038
4039    But we also need two additional methods
4040    for working with the handle and its substream seperately.
4041
4042    | method            | substream      | handle      |
4043    |-------------------+----------------+-------------|
4044    | close()           | flushed/closed | deallocated |
4045    | close_substream() | flushed/closed | nothing     |
4046    | free()            | nothing        | deallocated |
4047    |-------------------+----------------+-------------|
4048
4049    This is especially important for the Python wrappers
4050    in which closing and dealloacting will come from different routines.
4051**** DONE Add functions to bitstream core
4052***** DONE BitstreamReader
4053      - [X] br_close_substream_f
4054      - [X] br_close_substream_s
4055      - [X] br_close_substream_p
4056      - [X] br_close_methods
4057      - [X] br_free_f
4058      - [X] br_free_s
4059      - [X] br_free_p
4060      - [X] br_close
4061      - [X] br_read_bits_c
4062      - [X] br_read_bits64_c
4063      - [X] br_skip_bits_c
4064      - [X] br_unread_c
4065      - [X] br_read_unary_c
4066      - [X] br_read_limited_unary_c
4067      - [X] br_read_huffman_code_c
4068      - [X] br_read_bytes_c
4069      - [X] br_set_endianness_c
4070      - [X] br_close_substream_c
4071      - [X] br_mark_c
4072      - [X] br_rewind_c
4073      - [X] br_unmark_c
4074      - [X] br_substream_append_c
4075***** DONE BitstreamWriter
4076      - [X] bw_close_substream_f
4077      - [X] bw_close_substream_r
4078      - [X] bw_close_substream_p
4079      - [X] bw_close_substream_a
4080      - [X] bw_close_methods
4081      - [X] bw_free_f_a
4082      - [X] bw_free_r
4083      - [X] bw_free_p
4084      - [X] bw_close
4085      - [X] bw_write_bits_c
4086      - [X] bw_write_bits64_c
4087      - [X] bw_write_bytes_c
4088      - [X] bw_write_signed_bits_c
4089      - [X] bw_write_signed_bits64_c
4090      - [X] bw_write_unary_c
4091      - [X] bw_set_endianness_c
4092      - [X] bw_close_substream_c
4093      - [X] bw_byte_align_c
4094***** DONE Ensure bw_rec_split detects closed stream(s) properly
4095      Call bw_abort if one attempts to split from or to a closed stream.
4096***** DONE Ensure bw_dump_bytes detects closed stream properly
4097      Call bw_abort if one attempts to dump bytes to a closed stream.
4098***** DONE Ensure bw_rec_copy detects closed stream properly
4099      Call bw_abort if one attempts to copy records to a closed stream.
4100**** DONE Update mod_bitstream.c to use new hooks properly
4101***** DONE Add bw_try/bw_etry wrappers around write methods
4102      - [X] BitstreamWriter_write
4103      - [X] BitstreamWriter_write_signed
4104      - [X] BitstreamWriter_write64
4105      - [X] BitstreamWriter_write_signed64
4106      - [X] BitstreamWriter_unary
4107      - [X] BitstreamWriter_byte_align
4108      - [X] BitstreamWriter_write_bytes
4109      - [X] BitstreamWriter_flush
4110      - [X] BitstreamRecorder_write
4111      - [X] BitstreamRecorder_write_signed
4112      - [X] BitstreamRecorder_write64
4113      - [X] BitstreamRecorder_write_signed64
4114      - [X] BitstreamRecorder_unary
4115      - [X] BitstreamRecorder_byte_align
4116      - [X] BitstreamRecorder_write_bytes
4117      - [X] BitstreamRecorder_copy
4118      - [X] BitstreamRecorder_split
4119      - [X] bitstream_build
4120      - [X] BitstreamAccumulator_write
4121      - [X] BitstreamAccumulator_write_signed
4122      - [X] BitstreamAccumulator_write64
4123      - [X] BitstreamAccumulator_write_signed64
4124      - [X] BitstreamAccumulator_unary
4125      - [X] BitstreamAccumulator_byte_align
4126      - [X] BitstreamAccumulator_write_bytes
4127***** DONE Ensure close() method calls bs->close_substream()
4128      - [X] BitstreamWriter
4129      - [X] BitstreamRecorder
4130      - [X] BitstreamAccumulator
4131***** DONE Ensure dealloc method calls bs->free()
4132      - [X] BitstreamWriter
4133      - [X] BitstreamRecorder
4134      - [X] BitstreamAccumulator
4135***** DONE Don't fclose() file objects from underneath Python file objects
4136      Although we may convert a Python file object to a FILE struct,
4137      we must not fclose that struct out from underneath its parent.
4138****** DONE BitstreamReader_close
4139       - [X] call .close() method on Python file object
4140       - [X] set read methods to raise errors
4141       - [X] return result of .close() method
4142****** DONE BitstreamWriter_close
4143       - [X] flush pending output
4144       - [X] call .close() method on Python file object
4145       - [X] set write methods to raise errors
4146       - [X] return result of .close() method
4147****** DONE BitstreamRecorder_close
4148****** DONE BitstreamAccumulator_close
4149**** DONE Double-check existing C modules for proper free/close usage
4150     - [X] bitstream.c
4151     - [X] decoders/alac.c
4152     - [X] decoders/flac.c
4153     - [X] decoders/mlp.c
4154     - [X] decoders/ogg.c
4155     - [X] decoders/shn.c
4156     - [X] decoders/wavpack.c
4157     - [X] encoders/alac.c
4158     - [X] encoders/flac.c
4159     - [X] encoders/flac_lpc.c
4160     - [X] encoders/shn.c
4161     - [X] encoders/wavpack.c
4162     - [X] mod_bitstream.c
4163     - [X] verify/mpeg.c
4164     - [X] verify/ogg.c
4165*** DONE Ensure that reading/writing closed streams always fails
4166    Once a stream's been closed,
4167    the bitstream should set I/O functions to error generators
4168    and further closes should do nothing.
4169**** DONE Add unit test to bitstream.c
4170**** DONE Add unit test to Python side
4171** DONE Add update_metadata() method to AudioFile
4172   Consider how AudioFile.set_metadata() handles six different cases:
4173
4174   Case 1: adjusting a file's own textual metadata (tracktag)
4175   >>> m = flac1.get_metadata()
4176   >>> m.track_name = u"Fixed Name"
4177   >>> flac1.set_metadata(m)
4178
4179   Case 2: transferring metadata from the same audio type (track2track)
4180   >>> flac1.set_metadata(flac2.get_metadata())
4181
4182   Case 3: transferring metadata from a different audio type (track2track)
4183   >>> flac1.set_metadata(mp3.get_metadata())
4184
4185   Case 4: adjusting a file's own low-level metadata (tracklint)
4186   >>> m = flac1.get_metadata()
4187   >>> m.streaminfo.md5sum = fixed_md5sum
4188   >>> flac1.set_metadata(m)
4189
4190   Case 5: building new metadata from scratch (cd2track)
4191   >>> flac1 = FlacAudio.from_pcm(pcm_reader)
4192   >>> flac1.set_metadata(MetaData(track_name=u"New Name", ...))
4193
4194   Case 6: transferring adjusted metadata from the same audio type
4195   >>> m = flac2.get_metadata()
4196   >>> m.track_name = u"Adjusted Name"
4197   >>> flac1.set_metadata(m)
4198
4199   What happens to stuff like the STREAMINFO block
4200   which contains track length, md5sum, etc.
4201   and is part of FLAC's metadata?
4202
4203   If set_metadata() works naively and doesn't override any values,
4204   case 2 breaks since flac1 suddenly has flac2's track length.
4205
4206   If set_metadata() overrides new metadata with stuff from the current file,
4207   case 4 breaks since the fixed_md5sum gets overridden.
4208
4209   Counting on set_metadata() being smart enough to know what you mean
4210   is a losing proposition for FLAC, M4A and other metadata formats
4211   with embedded non-textual (side) metadata.
4212
4213   One solution is to add a low-level AudioFile.update_metadata() method
4214   which takes only the AudioFile's required metadata type
4215   and leaves side data as-is.  So case 1 becomes:
4216
4217   >>> m = flac1.get_metadata()
4218   >>> m.track_name = u"Fixed Name"
4219   >>> flac1.update_metadata(m)
4220
4221   and case 4 becomes:
4222
4223   >>> m = flac1.get_metadata()
4224   >>> m.streaminfo.md5sum = fixed_md5sum
4225   >>> flac1.update_metadata(m)
4226
4227   while the other cases remain unchanged.
4228*** DONE Add update_metadata() method to AudioFile and subclasses
4229    for formats which take metadata at all
4230    - [X] ALACAudio
4231    - [X] AiffAudio
4232    - [X] AudioFile
4233    - [X] FlacAudio
4234    - [X] M4AAudio
4235    - [X] MP2Audio
4236    - [X] MP3Audio
4237    - [X] OggFlacAudio
4238    - [X] VorbisAudio
4239    - [X] WavPackAudio
4240*** DONE Update clean() method to use update_metadata()
4241    for formats which implement clean() at all
4242    - [X] ALACAudio
4243    - [X] AiffAudio
4244    - [X] FlacAudio
4245    - [X] M4AAudio
4246    - [X] MP2Audio
4247    - [X] MP3Audio
4248    - [X] OggFlacAudio
4249    - [X] VorbisAudio
4250    - [X] WavPackAudio
4251*** DONE Add update_metadata() to tracktag
4252    when not using -r/--replace option
4253*** DONE Document new set_metadata() behavior
4254*** DONE Document update_metadata() method
4255*** DONE Add unit tests for update_metadata()
4256    - [X] ALACAudio
4257    - [X] AiffAudio
4258    - [X] FlacAudio
4259    - [X] M4AAudio
4260    - [X] MP2Audio
4261    - [X] MP3Audio
4262    - [X] OggFlacAudio
4263    - [X] VorbisAudio
4264    - [X] WavPackAudio
4265** DONE Ensure Python API docs are updated if necessary
4266   - [X] audiotools.rst
4267   - [X] audiotools_bitstream.rst
4268   - [X] audiotools_cdio.rst
4269   - [X] audiotools_cue.rst
4270   - [X] audiotools_pcm.rst
4271   - [X] audiotools_player.rst
4272   - [X] audiotools_replaygain.rst
4273   - [X] audiotools_resample.rst
4274   - [X] audiotools_toc.rst
4275   - [X] index.rst
4276   - [X] metadata.rst
4277** DONE Spellcheck Python API docs
4278   - [X] audiotools.rst
4279   - [X] audiotools_bitstream.rst
4280   - [X] audiotools_cdio.rst
4281   - [X] audiotools_cue.rst
4282   - [X] audiotools_pcm.rst
4283   - [X] audiotools_player.rst
4284   - [X] audiotools_replaygain.rst
4285   - [X] audiotools_resample.rst
4286   - [X] audiotools_toc.rst
4287   - [X] index.rst
4288   - [X] metadata.rst
4289** DONE Eliminate all compiler warnings
4290   - [X] check Ubuntu
4291   - [X] check Fedora
4292   - [X] check Mac OS X
4293** DONE Reduce load from progress updating routines
4294   Send fewer progress updates from child to parent
4295   when performing progress-based tasks.
4296
4297   This is harder than it sounds.
4298
4299   In an ideal world, I'd have the parent poll its children
4300   on a regular basis and update the progress bars
4301   and/or start more children.
4302
4303   In practice, this doesn't work with subprocesses and shared memory
4304   because shared memory accumulators don't free themselves correctly
4305   when working with more than one child process.
4306
4307   It works even less well with threading because subprocess.Popen
4308   isn't thread-safe and will certainly deadlock if called often enough.
4309
4310   So although the current system is clunky and inefficient
4311   at least it works.
4312** DONE Ensure all Python modules have up-to-date docstrings
4313   - [X] __aiff__.py
4314   - [X] __ape__.py
4315   - [X] __au__.py
4316   - [X] __dvda__.py
4317   - [X] __flac__.py
4318   - [X] __id3__.py
4319   - [X] __id3v1__.py
4320   - [X] __image__.py
4321   - [X] __init__.py
4322   - [X] __m4a__.py
4323   - [X] __m4a_atoms__.py
4324   - [X] __mp3__.py
4325   - [X] __ogg__.py
4326   - [X] __shn__.py
4327   - [X] __vorbis__.py
4328   - [X] __vorbiscomment__.py
4329   - [X] __wav__.py
4330   - [X] __wavpack__.py
4331   - [X] cue.py
4332   - [X] delta.py
4333   - [X] freedb.py
4334   - [X] musicbrainz.py
4335   - [X] player.py
4336   - [X] toc.py
4337   - [X] ui.py
4338** DONE Ensure MANIFEST.in is complete
4339** DONE Update apptest.sh
4340   It's not much of a test, but it's good to keep it around
4341   as a last-resort check of things people normally run.
4342** DONE Run final batch of unit tests on different platforms
4343   - [X] check Ubuntu
4344   - [X] check Fedora
4345   - [X] check Mac OS X
4346* DONE Finish version 2.19
4347** DONE Update PCMReader.read() to take a PCM frame count instead of bytes
4348   Taking a byte count as an argument is a relic from the days
4349   when most conversion happened through external programs.
4350   It's time to update this function to work on PCM frames instead
4351   which is much more natural fit and makes many calculations much easier.
4352*** DONE Update __init__.py
4353    - [X] PCMReader.read
4354    - [X] PCMReaderError.read
4355    - [X] PCMReaderProgress.read
4356    - [X] ReorderedPCMReader.read
4357    - [X] transfer_framelist_data
4358    - [X] threaded_transfer_framelist_data
4359    - [X] pcm_cmp
4360    - [X] pcm_frame_cmp
4361    - [X] PCMCat.read
4362    - [X] __buffer__.__init__
4363    - [X] __buffer__.__len__
4364    - [X] BufferedPCMReader.read
4365    - [X] BufferedPCMReader.__fill__
4366    - [X] LimitedPCMReader.read
4367    - [X] pcm_split
4368    - [X] PCMConverter.read
4369    - [X] ReplayGainReader.read
4370    - [X] calculate_replay_gain
4371    - [X] AudioFile.verify
4372    - [X] PCMReaderWindow.read
4373    - [X] CDTrackReader.read
4374    - [X] CDTrackReaderAccurateRipCRC.read
4375*** DONE Update __aiff__.py
4376    - [X] AiffReader.read
4377    - [X] AiffAudio.from_pcm
4378*** DONE Update __au__.py
4379    - [X] AuReader.read
4380    - [X] AuReader.from_pcm
4381*** DONE Update __flac__.py
4382    - [X] FlacAudio.__eq__
4383    - [X] FLAC_Data_Chunk.write
4384    - [X] FLAC_SSND_Chunk.write
4385*** DONE Update __shn__.py
4386    - [X] ShortenAudio.to_wave
4387    - [X] ShortenAudio.to_aiff
4388*** DONE Update __wav__.py
4389    - [X] WaveReader.read
4390    - [X] WaveAudio.from_pcm
4391    - [X] WaveAudio.add_replay_gain
4392*** DONE Update __wavpack__.py
4393    - [X] WavPackAudio.to_wave
4394*** DONE Update py_decoders/alac.py
4395    - [X] ALACDecoder.read
4396*** DONE Update py_decoders/flac.py
4397    - [X] FlacDecoder.read
4398*** DONE Update py_decoders/shn.py
4399    - [X] SHNDecoder.read
4400*** DONE Update py_decoders/wavpack.py
4401    - [X] WavPackDecoder.read
4402*** DONE Update py_encoders/alac.py
4403    - [X] encode_mdat
4404*** DONE Update py_encoders/flac.py
4405    - [X] encode_flac
4406*** DONE Update py_encoders/shn.py
4407    - [X] encode_shn
4408*** DONE Update py_encoders/wavpack.py
4409    - [X] encode_wavpack
4410*** DONE Update src/pcmconv.c
4411    - [X] pcmreader_read
4412    - [X] pcmreader_read (alt version)
4413*** DONE Update src/replaygain.c
4414    - [X] ReplayGainReader_read
4415*** DONE Update src/decoders/sine.c
4416    - [X] Sine_Mono_read
4417    - [X] Sine_Stereo_read
4418    - [X] Sine_Simple_read
4419*** DONE Update test/test.py
4420    - [X] BLANK_PCM_Reader
4421    - [X] RANDOM_PCM_Reader
4422    - [X] EXACT_BLANK_PCM_Reader
4423    - [X] EXACT_SILENCE_PCM_Reader
4424    - [X] EXACT_RANDOM_PCM_Reader
4425    - [X] MD5_Reader
4426    - [X] Variable_Reader
4427    - [X] Join_Reader
4428    - [X] MiniFrameReader
4429*** DONE Update test/test_core.py
4430    - [X] BufferedPCMReader.test_pcm
4431    - [X] LimitedPCMReader.test_read
4432    - [X] PCMReaderWindow.__test_reader__
4433    - [X] PCM_Reader_Multiplexer.read
4434    - [X] TestMultiChannel.__test_assignment__
4435*** DONE Update test/test_formats.py
4436    - [X] ERROR_PCM_Reader.read
4437    - [X] ALACFileTest.__test_reader__
4438    - [X] ALACFileTest.__test_reader_nonalac__
4439    - [X] ALACFileTest.test_streams
4440    - [X] FlacFileTest.test_streams
4441    - [X] FlacFileTest.__test_reader__
4442    - [X] ShortenFileTest.test_streams
4443    - [X] ShortenFileTest.__test_reader__
4444    - [X] WavPackFileTest.__test_reader__
4445*** DONE Update test/test_streams.py
4446    - [X] FrameListReader.read
4447    - [X] MD5Reader.read
4448    - [X] Sine8_Mono.read
4449    - [X] Sine8_Stereo.read
4450    - [X] Simple_Sine.read
4451    - [X] WastedBPS16.read
4452*** DONE Ensure all unit tests pass
4453**** DONE [Lib]
4454     - [X] core
4455     - [X] cuesheet
4456     - [X] freedb
4457     - [X] image
4458     - [X] musicbrainz
4459     - [X] pcm
4460     - [X] bitstream
4461     - [X] replaygain
4462     - [X] resample
4463     - [X] tocfile
4464     - [X] verify
4465     - [X] player
4466**** DONE [Format]
4467     - [X] audiofile
4468     - [X] lossless
4469     - [X] lossy
4470     - [X] aiff
4471     - [X] alac
4472     - [X] au
4473     - [X] dvda
4474     - [X] flac
4475     - [X] m4a
4476     - [X] mp2
4477     - [X] mp3
4478     - [X] oggflac
4479     - [X] shorten
4480     - [X] sines
4481     - [X] vorbis
4482     - [X] wave
4483     - [X] wavpack
4484**** DONE [Metadata]
4485     - [X] metadata
4486     - [X] flac
4487     - [X] wavpack
4488     - [X] id3v1
4489     - [X] id3v2
4490     - [X] vorbis
4491     - [X] m4a
4492**** DONE [Util]
4493     - [X] audiotools_config
4494     - [X] cd2track
4495     - [X] cdinfo
4496     - [X] cdplay
4497     - [X] coverdump
4498     - [X] coverview
4499     - [X] dvda2track
4500     - [X] dvdainfo
4501     - [X] track2cd
4502     - [X] track2track
4503     - [X] trackcat
4504     - [X] trackcmp
4505     - [X] trackinfo
4506     - [X] tracklength
4507     - [X] tracklint
4508     - [X] trackplay
4509     - [X] trackrename
4510     - [X] tracksplit
4511     - [X] tracktag
4512     - [X] trackverify
4513*** DONE Update reference documentation
4514** DONE Remove the big pile of imports from various modules
4515   Only import the stuff we need, when we need it.
4516*** DONE __init__.py
4517*** DONE accuraterip.py
4518*** DONE aiff.py
4519*** DONE ape.py
4520*** DONE au.py
4521*** DONE dvda.py
4522*** DONE flac.py
4523*** DONE id3.py
4524*** DONE id3v1.py
4525*** DONE image.py
4526*** DONE m4a.py
4527*** DONE m4a_atoms.py
4528*** DONE mp3.py
4529*** DONE ogg.py
4530*** DONE shn.py
4531*** DONE vorbis.py
4532*** DONE vorbiscomment.py
4533*** DONE wav.py
4534*** DONE wavpack.py
4535*** DONE cue.py
4536*** DONE delta.py
4537*** DONE freedb.py
4538*** DONE musicbrainz.py
4539*** DONE player.py
4540*** DONE toc.py
4541*** DONE ui.py
4542*** DONE ensure unit tests pass
4543**** DONE [Lib]
4544     - [X] core
4545     - [X] cuesheet
4546     - [X] freedb
4547     - [X] image
4548     - [X] musicbrainz
4549     - [X] pcm
4550     - [X] bitstream
4551     - [X] replaygain
4552     - [X] resample
4553     - [X] tocfile
4554     - [X] verify
4555     - [X] player
4556**** DONE [Format]
4557     - [X] audiofile
4558     - [X] lossless
4559     - [X] lossy
4560     - [X] aiff
4561     - [X] alac
4562     - [X] au
4563     - [X] dvda
4564     - [X] flac
4565     - [X] m4a
4566     - [X] mp2
4567     - [X] mp3
4568     - [X] oggflac
4569     - [X] shorten
4570     - [X] sines
4571     - [X] vorbis
4572     - [X] wave
4573     - [X] wavpack
4574**** DONE [Metadata]
4575     - [X] metadata
4576     - [X] flac
4577     - [X] wavpack
4578     - [X] id3v1
4579     - [X] id3v2
4580     - [X] vorbis
4581     - [X] m4a
4582**** DONE [Util]
4583     - [X] audiotools_config
4584     - [X] cd2track
4585     - [X] cdinfo
4586     - [X] cdplay
4587     - [X] coverdump
4588     - [X] coverview
4589     - [X] dvda2track
4590     - [X] dvdainfo
4591     - [X] track2cd
4592     - [X] track2track
4593     - [X] trackcat
4594     - [X] trackcmp
4595     - [X] trackinfo
4596     - [X] tracklength
4597     - [X] tracklint
4598     - [X] trackplay
4599     - [X] trackrename
4600     - [X] tracksplit
4601     - [X] tracktag
4602     - [X] trackverify
4603** DONE Integrate Filename object into utilities
4604   This object should replace the Messenger.filename classmethod.
4605   It automatically performs filename -> unicode conversion
4606   and, when files are on disk, compares for equality by device/inode.
4607*** DONE Update utilities
4608**** DONE audiotools-config
4609**** DONE cd2track
4610**** DONE coverdump
4611**** DONE coverview
4612**** DONE dvda2track
4613**** DONE dvdainfo
4614**** DONE track2cd
4615**** DONE track2track
4616**** DONE trackcat
4617**** DONE trackcmp
4618**** DONE trackinfo
4619**** DONE tracklint
4620**** DONE trackrename
4621**** DONE tracksplit
4622**** DONE tracktag
4623**** DONE trackverify
4624*** DONE Add documentation for Filename
4625*** DONE Add unit tests for Filename
4626*** DONE Remove Messenger.filename classmethod
4627**** DONE remove mention in documentation
4628*** DONE Ensure all unit tests pass
4629** DONE Sanity check tool inputs/outputs
4630*** DONE Ensure input files are included only once
4631    - [X] track2cd - generate warning
4632    - [X] track2track - generate error
4633    - [X] trackcat - generate warning
4634    - [X] trackcmp - short-circuit same file comparison
4635    - [X] tracklength - generate warning
4636    - [X] tracklint - generate error
4637    - [X] trackrename - generate error
4638    - [X] tracktag - generate error
4639    - [X] trackverify - skip duplicates
4640**** DONE Unit test new behavior
4641    - [X] track2cd
4642    - [X] track2track
4643    - [X] trackcat
4644    - [X] trackcmp
4645    - [X] tracklint
4646    - [X] trackrename
4647    - [X] tracktag
4648    - [X] trackverify
4649*** DONE Ensure input file(s) are different from output file(s)
4650    Overwriting files is okay by default (in the Unix tradition)
4651    but input and output as same file is not and should generate error.
4652    - [X] coverdump
4653    - [X] track2track
4654    - [X] trackcat
4655    - [X] tracksplit
4656**** DONE Unit test new behavior
4657     - [X] coverdump
4658     - [X] track2track
4659     - [X] trackcat
4660     - [X] tracksplit
4661*** DONE Ensure same file isn't written twice by the same utility
4662    This would typically be the result of a misused "--format" argument.
4663    - [X] cd2track
4664    - [X] dvda2track
4665    - [X] track2track
4666    - [X] trackrename
4667    - [X] tracksplit
4668**** DONE unit test new behavior
4669     - [X] cd2track
4670     - [X] track2track
4671     - [X] trackrename
4672     - [X] tracksplit
4673** DONE Ensure progress display doesn't overload terminal with rows
4674   As more cores become commonplace, it's important not to
4675   overload the screen with too many progress rows
4676   in case the number of simultaneous jobs
4677   exceeds the number of terminal rows.
4678** DONE Improve metadata tagging widget
4679   I'm not convinced the current opened/closed bottom window
4680   is the ideal design for editing extended album/track metadata.
4681   Something more similar to a spreadsheet would be ideal
4682   but that's complicated by the serious lack of space
4683   in terminal windows.
4684** DONE Fix ReplayGain to work on files with different sample rates
4685   This should handle a wider array of cases than it does now.
4686*** DONE Update can_add_replay_gain classmethod
4687    It should take a list of tracks and return True
4688    if ReplayGain can be added to them, False if not
4689    which takes the place of applicable_replay_gain
4690    - [X] AudioFile.can_add_replay_gain
4691    - [X] FlacAudio.can_add_replay_gain
4692    - [X] M4AAudio_faac.can_add_replay_gain
4693    - [X] MP3Audio.can_add_replay_gain
4694    - [X] VorbisAudio.can_add_replay_gain
4695    - [X] WaveAudio.can_add_replay_gain
4696    - [X] WavPackAudio.can_add_replay_gain
4697**** DONE update reference documentation
4698**** DONE update unit tests
4699     - [X] test_formats.py  AudioFileTest.test_replay_gain
4700     - [X] test_utils.py    track2track.test_options
4701     - [X] test_utils.py    tracktag.test_options
4702*** DONE Add supports_replay_gain() classmethod
4703    Returns True if the class supports ReplayGain of any kind.
4704    - [X] AudioFile
4705    - [X] FlacAudio
4706    - [X] M4AAudio
4707    - [X] MP2Audio
4708    - [X] MP3Audio
4709    - [X] OggFlacAudio
4710    - [X] VorbisAudio
4711    - [X] WavPackAudio
4712    - [X] WaveAudio
4713**** DONE Add documentation
4714*** DONE Remove audiotools.applicable_replay_gain function
4715    this functionality is shifted to can_add_replay_gain
4716**** DONE remove from utilities
4717     - [X] dvda2track
4718     - [X] track2track
4719     - [X] tracksplit
4720**** DONE update reference documentation
4721**** DONE update unit tests
4722*** DONE Update calculate_replay_gain function
4723**** DONE tracks with unsupported sample rates should be resampled
4724     according to the nearest supported sample rate available
4725**** DONE tracks with different sample rates should be resampled
4726     according to the most common sample rate available
4727**** DONE tracks with more than two channels should be culled
4728     remove any channels above the first two during calculation
4729**** DONE update unit tests
4730     - [X] test_core.py  TestReplayGain.test_basics
4731*** DONE Update add_replay_gain classmethods as needed
4732    - [X] AudioFile.add_replay_gain
4733    - [X] FlacAudio.add_replay_gain
4734    - [X] M4AAudio_faac.add_replay_gain
4735    - [X] MP3Audio.add_replay_gain
4736    - [X] VorbisAudio.add_replay_gain
4737    - [X] WaveAudio.add_replay_gain
4738    - [X] WavPackAudio.add_replay_gain
4739*** DONE Update utilities to use new ReplayGain application procedure
4740    Given a list of tracks for a given album,
4741    if all are the same format and can_add_replay_gain returns True,
4742    queue a call to add_replay_gain on those tracks.
4743**** DONE audiotools-config
4744**** DONE cd2track
4745**** DONE dvda2track
4746**** DONE track2track
4747**** DONE tracksplit
4748**** DONE tracktag
4749** DONE Display X/Y progress during operations
4750   When a track is finished transcoding, for instance, output:
4751   [ 2 / 15 ] input.wav -> output.mp3
4752   or something similar.
4753*** DONE Update utilities
4754    - [X] cd2track
4755    - [X] dvda2track
4756    - [X] track2track
4757    - [X] trackcmp
4758    - [X] trackrename
4759    - [X] tracksplit
4760    - [X] tracktag
4761    - [X] trackverify
4762*** DONE Update unit tests
4763    - [X] cd2track
4764    - [X] dvda2track
4765    - [X] track2track
4766    - [X] trackcmp
4767    - [X] trackrename
4768    - [X] tracksplit
4769    - [X] tracktag
4770    - [X] trackverify
4771** DONE Update metadata documentation with examples
4772*** DONE FLAC
4773*** DONE MP3
4774    - [X] ID3v1/ID3v1.1
4775    - [X] ID3v2.2
4776    - [X] ID3v2.3
4777    - [X] ID3v2.4
4778*** DONE APEv2
4779*** DONE M4A
4780** DONE Remove Construct module
4781   Convert all usage of Construct to BitstreamReader/BitstreamWriter.
4782   Since this is unlikely to be updated for Python 3,
4783   removing it should smooth that inevitable transition.
4784*** DONE __accuraterip__.py
4785*** DONE __aiff__.py
4786    - [X] chunks
4787    - [X] get_metadata
4788    - [X] set_metadata
4789    - [X] delete_metadata
4790    - [X] to_pcm
4791    - [X] from_pcm
4792    - [X] pcm_split
4793    - [X] aiff_from_chunks
4794**** DONE AiffReader
4795     - [X] __init__
4796     - [X] read
4797*** DONE __ape__.py
4798*** DONE __au__.py
4799*** DONE cue.py
4800*** DONE __dvda__.py
4801**** DONE DVDAudio
4802     - [X] __titlesets__
4803     - [X] __titles__
4804**** DONE DVDATitle
4805     - [X] __parse_info__
4806*** DONE __flac__.py
4807*** DONE __freedb__.py
4808*** DONE __id3__.py
4809*** DONE __id3v1__.py
4810*** DONE __image__.py
4811    - [X] __JPEG__
4812    - [X] __PNG__
4813    - [X] __GIF__
4814    - [X] __BMP__
4815    - [X] __TIFF__
4816**** DONE check these against PIL's output
4817**** DONE check against truncated images
4818*** DONE __init__.py
4819*** DONE __m4a__.py
4820**** DONE M4ATaggedAudio
4821     - [X] get_metadata
4822     - [X] set_metadata
4823     - [X] delete_metadata
4824**** DONE M4A_META_Atom
4825     - [X] __init__
4826     - [X] __repr__
4827     - [X] parse
4828     - [X] __getattr__
4829     - [X] __setattr__
4830     - [X] __delattr__
4831     - [X] images
4832     - [X] add_image
4833     - [X] delete_image
4834     - [X] converted
4835     - [X] __comment_name__
4836     - [X] supports_images
4837     - [X] __by_pair__
4838     - [X] __comment_pairs__
4839     - [X] clean
4840**** DONE M4AAudio_faac
4841     - [X] __init__
4842     - [X] is_type
4843**** DONE ALACAudio
4844     - [X] __init__
4845     - [X] is_type
4846     - [X] to_pcm
4847     - [X] from_pcm
4848     - [X] __ftyp_atom__
4849     - [X] __moov_atom__
4850     - [X] __free_atom__
4851*** DONE __m4a_atoms__.py
4852    Re-implement atom parsing/building without Construct
4853**** DONE ftyp
4854**** DONE moov
4855***** DONE mvhd
4856***** DONE trak
4857****** DONE tkhd
4858****** DONE mdia
4859******* DONE mdhd
4860******* DONE hdlr
4861******* DONE minf
4862******** DONE smhd
4863******** DONE dinf
4864********* DONE dref
4865******** DONE stbl
4866********* DONE stsd
4867********** DONE alac
4868********* DONE stts
4869********* DONE stsc
4870********* DONE stsz
4871********* DONE stco
4872***** DONE udta
4873****** DONE meta
4874**** DONE free
4875**** DONE Rename __m4a_atoms2__.py to __m4a_atoms__.py
4876*** DONE __mp3__.py
4877**** DONE MP3
4878     - [X] __init__
4879     - [X] is_type
4880     - [X] __find_next_mp3_frame__
4881     - [X] __find_mp3_start__
4882     - [X] __find_last_mp3_frame__
4883     - [X] verify
4884**** DONE MP2
4885     - [X] is_type
4886*** DONE __musepack__.py
4887*** DONE __musicbrainz__.py
4888*** DONE __ogg__.py
4889*** DONE player.py
4890*** DONE __shn__.py
4891*** DONE toc.py
4892*** DONE __vorbis__.py
4893**** DONE VorbisAudio
4894     - [X] __read_metadata__
4895     - [X] total_frames
4896     - [X] get_metadata
4897     - [X] set_metadata
4898**** DONE Remove cruft
4899     - [X] OggStreamReader
4900     - [X] OggStreamWriter
4901*** DONE __vorbiscomment__.py
4902*** DONE __wav__.py
4903*** DONE __wavpack__.py
4904*** DONE test/test_core.py
4905**** DONE TestFrameList
4906     - [X] test_8bit_roundtrip
4907     - [X] test_16bit_roundtrip
4908     - [X] test_24bit_roundtrip
4909*** DONE test/test_formats.py
4910**** DONE ALACFileTest
4911     - [X] test_blocksizes
4912**** DONE FlacFileTest
4913     - [X] test_blocksizes
4914**** DONE ShortenFileTest
4915     - [X] test_blocksizes
4916**** DONE WavpackFileTest
4917     - [X] test_blocksizes
4918** DONE Replace gettext with string constants
4919   Transforming _(u"some text") to SOME_TEXT
4920   where SOME_TEXT is a predefined unicode string
4921   in an audiotools sub-module.
4922   This makes text more consistent *and* easier to modify.
4923*** DONE Update utilities
4924    - [X] audiotools-config
4925    - [X] cd2track
4926    - [X] cdinfo
4927    - [X] cdplay
4928    - [X] coverdump
4929    - [X] coverview
4930    - [X] dvda2track
4931    - [X] dvdainfo
4932    - [X] track2cd
4933    - [X] track2track
4934    - [X] trackcat
4935    - [X] trackcmp
4936    - [X] trackinfo
4937    - [X] tracklength
4938    - [X] tracklint
4939    - [X] trackplay
4940    - [X] trackrename
4941    - [X] tracksplit
4942    - [X] tracktag
4943    - [X] trackverify
4944*** DONE Update modules
4945    - [X] __init__.py
4946    - [X] aiff.py
4947    - [X] ape.py
4948    - [X] au.py
4949    - [X] cue.py
4950    - [X] dvda.py
4951    - [X] flac.py
4952    - [X] image.py
4953    - [X] m4a.py
4954    - [X] m4a_atoms.py
4955    - [X] mp3.py
4956    - [X] ogg.py
4957    - [X] toc.py
4958    - [X] ui.py
4959    - [X] vorbis.py
4960    - [X] vorbiscomment.py
4961    - [X] wav.py
4962    - [X] wavpack.py
4963*** DONE Remove gettext
4964    - [X] audiotools-config
4965    - [X] cd2track
4966    - [X] cdinfo
4967    - [X] cdplay
4968    - [X] coverdump
4969    - [X] coverview
4970    - [X] dvda2track
4971    - [X] dvdainfo
4972    - [X] track2cd
4973    - [X] track2track
4974    - [X] trackcat
4975    - [X] trackcmp
4976    - [X] trackinfo
4977    - [X] tracklength
4978    - [X] tracklint
4979    - [X] trackplay
4980    - [X] trackrename
4981    - [X] tracksplit
4982    - [X] tracktag
4983    - [X] trackverify
4984    - [X] audiotools/__init__.py
4985    - [X] audiotools/aiff.py
4986    - [X] audiotools/ape.py
4987    - [X] audiotools/au.py
4988    - [X] audiotools/cue.py
4989    - [X] audiotools/flac.py
4990    - [X] audiotools/id3.py
4991    - [X] audiotools/image.py
4992    - [X] audiotools/m4a.py
4993    - [X] audiotools/mp3.py
4994    - [X] audiotools/toc.py
4995    - [X] audiotools/vorbis.py
4996    - [X] audiotools/wav.py
4997    - [X] audiotools/wavpack.py
4998*** DONE Update unit tests
4999    - [X] test_formats.py
5000    - [X] test_metadata.py
5001    - [X] test_utils.py
5002*** DONE Reduce big import chunks
5003    Convert "from audiotools.text import (CONSTANT, ...)"
5004    to "import audiotools.text as t" and "t.CONSTANT"
5005    to avoid having huge import blocks at the start of utilities.
5006    - [X] audiotools-config
5007    - [X] cd2track
5008    - [X] cdinfo
5009    - [X] cdplay
5010    - [X] coverdump
5011    - [X] coverview
5012    - [X] dvda2track
5013    - [X] dvdainfo
5014    - [X] track2cd
5015    - [X] track2track
5016    - [X] trackcat
5017    - [X] trackcmp
5018    - [X] trackinfo
5019    - [X] tracklength
5020    - [X] tracklint
5021    - [X] trackplay
5022    - [X] trackrename
5023    - [X] tracksplit
5024    - [X] tracktag
5025    - [X] trackverify
5026*** DONE Ensure unit tests pass
5027** DONE Update C-based ReplayGainReader
5028   Use a lot of the new C-based facilities to make it simpler.
5029** DONE Fix --number=0 argument to tracktag
5030   I'd like to make track_number=0 a valid value
5031   and have track_number=None indicate a missing field.
5032   The problem is maintaining consistency between
5033   metadata formats that store track numbers as text (like ID3v2)
5034   and those that store track numbers as integers (like M4A).
5035   - [X] MetaData
5036   - [X] ApeTag
5037   - [X] FlacMetaData
5038   - [X] ID3CommentPair
5039   - [X] ID3v1Comment
5040   - [X] ID3v22Comment
5041   - [X] ID3v23Comment
5042   - [X] ID3v24Comment
5043   - [X] M4A_META_Atom
5044   - [X] VorbisComment
5045*** DONE Update utilities
5046*** DONE Update documentation
5047*** DONE Update unit tests
5048**** DONE Add getitem/setitem/getattr/setattr/delattr tests to metadata
5049     - [X] flac
5050     - [X] wavpack
5051     - [X] id3v1
5052     - [X] id3v2
5053     - [X] vorbis
5054     - [X] m4a
5055** DONE Remove Python Imaging Library requirement
5056   This is only used for thumbnailing
5057   and for TKinter-based image display.
5058   Since a Python3 version doesn't seem to be pending,
5059   it would be better to remove the requirement.
5060*** DONE Remove thumbnail functions from audiotools.image
5061    - [X] can_thumbnail
5062    - [X] thumbnail_formats
5063    - [X] thumbnail_image
5064*** DONE Remove thumbnail method from audiotools.Image
5065*** DONE Remove thumbnail config options
5066    - [X] audiotools.THUMBNAIL_FORMAT
5067    - [X] audiotools.THUMBNAIL_SIZE
5068*** DONE Update utilities
5069    - [X] audiotools-config
5070    - [X] track2track
5071    - [X] tracktag
5072*** DONE Update utility man pages
5073    - [X] audiotools-config
5074    - [X] track2track
5075    - [X] tracktag
5076*** DONE Update programming documentation
5077*** DONE Update unit tests
5078** DONE Update cuesheet interface to handle non-CD sheets
5079   Though rare, non-CD audio is sometimes combined
5080   with cuesheets and should be handled properly
5081   in those instances.
5082*** DONE Add sample_rate field to cuesheet pcm_lengths() method
5083    - [X] Cuesheet.pcm_lengths
5084    - [X] Flac_CUESHEET.pcm_lengths
5085    - [X] TOCFile.pcm_lengths
5086**** DONE Update documentation
5087**** DONE Update unit tests
5088*** DONE Remove sheet_to_unicode function
5089*** DONE Update utilities to use sample_rate field with pcm_lengths
5090    - [X] trackinfo
5091    - [X] tracksplit
5092    - [X] tracktag
5093*** DONE Ensure unit tests pass
5094** DONE Add faster and more accurate audio type identifier
5095   Instead of checking open files on a format-by-format basis
5096   to determine its type via looping, it's more effecient
5097   to check for all possible file types from the same stream simultaneously
5098   via a sort of finite automata.
5099*** DONE Add file_type function
5100    - [X] ALACAudio
5101    - [X] AiffAudio
5102    - [X] AuAudio
5103    - [X] FlacAudio - without ID3v2 tag
5104    - [X] M4AAudio
5105    - [X] MP2Audio - without ID3v2 tag
5106    - [X] MP3Audio - without ID3v2 tag
5107    - [X] OggFlacAudio
5108    - [X] ShortenAudio
5109    - [X] VorbisAudio
5110    - [X] WavPackAudio
5111    - [X] WaveAudio
5112    - [X] FlacAudio - with ID3v2 tag
5113    - [X] MP2Audio - with ID3v2 tag
5114    - [X] MP3Audio - with ID3v2 tag
5115*** DONE Update functions which use is_type method to use file_type function
5116    - [X] trackverify
5117    - [X] audiotools.open
5118*** DONE Remove is_type method from AudioFile classes
5119    - [X] ALACAudio
5120    - [X] AiffAudio
5121    - [X] AuAudio
5122    - [X] FlacAudio
5123    - [X] M4AAudio
5124    - [X] MP2Audio
5125    - [X] MP3Audio
5126    - [X] OggFlacAudio
5127    - [X] ShortenAudio
5128    - [X] VorbisAudio
5129    - [X] WavPackAudio
5130    - [X] WaveAudio
5131*** DONE Update programming documentation
5132*** DONE Update unit tests
5133    - [X] test_formats.py
5134** DONE Update utilities to handle broken track_name template fields
5135   - [X] cd2track
5136   - [X] dvda2track
5137   - [X] track2track
5138   - [X] trackrename
5139   - [X] tracksplit
5140*** DONE Add unit tests for broken track_name template fields
5141** DONE Split off tracktag's functionality
5142*** DONE Move the image options into a specialized tool (covertag?)
5143    Its interactive mode should use PyGTK/Tkinter
5144    since it's helpful to see the images one is selecting to add/remove.
5145    - [X] --remove-images
5146    - [X] --front-cover
5147    - [X] --back-cover
5148    - [X] --leaflet
5149    - [X] --media
5150    - [X] --other-image
5151    - [X] -T, --thumbnail
5152*** DONE Move CD options into a specialized tool
5153    Something for tagging a group of tracks as if they're an entire CD
5154    - [X] --cue
5155    - [X] -l, --lookup
5156    - [X] --musicbrainz-server
5157    - [X] --musicbrainz-port
5158    - [X] --no-musicbrainz
5159    - [X] --freedb-server
5160    - [X] --freedb-port
5161    - [X] --no-freedb
5162** DONE Add interative mode to audiotools-config
5163   This is one instance where seeing all the options "up-front"
5164   is likely to make the process easier.
5165** DONE Add Python-based file decoders
5166   These would be low-performance, Python-based PCMReader-style objects
5167   demonstrating how the decoding process works in a relatively simple
5168   manner through the use of the BitstreamReader objects.
5169   They would also provide reference implementations.
5170*** DONE py_decoders/FlacDecoder
5171*** DONE py_decoders/SHNDecoder
5172*** DONE py_decoders/ALACDecoder
5173*** DONE py_decoders/WavPackDecoder
5174** DONE Update .convert() method to use fewer temporary files
5175   .wav and .aiff containers with embedded chunks
5176   currently route data through actual .wav/.aiff files
5177   in order to pass those chunks to another format.
5178   It would be better to avoid creating an intermediate
5179   file whenever possible.
5180*** DONE Update wav containers to use new interface
5181    WaveContainer.has_foreign_wav_chunks()
5182    returns True if the instance has chunks to convert
5183    WaveContainer.header_footer()
5184    returns (header, footer) binary strings
5185    WaveContainer.from_wave(header, pcmreader, footer)
5186    returns new instance built from heaeder, PCM data and footer
5187
5188    Once interface is in place, .convert() can pass header/footer
5189    and wrap progress monitor around pcmreader in order to avoid
5190    temporary wav files.
5191**** DONE Update FlacAudio
5192     - [X] has_foreign_wave_chunks()
5193     - [X] wave_header_footer()
5194     - [X] from_wave(filename, header, pcmreader, footer, compression)
5195     - [X] convert(target_page, target_class, compression, progress)
5196**** DONE Update OggFlacAudio
5197     - [X] has_foreign_wave_chunks()
5198     - [X] wave_header_footer()
5199     - [X] from_wave(filename, header, pcmreader, footer, compression)
5200     - [X] convert(target_page, target_class, compression, progress)
5201**** DONE Update ShortenAudio
5202     - [X] has_foreign_wave_chunks()
5203     - [X] wave_header_footer()
5204     - [X] from_wave(filename, header, pcmreader, footer, compression)
5205     - [X] convert(target_page, target_class, compression, progress)
5206**** DONE Update WavPackAudio
5207     - [X] has_foreign_wave_chunks()
5208     - [X] wave_header_footer()
5209     - [X] from_wave(filename, header, pcmreader, footer, compression)
5210     - [X] convert(target_page, target_class, compression, progress)
5211**** DONE Update WaveAudio
5212     - [X] has_foreign_wave_chunks()
5213     - [X] wave_header_footer()
5214     - [X] from_wave(filename, header, pcmreader, footer, compression)
5215     - [X] convert(target_page, target_class, compression, progress)
5216**** DONE Update programming documentation
5217**** DONE Update unit tests
5218*** DONE Update aiff containers to use new interface
5219    AiffContainer.has_foreign_aiff_chunks()
5220    returns True if the instance has chunks to convert
5221
5222    AiffContainer.header_footer()
5223    returns (header, footer) binary strings
5224
5225    AiffContainer.from_aiff(header, pcmreader, footer)
5226    returns new instance built from heaeder, PCM data and footer
5227
5228    Once interface is in place, .convert() can pass header/footer
5229    and wrap progress monitor around pcmreader in order to avoid
5230    temporary aiff files.
5231**** DONE Update AiffAudio
5232     - [X] has_foreign_aiff_chunks()
5233     - [X] aiff_header_footer()
5234     - [X] from_aiff(filename, header, pcmreader, footer, compression)
5235     - [X] convert(target_page, target_class, compression, progress)
5236**** DONE Update FlacAudio
5237     - [X] has_foreign_aiff_chunks()
5238     - [X] aiff_header_footer()
5239     - [X] from_aiff(filename, header, pcmreader, footer, compression)
5240     - [X] convert(target_page, target_class, compression, progress)
5241**** DONE Update OggFlacAudio
5242     - [X] has_foreign_aiff_chunks()
5243     - [X] aiff_header_footer()
5244     - [X] from_aiff(filename, header, pcmreader, footer, compression)
5245     - [X] convert(target_page, target_class, compression, progress)
5246**** DONE Update ShortenAudio
5247     - [X] has_foreign_aiff_chunks()
5248     - [X] aiff_header_footer()
5249     - [X] from_aiff(filename, header, pcmreader, footer, compression)
5250     - [X] convert(target_page, target_class, compression, progress)
5251**** DONE Update programming documentation
5252**** DONE Update unit tests
5253** DONE Add pause()/resume() methods to low-level output players
5254   This will hopefully make pausing more responsive
5255   and work better than simply emptying the output buffer.
5256** DONE Ensure unicode command-line arguments are parsed properly
5257*** DONE cd2track
5258    - [X] --format
5259    - [X] --dir
5260*** DONE coverdump
5261    - [X] filename arguments
5262    - [X] --dir
5263    - [X] --prefix
5264*** DONE covertag
5265    - [X] filename arguments
5266    - [X] --front-cover
5267    - [X] --back-cover
5268    - [X] --leaflet
5269    - [X] --media
5270    - [X] --other-image
5271*** DONE track2track
5272    - [X] filename arguments
5273    - [X] --dir
5274    - [X] --format
5275    - [X] --output
5276*** DONE trackcat
5277    - [X] filename arguments
5278    - [X] --output
5279    - [X] --cue
5280*** DONE trackcmp
5281    - [X] filename arguments
5282*** DONE trackinfo
5283    - [X] filename arguments
5284*** DONE tracklength
5285    - [X] filename arguments
5286*** DONE tracklint
5287    - [X] filename arguments
5288    - [X] --db
5289*** DONE trackrename
5290    - [X] filename arguments
5291    - [X] --format
5292*** DONE tracksplit
5293    - [X] filename arguments
5294    - [X] --cue
5295    - [X] --dir
5296    - [X] --format
5297*** DONE tracktag
5298    - [X] filename arguments
5299    - [X] --name
5300    - [X] --artist
5301    - [X] --album
5302    - [X] --performer
5303    - [X] --composer
5304    - [X] --conductor
5305    - [X] --catalog
5306    - [X] --ISRC
5307    - [X] --publisher
5308    - [X] --media-type
5309    - [X] --year
5310    - [X] --date
5311    - [X] --copyright
5312    - [X] --comment
5313    - [X] --comment-file
5314** DONE Add unit tests for Python-based decoders
5315   Since Python-based codecs are so much slower
5316   it's impossible to make these as comprehensive as the C-based tests.
5317   So we'll only be able to test the basics using very small streams
5318   against the output of the C-based decoders.
5319*** DONE FlacDecoder
5320*** DONE ALACDecoder
5321*** DONE WavPackDecoder
5322*** DONE SHNDecoder
5323** DONE Add unit tests for Python-based encoders
5324*** DONE encode_flac
5325*** DONE encode_mdat
5326*** DONE encode_wavpack
5327*** DONE encode_shn
5328** DONE Ensure Python files are PEP8-compliant
5329*** DONE executables
5330    - [X] audiotools-config
5331    - [X] cd2track
5332    - [X] cdinfo
5333    - [X] cdplay
5334    - [X] coverdump
5335    - [X] covertag
5336    - [X] coverview
5337    - [X] dvda2track
5338    - [X] dvdainfo
5339    - [X] track2cd
5340    - [X] track2track
5341    - [X] trackcat
5342    - [X] trackcmp
5343    - [X] trackinfo
5344    - [X] tracklength
5345    - [X] tracklint
5346    - [X] trackplay
5347    - [X] trackrename
5348    - [X] tracksplit
5349    - [X] tracktag
5350    - [X] trackverify
5351*** DONE libraries
5352    - [X] __init__.py
5353    - [X] accuraterip.py
5354    - [X] aiff.py
5355    - [X] ape.py
5356    - [X] au.py
5357    - [X] cue.py
5358    - [X] delta.py
5359    - [X] dvda.py
5360    - [X] flac.py
5361    - [X] freedb.py
5362    - [X] id3.py
5363    - [X] id3v1.py
5364    - [X] image.py
5365    - [X] m4a.py
5366    - [X] m4a_atoms.py
5367    - [X] mp3.py
5368    - [X] musicbrainz.py
5369    - [X] ogg.py
5370    - [X] opus.py
5371    - [X] player.py
5372    - [X] shn.py
5373    - [X] text.py
5374    - [X] toc.py
5375    - [X] ui.py
5376    - [X] vorbis.py
5377    - [X] vorbiscomment.py
5378    - [X] wav.py
5379    - [X] wavpack.py
5380**** DONE py_decoders
5381     - [X] __init__.py
5382     - [X] alac.py
5383     - [X] flac.py
5384     - [X] shn.py
5385     - [X] wavpack.py
5386**** DONE py_encoders
5387     - [X] __init__.py
5388     - [X] alac.py
5389     - [X] flac.py
5390     - [X] shn.py
5391     - [X] wavpack.py
5392** DONE Have interactive modes clear screen after finishing
5393   Some systems leave the Urwid-built screens half-cleared
5394   before resuming regular output.
5395   It's preferable to erase the whole thing in that case.
5396   - [X] audiotools-config
5397   - [X] cd2track
5398   - [X] cdplay
5399   - [X] dvda2track
5400   - [X] track2track
5401   - [X] trackcat
5402   - [X] trackplay
5403   - [X] trackrename
5404   - [X] tracksplit
5405   - [X] tracktag
5406** DONE Have interactive modes detect termios.error at launch
5407   This seems to be caused mostly by opening interactive modes
5408   with arguments piped from xargs
5409   - [X] audiotools-config
5410   - [X] cd2track
5411   - [X] cdplay
5412   - [X] dvda2track
5413   - [X] track2track
5414   - [X] trackcat
5415   - [X] trackplay
5416   - [X] trackrename
5417   - [X] tracksplit
5418   - [X] tracktag
5419** DONE Spellcheck programming documentation
5420   - [X] audiotools.rst
5421   - [X] audiotools_bitstream.rst
5422   - [X] audiotools_cdio.rst
5423   - [X] audiotools_cue.rst
5424   - [X] audiotools_pcm.rst
5425   - [X] audiotools_pcmconverter.rst
5426   - [X] audiotools_player.rst
5427   - [X] audiotools_replaygain.rst
5428   - [X] audiotools_toc.rst
5429   - [X] index.rst
5430   - [X] metadata.rst
5431** DONE Spellcheck reference documentation
5432   - [X] aiff.tex
5433   - [X] alac.tex
5434   - [X] ape.tex
5435   - [X] apev2.tex
5436   - [X] au.tex
5437   - [X] basics.tex
5438   - [X] dvda2.tex
5439   - [X] flac.tex
5440   - [X] freedb.tex
5441   - [X] introduction.tex
5442   - [X] license.tex
5443   - [X] m4a.tex
5444   - [X] mp3.tex
5445   - [X] musepack.tex
5446   - [X] musicbrainz.tex
5447   - [X] musicbrainz_mmd.tex
5448   - [X] ogg.tex
5449   - [X] oggflac.tex
5450   - [X] references.tex
5451   - [X] replaygain.tex
5452   - [X] shorten.tex
5453   - [X] speex.tex
5454   - [X] vorbis.tex
5455   - [X] wav.tex
5456   - [X] wavpack.tex
5457** DONE Spellcheck reference figures
5458** DONE Spellcheck manual pages
5459** DONE Split reference documentation into sections by file
5460   That is, massive codecs should be split into individual
5461   .tex files by decode/encode sections and then subdivided
5462   further as necessary to keep them from getting too fragile/unweildy
5463*** DONE Shorten
5464    - [X] decode
5465    - [X] encode
5466*** DONE FLAC
5467**** DONE metadata
5468**** DONE decode
5469**** DONE encode
5470     - [X] fixed
5471     - [X] residual
5472     - [X] lpc
5473*** DONE WavPack
5474**** DONE decode
5475     - [X] terms
5476     - [X] weights
5477     - [X] samples
5478     - [X] entropy
5479     - [X] bitstream
5480     - [X] decorrelation
5481**** DONE encode
5482     - [X] correlation
5483     - [X] terms
5484     - [X] weights
5485     - [X] samples
5486     - [X] entropy
5487     - [X] bitstream
5488*** DONE ALAC
5489**** DONE decode
5490**** DONE encode
5491     - [X] atoms
5492     - [X] lpc
5493     - [X] residual
5494*** DONE Remove m4 requirement
5495    This is massive overkill for what little we need it for,
5496    which is generating audioformats-_.tex and _-codec.tex files
5497    with header/footers included and paper size populated.
5498
5499    It's better to use Python for that trivial templating instead.
5500** DONE Tweak reference documentation layout
5501   Try harder to get pseudocode, file diagram,
5502   bit diagram and example on same pair of pages.
5503*** DONE Shorten
5504*** DONE FLAC
5505*** DONE WavPack
5506*** DONE ALAC
5507*** DONE DVD-A
5508** DONE Ensure documentation flows correctly
5509   Pages should start/end properly at letter and A4 paper sizes.
5510   - [X] letter
5511   - [X] A4
5512** DONE Handle multi-channel .opus files properly
5513*** DONE Add unit tests
5514** DONE Add optional interactive modes to utilities
5515   Now that Urwid is being used for editxmcd, it might be helpful
5516   to add optional console-based interactive modes to the other tools.
5517   This may improve ease-of-use (particularly discoverability
5518   in the case of format and quality options) without sacrificing
5519   scriptability or command-line power.
5520
5521   Just as the command-line options are kept as consistent as possible,
5522   all interactive modes will also need a consistent interface.
5523*** DONE audiotools-config
5524*** DONE cd2track
5525*** DONE cdplay
5526**** DONE make player widget generic
5527*** DONE dvda2track
5528*** DONE track2track
5529*** DONE trackcat
5530*** DONE trackplay
5531**** DONE make player widget generic
5532*** DONE trackrename
5533*** DONE tracksplit
5534*** DONE tracktag
5535** DONE run codecs through valgrind
5536   Try to ensure there's no hidden bugs in the low-level C code.
5537*** DONE decoders
5538    Will need to assemble standalone decoders for this.
5539    - [X] alac
5540    - [X] flac
5541    - [X] mlp
5542    - [X] oggflac
5543    - [X] shorten
5544    - [X] wavpack
5545*** DONE encoders
5546    - [X] alac
5547    - [X] flac
5548    - [X] shorten
5549    - [X] wavpack
5550*** DONE Update standalone encoders to take command-line arguments
5551    This would make them easier to memory test.
5552    - [X] alac
5553    - [X] flac
5554    - [X] shorten
5555    - [X] wavpack
5556** DONE Have cdplay play properly
5557** DONE Update version number to 2.19 final
5558* DONE Finish version 2.20
5559** DONE Remove track number/album number guessing heuristics
5560   Don't try to guess number from filename.
5561   If a track number is needed, build it from the order
5562   in which the files are submitted on the command line.
5563*** DONE track_number()
5564*** DONE album_number()
5565** DONE Update track2track/tracktag to use better option filler widget
5566   cd2track, dvda2track and tracksplit always work with one album
5567   at a time, pretty much by definition.
5568   track2track may handle multiple albums simultaneously
5569   and currently makes mutiple calls to urwid to work.
5570   A better approach is to have all the metadata lookups
5571   performed first, multiple metadata edit screens,
5572   and a single output options/preview screen.
5573** DONE Simplify PCMReaderWindow
5574   Split this into a PCMReaderHead and PCMReaderDeHead pair.
5575
5576   The former handles the "pcm_frames" argument,
5577   either truncating a PCMReader or appending silence as needed.
5578
5579   The latter handles the "initial_offset" argument,
5580   either chopping off frames or prepending silence as needed.
5581** DONE Adjust BitstreamReader to better handle huge byte counts
5582   These are typically done in error and shouldn't eat up
5583   all the RAM in the world before failing.
5584*** DONE Update functions
5585    - [X]  br_substream_append_f
5586    - [X]  br_substream_append_e
5587    - [X]  BitstreamReader_read_bytes
5588    - [X]  BitstreamReader_substream_meth
5589    - [X]  BitstreamReader_substream_append
5590*** DONE Add unit tests
5591    - [X]  br_substream_append_f
5592    - [X]  br_substream_append_e
5593    - [X]  BitstreamReader_read_bytes
5594    - [X]  BitstreamReader_substream_meth
5595    - [X]  BitstreamReader_substream_append
5596*** DONE turn the current buf_append into buf_extend
5597*** DONE Replace buf_extend with buf_append
5598** DONE Have process-based decoders exit when data runs out
5599   That is, have PCMReader's .read() call subprocess.wait()
5600   when the framelists end rather than waiting for .close()
5601   to be called (which typically isn't).
5602   If the wait fails, have it raise an exception at that point.
5603*** DONE Update process-based formats
5604    - [X] M4AAudio
5605    - [X] MP2Audio
5606    - [X] MP3Audio
5607    - [X] OpusAudio
5608*** DONE Update documentation
5609*** DONE Ensure unit tests pass
5610** DONE Reduce stdint usage in bitstream library
5611   Try to limit stdint variables to places
5612   where it's necessary, like read_64 and read_bytes
5613*** DONE Convert state/context to state_t
5614**** DONE Update br_huffman_table
5615**** DONE Update BitstreamReader
5616**** DONE struct read_bits
5617**** DONE struct unread_bit
5618**** DONE struct read_unary
5619**** DONE struct read_limited_unary
5620*** DONE Update jump table structures
5621**** DONE read_bits
5622**** DONE unread_bit
5623**** DONE read_unary
5624**** DONE read_limited_unary
5625*** DONE Update br_huffman_table
5626**** DONE Update continue_ to int
5627*** DONE Update bs_buffer
5628**** DONE unsigned data_size
5629**** DONE unsigned window_start
5630**** DONE unsigned window_end
5631**** DONE buf_resize()
5632**** DONE buf_write()
5633**** DONE buf_read()
5634*** DONE Update br_mark
5635**** DONE unsigned substream
5636**** DONE unsigned external
5637*** DONE Update substream_append()
5638**** DONE br_substream_append_f
5639**** DONE br_substream_append_s
5640**** DONE br_substream_append_e
5641**** DONE br_substream_append_c
5642*** DONE Update bw_external_output
5643**** DONE buffer_size
5644**** DONE bw_open_external()
5645**** DONE ext_open_w()
5646** DONE Add bs_buffer-specific unit tests to bitstream.c
5647   - [X] buf_resize
5648   - [X] buf_write
5649   - [X] buf_read
5650   - [X] buf_copy
5651   - [X] buf_extend
5652   - [X] buf_reset
5653   - [X] buf_getc
5654   - [X] buf_putc
5655   - [X] buf_getpos
5656   - [X] buf_setpos
5657   - [X] buf_set_rewindable
5658   - [X] BUF_WINDOW_SIZE
5659   - [X] BUF_WINDOW_START
5660   - [X] BUF_WINDOW_END
5661** DONE Fix cuesheet handling
5662   Ensure the cuesheet interface handles unusual cases more accurately
5663   and build a proper base class for the .cue and .toc files
5664*** DONE Add new base classes
5665    - [X] Sheet
5666    - [X] SheetTrack
5667    - [X] SheetIndex
5668*** DONE Update cue module
5669    - [X] read_cuesheet
5670    - [X] write_cuesheet
5671*** DONE Update toc module
5672    - [X] read_tocfile
5673    - [X] write_tocfile
5674*** DONE Update get_cuesheet methods
5675    - [X] FlacAudio
5676    - [X] TrueAudio
5677    - [X] WavPackAudio
5678*** DONE Update set_cuesheet methods
5679    - [X] FlacAudio
5680    - [X] TrueAudio
5681    - [X] WavPackAudio
5682*** DONE Update documentation
5683    - [X] read_sheet
5684    - [X] Sheet
5685    - [X] SheetTrack
5686    - [X] SheetIndex
5687*** DONE Update utilities using cuesheet interface
5688    - [X] track2cd
5689    - [X] trackcat
5690    - [X] trackinfo
5691    - [X] tracksplit
5692*** DONE Update unit tests
5693*** DONE Add unit test for FlacAudio.set_cuesheet()
5694    Ensure the resulting Flac_CUESHEET block matches
5695    the one generated by metaflac --import-cuesheet-from
5696** DONE Add optional total_pcm_frames argument to from_pcm()
5697   Certain formats encode much easier if the total number of PCM frames
5698   is known in advance.  Adding a total_pcm_frames argument
5699   allows encoders to make use of that info if it's available.
5700*** DONE Update formats with total_pcm_frames argument
5701**** DONE AudioFile
5702     does nothing
5703**** DONE ALACAudio
5704     Builds placeholder seektable which is then populated later
5705**** DONE AiffAudio
5706     does nothing
5707**** DONE AuAudio
5708     does nothing
5709**** DONE FlacAudio
5710     Allocates enough space for populated seektable
5711     on top of any leftover for VORBIS tags.
5712**** DONE M4AAudio
5713     does nothing
5714**** DONE MP2Audio
5715     does nothing
5716**** DONE MP3Audio
5717     does nothing
5718**** DONE OggFlacAudio
5719     does nothing
5720**** DONE OpusAudio
5721     does nothing
5722**** DONE ShortenAudio
5723     If total_pcm_frames argument is given to from_pcm(),
5724     precalculate Wave header rather than use temporary file.
5725     This shouldn't require updating the encode_shn function.
5726**** DONE TrueAudio
5727     If total_pcm_frames argument is given to from_pcm(),
5728     fill header and temporary seektable rather than use temporary file.
5729     This shouldn't require updating the encode_tta function.
5730**** DONE VorbisAudio
5731     does nothing
5732**** DONE WavPackAudio
5733     If total_pcm_frames argument is given to from_pcm(),
5734     fill blocks during encoding rather than re-fill them afterward.
5735     This will require minor update to encode_wavpack function.
5736**** DONE WaveAudio
5737     does nothing
5738*** DONE Update convert() methods to call from_pcm() with argument
5739    But only if the input file is lossless.
5740**** DONE AudioFile
5741**** DONE ALACAudio
5742**** DONE AiffAudio
5743**** DONE AuAudio
5744**** DONE FlacAudio
5745**** DONE M4AAudio
5746**** DONE MP2Audio
5747**** DONE MP3Audio
5748**** DONE OggFlacAudio
5749**** DONE OpusAudio
5750**** DONE ShortenAudio
5751**** DONE TrueAudio
5752**** DONE VorbisAudio
5753**** DONE WavPackAudio
5754**** DONE WaveAudio
5755*** DONE Update utilities to make use of total_pcm_frames argument
5756**** DONE cd2track
5757**** DONE dvda2track
5758**** DONE trackcat
5759**** DONE tracksplit
5760*** DONE Document total_pcm_frames argument
5761*** DONE Unit test calling formats with total_pcm_frames and without
5762*** DONE Add unit tests with total_pcm_frames and without per encoder
5763    - [X]  ALACAudio.from_pcm()
5764    - [X]  FlacAudio.from_pcm()
5765    - [X]  ShnAudio.from_pcm()
5766    - [X]  TTAAudio.from_pcm()
5767    - [X]  WavPackAudio.from_pcm()
5768** DONE Make decoder streams seekable
5769   PCMReader.seek(pcm_frames) -> new pcm frames position
5770   could seek to the closest position in the stream to the given frames
5771   without going over, and return the current position in PCM frames.
5772*** DONE Update decoders
5773**** DONE AiffAudio
5774**** DONE AuAudio
5775**** DONE WaveAudio
5776**** DONE decoders.ALACDecoder
5777**** DONE decoders.FlacDecoder
5778**** DONE decoders.TTADecoder
5779**** DONE decoders.WavPackDecoder
5780*** DONE Add unit tests
5781** DONE Swap array types for something easier to comprehend
5782   array_i/array_f/array_u is more opaque than it should be
5783*** DONE array_i         -> a_int
5784*** DONE array_i_new()   -> a_int_new()
5785    - [X] decoders/alac.h/.c
5786    - [X] decoders/flac.h/.c
5787    - [X] decoders/mlp.h/.c
5788    - [X] decoders/oggflac.h/.c
5789    - [X] decoders/shn.h/.c
5790    - [X] decoders/tta.h/.c
5791    - [X] decoders/wavpack.h/.c
5792    - [X] encoders/alac.h/.c
5793    - [X] encoders/flac.h/.c
5794    - [X] encoders/shn.h/.c
5795    - [X] encoders/tta.h/.c
5796    - [X] encoders/wavpack.h/.c
5797    - [X] pcmconverter.h/.c
5798*** DONE array_ia        -> aa_int
5799*** DONE array_ia_new()  -> aa_int_new()
5800    - [X] decoders/alac.h/.c
5801    - [X] decoders/aob.h/.c
5802    - [X] decoders/flac.h/.c
5803    - [X] decoders/mlp.h/.c
5804    - [X] decoders/oggflac.h/.c
5805    - [X] decoders/shn.h/.c
5806    - [X] decoders/sine.h/.c
5807    - [X] decoders/tta.h/.c
5808    - [X] decoders/wavpack.h/.c
5809    - [X] encoders/alac.h/.c
5810    - [X] encoders/flac.h/.c
5811    - [X] encoders/shn.h/.c
5812    - [X] encoders/tta.h/.c
5813    - [X] encoders/wavpack.h/.c
5814    - [X] pcmconverter.h/.c
5815    - [X] replaygain.h/.c
5816*** DONE array_li        -> l_int
5817*** DONE array_li_new()  -> l_int_new()
5818    - [X] encoders/flac.h/.c
5819*** DONE array_lia       -> al_int
5820*** DONE array_lia_new() -> al_int_new()
5821    - [X] encoders/flac.h/.c
5822    - [X] pcmconverter.h/.c
5823*** DONE array_u         -> l_unsigned
5824*** DONE array_u_new     -> l_unsigned_new()
5825    - [X] decoders/alac.c
5826    - [X] encoders/alac.c
5827*** DONE array_lf        -> l_double
5828*** DONE array_lu        -> l_unsigned
5829*** DONE array_lu_new()  -> l_unsigned_new()
5830    - [X] decoders/alac.h/.c
5831*** DONE array_lfa       -> al_double
5832*** DONE array_lfa_new() -> al_double_new()
5833*** DONE array_iaa       -> aaa_int
5834*** DONE array_iaa_new() -> aaa_int_new()
5835    - [X] encoders/wavpack.h/.c
5836    - [X] decoders/wavpach.h/.c
5837*** DONE array_f         -> a_double
5838*** DONE array_f_new()   -> a_double_new()
5839    - [X] pcmconverter.h/.c
5840    - [X] encoders/alac.h/.c
5841    - [X] encoders/flac.h/.c
5842*** DONE array_fa        -> aa_double
5843*** DONE array_fa_new()  -> aa_double_new()
5844    - [X] replaygain.h/.c
5845    - [X] encoders/alac.h/.c
5846    - [X] encoders/flac.h/.c
5847*** DONE array_faa       -> aaa_double
5848*** DONE array_faa_new() -> aaa_double_new()
5849*** DONE array_o         -> a_obj
5850*** DONE array_o_new()   -> a_obj_new()
5851    - [X] decoders/alac.h/.c
5852    - [X] decoders/aob.h/.c
5853    - [X] decoders/flac.h/.c
5854    - [X] encoders/wavpack.h/.c
5855*** DONE array_i_to_FrameList  -> a_int_to_FrameList
5856    - [X] decoders/flac.c
5857    - [X] decoders/oggflac.c
5858    - [X] pcmconv.c
5859    - [X] pcmconverter.c
5860*** DONE array_ia_to_FrameList -> aa_int_to_FrameList
5861    - [X] decoders/alac.c
5862    - [X] decoders/aob.c
5863    - [X] decoders/shn.c
5864    - [X] decoders/sine.c
5865    - [X] decoders/tta.c
5866    - [X] decoders/wavpack.c
5867    - [X] pcmconv.c
5868    - [X] pcmconverter.c
5869    - [X] replaygain.c
5870** DONE Better document M4A atoms
5871   For each atom, indicate parent in bit diagram,
5872   tell what it's for, have a hex diagram example
5873   and show what those parsed values are.
5874*** DONE ftyp
5875*** DONE moov
5876**** DONE mvhd
5877**** DONE trak
5878***** DONE tkhd
5879***** DONE mdia
5880****** DONE mdhd
5881****** DONE hdlr
5882****** DONE minf
5883******* DONE smhd
5884******* DONE dinf
5885******** DONE dref
5886******* DONE stbl
5887******** DONE stsd
5888******** DONE stts
5889******** DONE stsc
5890******** DONE stsz
5891******** DONE stco
5892**** DONE udta
5893***** DONE meta
5894*** DONE free
5895*** DONE mdat
5896*** DONE Add some tree diagrams to fill sections of whitespace
5897    - [X] moov
5898    - [X] trak
5899    - [X] mdia
5900    - [X] minf
5901    - [X] stbl
5902** DONE Add volume control to trackplay/cdplay
5903*** DONE Add get_volume() method to low-level audio players
5904    It should return the current set volume as a float.
5905    - [X] PulseAudioOutput
5906    - [X] OSSAudioOutput
5907    - [X] CoreAudioOutput
5908    - [X] NULLAudioOutput
5909*** DONE Add set_volume(volume) method to low-level audio players
5910    It should take a volume setting float and adjust the output volume.
5911    - [X] PulseAudioOutput
5912    - [X] OSSAudioOutput
5913    - [X] CoreAudioOutput
5914    - [X] NULLAudioOutput
5915*** DONE Build volume setting, progress-bar style widget
5916*** DONE Ensure volume control updates when system volume is updated
5917*** DONE Add widget to player widget
5918** DONE Make audio output selectable in trackplay/cdplay
5919*** DONE Add available_outputs() function to audiotools.player module
5920    should return list of available AudioOutput subclasses
5921*** DONE Add description() method to AudioOutput subclasses
5922    Returns a human-readable Unicode string indicating
5923    what the audio output device is.
5924    - [X] PulseAudioOutput
5925    - [X] OSSAudioOutput
5926    - [X] CoreAudioOutput
5927    - [X] NULLAudioOutput
5928*** DONE Ensure output switches correctly
5929    Switches should be as seamless as possible and include
5930    setting the output volume properly.
5931** DONE Add track2track format conversion options
5932   Primarily for conversion between one lossless format to another.
5933   - [X] --sample-rate
5934   - [X] --channels
5935   - [X] --bits-per-sample
5936*** DONE Add documentation to man page
5937*** DONE Add unit tests
5938** DONE Add internal True Audio codec
5939*** DONE Add Python-based True Audio decoder
5940*** DONE Add Python-based True Audio encoder
5941*** DONE Document True Audio decoding
5942*** DONE Document True Audio encoding
5943*** DONE Add C-based True Audio decoder
5944**** DONE modify it for standalone use
5945**** DONE ensure there's no memory errors or leaks
5946**** DONE Make decoder thread-friendly
5947*** DONE Add C-based True Audio encoder
5948**** DONE modify it for standalone use
5949**** DONE ensure there's no memory errors or leaks
5950**** DONE make encoder thread-friendly
5951*** DONE Add True Audio-specific unit tests
5952*** DONE Test encoder/decoder against reference
5953*** DONE Add True Audio detecter to file_type() function
5954*** DONE Add True Audio AudioFile class
5955*** DONE Ensure True Audio class passes all unit tests
5956** DONE Add AccurateRip support
5957  This might make give cd2track a little added respectability
5958  and post-rip tools could be built also to verify tracks/disc images.
5959*** DONE Add accuraterip Python support
5960**** DONE add DiscID to accuraterip module
5961**** DONE add perform_lookup to accuraterip module
5962**** DONE add accuraterip_lookup to __init__ module
5963**** DONE add AccurateRipReader to accuraterip module
5964     This would be a PCMReader wrapper to be used
5965     by cd2track to verify track checksum while it's being extracted.
5966**** DONE add AccurateRipTrackChecksum to accuraterip module
5967     This would be a transfer_framelist_data target
5968     to be used by trackverify to verify tracks
5969     already extracted.
5970**** DONE Add programming docstrings
5971**** DONE Add programming documentation
5972*** DONE Add AccurateRip disc ID to cdinfo
5973*** DONE Add support to cd2track
5974    This should be done by default as a wrapper around
5975    the existing CDDA PCMReader objects.
5976*** DONE Add support to trackverify
5977    Add a -R/--accuraterip option which foregoes the usual
5978    calls to .verify(), performs an AccurateRip lookup
5979    for all albums and indicates whether they match/don't match/aren't found
5980*** DONE Add protocol documentation
5981    - [X] calculating the AccurateRip disc ID
5982    - [X] fetching database entries from the server
5983    - [X] parsing database entries from the server
5984    - [X] calculating a track's CRC
5985    - [X] checking the confidence level
5986* DONE Finish version 2.21
5987** DONE Bump minimum required Python version to 2.7
5988   Python 2.7 is pretty common these days and offers useful
5989   features that have been backported from 3.
5990** DONE Cleanup ProgressDisplay design
5991   Replace row_id indicators with a ProgressDisplayRow object
5992   which is created by ProgressDisplay and features
5993   update() and delete() methods that update the main ProgressDisplay.
5994
5995   This removes the need to specify row_id values and the
5996   main ProgressDisplay can assign empty slots as needed.
5997*** DONE Update documentation
5998** DONE Update temporary file management
5999   Some of the metadata functions update via tempfile.TemporaryFile
6000   then overwrite the original once the file's been completed.
6001
6002   This would be a bad idea in the edge case of a full disk.
6003
6004   So it may be preferable to write to a temp file alongside the original
6005   and then rename over it.  That way the original wouldn't be changed
6006   until commit-time.
6007   - [X] aiff.py
6008   - [X] ape.py
6009   - [X] flac.py
6010   - [X] m4a.py
6011   - [X] tta.py
6012   - [X] wav.py
6013*** DONE Add unit tests
6014    Ensure tracks retain permissions when metadata is updated.
6015** DONE Convert utilties from optparse to argparse
6016   argparse seems a bit nicer and more user-friendly
6017   since it generates usage lines automatically.
6018   - [X] audiotools-config
6019   - [X] cd2track
6020   - [X] cdinfo
6021   - [X] cdplay
6022   - [X] coverdump
6023   - [X] covertag
6024   - [X] coverview
6025   - [X] dvda2track
6026   - [X] dvdainfo
6027   - [X] track2cd
6028   - [X] track2track
6029   - [X] trackcat
6030   - [X] trackcmp
6031   - [X] trackinfo
6032   - [X] tracklength
6033   - [X] tracklint
6034   - [X] trackplay
6035   - [X] trackrename
6036   - [X] tracksplit
6037   - [X] tracktag
6038   - [X] trackverify
6039*** DONE Ensure all utilities have a working --version option
6040    Now that it's no longer built-in, I'll need to add and check
6041    the option by hand.
6042    - [X] audiotools-config
6043    - [X] cd2track
6044    - [X] cdinfo
6045    - [X] cdplay
6046    - [X] coverdump
6047    - [X] covertag
6048    - [X] coverview
6049    - [X] dvda2track
6050    - [X] dvdainfo
6051    - [X] track2cd
6052    - [X] track2track
6053    - [X] trackcat
6054    - [X] trackcmp
6055    - [X] trackinfo
6056    - [X] tracklength
6057    - [X] tracklint
6058    - [X] trackplay
6059    - [X] trackrename
6060    - [X] tracksplit
6061    - [X] tracktag
6062    - [X] trackverify
6063** DONE Improve Ogg format metadata speeds
6064   Reading/writing should be faster for all Ogg-based formats.
6065*** DONE Ogg FLAC
6066*** DONE Ogg Vorbis
6067*** DONE Opus
6068** DONE Adjust MetaData.clean() interface
6069   Returns (MetaData, [fixes]) tuple
6070   where MetaData is a new, fixed metadata object
6071   and fixes is a list of Unicode strings.
6072*** DONE Fix MetaData objects
6073    - [X] MetaData
6074    - [X] APEv2
6075    - [X] FlacMetaData
6076    - [X] ID3v22Comment
6077    - [X] ID3v23Comment
6078    - [X] ID3v24Comment
6079    - [X] ID3CommentPair
6080    - [X] ID3v1Comment
6081    - [X] M4A_META_Atom
6082    - [X] VorbisComment
6083*** DONE Update documentation
6084*** DONE Update unit tests
6085** DONE Adjust AudioFile.clean() interface
6086   Takes optional output_filename and returns list of fixes performed.
6087   It would be nice not to need separate code paths
6088   for dry runs, like with MetaData, but that may not be feasible.
6089*** DONE Fix AudioFile objects
6090    - [X] AudioFile
6091    - [X] AiffAudio
6092    - [X] FlacAudio
6093    - [X] WaveAudio
6094*** DONE Update tracklint
6095*** DONE Update documentation
6096*** DONE Update unit tests
6097** DONE Add lint for "double-wrapped" ID3v2 tags
6098   Sometimes files are idiotically wrapped in multiple layers of ID3v2 tags
6099   like nested Russian dolls.  These are currently unrecognized because
6100   audiotools assumes a valid track follows the tag instead of another tag.
6101*** DONE Support tracks nested arbitrarily deep in ID3v2 tags
6102    In order to be fixable,
6103    these files will need to be readable in the first place
6104**** DONE Update file_type function to skip ID3v2 tags recursively
6105     It should still only find MP2/MP3/FLAC/TTA files how matter how
6106     deeply nested, but should support arbitrarily deep nesting.
6107**** DONE Update FlacAudio
6108***** DONE Update FlacAudio to support nested tags
6109      This is basically a matter of making the offset as large
6110      as it takes to hold all the tags.
6111***** DONE Have FlacAudio.clean() remove all ID3v2 tags
6112***** DONE Update unit tests
6113**** DONE Update MP3Audio
6114***** DONE Update MP3Audio to support nested tags
6115***** DONE Have MP3Audio.clean() remove all ID3v2 tags except one
6116***** DONE Update unit tests
6117**** DONE Update MP2Audio
6118***** DONE Update MP2Audio to support nested tags
6119***** DONE Have MP2Audio.clean() remove all ID3v2 tags except one
6120***** DONE Update unit tests
6121**** DONE Update TrueAudio
6122***** DONE Update TrueAudio to support nested tags
6123***** DONE Have TrueAudio.clean() remove all ID3v2 tags except one
6124***** DONE Update unit tests
6125* DONE Finish version 2.22
6126** DONE Update ReplayGain interface
6127   All ReplayGain should be lossless (if supported at all) and needs an interface
6128   that's less dependant on external tools.
6129*** DONE Remove add_replay_gain() classmethod
6130    - [X] AudioFile
6131    - [X] ALACAudio
6132    - [X] AiffAudio
6133    - [X] AuAudio
6134    - [X] FlacAudio
6135    - [X] M4AAudio
6136    - [X] MP2Audio
6137    - [X] MP3Audio
6138    - [X] OggFlacAudio
6139    - [X] OpusAudio
6140    - [X] ShortenAudio
6141    - [X] TrueAudio
6142    - [X] VorbisAudio
6143    - [X] WavPackAudio
6144    - [X] WaveAudio
6145*** DONE Add add_replay_gain() function
6146    Takes an album's worth of audiofile objects and optional progress function
6147    and if any of their classes support it (via the supports_replay_gain() classmethod)
6148    calls calculate_replay_gain() on all of them and applies the result
6149    using set_replay_gain()
6150*** DONE Remove can_add_replay_gain() classmethod
6151    If supports_replay_gain() classmethod returns True,
6152    set_replay_gain() will be able to do something useful.
6153    - [X] AudioFile
6154    - [X] ALACAudio
6155    - [X] AiffAudio
6156    - [X] AuAudio
6157    - [X] FlacAudio
6158    - [X] M4AAudio
6159    - [X] MP2Audio
6160    - [X] MP3Audio
6161    - [X] OggFlacAudio
6162    - [X] OpusAudio
6163    - [X] ShortenAudio
6164    - [X] TrueAudio
6165    - [X] VorbisAudio
6166    - [X] WavPackAudio
6167    - [X] WaveAudio
6168*** DONE Remove lossless_replay_gain classmethod
6169    All ReplayGain will be lossless
6170    - [X] AudioFile
6171    - [X] ALACAudio
6172    - [X] AiffAudio
6173    - [X] AuAudio
6174    - [X] FlacAudio
6175    - [X] M4AAudio
6176    - [X] MP2Audio
6177    - [X] MP3Audio
6178    - [X] OggFlacAudio
6179    - [X] OpusAudio
6180    - [X] ShortenAudio
6181    - [X] TrueAudio
6182    - [X] VorbisAudio
6183    - [X] WavPackAudio
6184    - [X] WaveAudio
6185*** DONE Convert replay_gain() method to get_replay_gain()
6186    Returns a ReplayGain object or None, as it does now.
6187    Analagous to get_metadata()
6188    - [X] AudioFile
6189    - [X] ALACAudio
6190    - [X] AiffAudio
6191    - [X] AuAudio
6192    - [X] FlacAudio
6193    - [X] M4AAudio
6194    - [X] MP2Audio
6195    - [X] MP3Audio
6196    - [X] OggFlacAudio
6197    - [X] OpusAudio
6198    - [X] ShortenAudio
6199    - [X] TrueAudio
6200    - [X] VorbisAudio
6201    - [X] WavPackAudio
6202    - [X] WaveAudio
6203*** DONE Add set_replay_gain() method
6204    Takes a ReplayGain object or None and sets the file's values
6205    by updating metadata, or deletes the values if None
6206    Analagous to set_metadata()
6207    - [X] AudioFile
6208    - [X] ALACAudio
6209    - [X] AiffAudio
6210    - [X] AuAudio
6211    - [X] FlacAudio
6212    - [X] M4AAudio
6213    - [X] MP2Audio
6214    - [X] MP3Audio
6215    - [X] OggFlacAudio
6216    - [X] OpusAudio
6217    - [X] ShortenAudio
6218    - [X] TrueAudio
6219    - [X] VorbisAudio
6220    - [X] WavPackAudio
6221    - [X] WaveAudio
6222*** DONE Add delete_replay_gain() method
6223    Deletes any ReplayGain values, if any
6224    Analagous to delete_metadata()
6225    - [X] AudioFile
6226    - [X] ALACAudio
6227    - [X] AiffAudio
6228    - [X] AuAudio
6229    - [X] FlacAudio
6230    - [X] M4AAudio
6231    - [X] MP2Audio
6232    - [X] MP3Audio
6233    - [X] OggFlacAudio
6234    - [X] OpusAudio
6235    - [X] ShortenAudio
6236    - [X] TrueAudio
6237    - [X] VorbisAudio
6238    - [X] WavPackAudio
6239    - [X] WaveAudio
6240*** DONE Update utilities to use new ReplayGain interface
6241    - [X] cd2track
6242    - [X] dvda2track
6243    - [X] track2track
6244    - [X] trackinfo
6245    - [X] trackplay
6246    - [X] tracksplit
6247    - [X] tracktag
6248*** DONE Update documentation
6249*** DONE Update unit tests
6250** DONE Catch interrupts more cleanly in utilities
6251   When the user cancels a long-running job, utilities should clean the screen
6252   as well as possible, delete any partial files and display a "cancelled" message
6253   instead of delivering stack traces like they do now.
6254   - [X] cd2track
6255   - [X] cdplay
6256   - [X] dvda2track
6257   - [X] track2cd
6258   - [X] track2track
6259   - [X] trackcat
6260   - [X] trackcmp
6261   - [X] tracklength
6262   - [X] trackplay
6263   - [X] tracksplit
6264   - [X] tracktag
6265   - [X] trackverify
6266** DONE Add AudioFile.seekable() method
6267   This returns True if the format's PCMReader has a .seek() method
6268   and that method supports some sort of fine-grained seeking
6269   (i.e. not just return to the beginning of the file).
6270*** DONE Update audio formats
6271    - [X] AudioFile
6272    - [X] ALACAudio
6273    - [X] AiffAudio
6274    - [X] AuAudio
6275    - [X] FlacAudio
6276    - [X] M4AAudio
6277    - [X] MP2Audio
6278    - [X] MP3Audio
6279    - [X] OggFlacAudio
6280    - [X] OpusAudio
6281    - [X] ShortenAudio
6282    - [X] TrueAudio
6283    - [X] VorbisAudio
6284    - [X] WavPackAudio
6285    - [X] WaveAudio
6286*** DONE Update documentation
6287*** DONE Update utilities
6288**** DONE tracksplit
6289     If the file being split isn't seekable, decompress it to a temp file
6290     so it can be processed across multiple jobs.
6291**** DONE trackcmp
6292     If comparing multiple tracks against a non-seekable image,
6293     decompress it to a temp file so it can be processed across multiple jobs.
6294*** DONE Update unit tests
6295** DONE Have AccurateRip detect offset tracks
6296   If the tracks have been ripped accurately but are merely offset
6297   by some small positive or negative number of samples,
6298   this should be detected and indicated in the AccurateRip results screens.
6299*** DONE Update ChecksumV1
6300**** DONE Add keyword parameters with sensible defaults
6301**** DONE Add range option
6302     This indicates the total range of the window size.
6303     Defaults to 1, indicating only a single checksum to be generated.
6304**** DONE Change .checksum() method to .checksums()
6305     Returns a list of all calculated checksums over the whole range.
6306*** DONE Add unit tests
6307    Ensure ranged and non-ranged checksums are calculated properly
6308    and update tests for new interface.
6309*** DONE Build audiotools.cdio.CDDAReader object
6310    This functions like a PCMReader object over the entire CDDA device.
6311    It should work transparently on physical CD devices as well as CD images
6312    and be PCM-frame centric by doing all the CD sector conversions
6313    behind-the-scenes.
6314**** DONE Add methods and attributes
6315***** DONE CDDAReader.sample_rate
6316***** DONE CDDAReader.channels
6317***** DONE CDDAReader.channel_mask
6318***** DONE CDDAReader.bits_per_sample
6319***** DONE CDDAReader.track_offsets
6320      - [X] CD image
6321      - [X] CD drive
6322      Returns starting offset of each track, in PCM frames
6323***** DONE CDDAReader.track_lengths
6324      - [X] CD image
6325      - [X] CD drive
6326      Returns length of each track, in PCM frames
6327***** DONE CDDAReader.first_sector
6328      - [X] CD image
6329      - [X] CD drive
6330      Used for calculating various disc IDs
6331***** DONE CDDAReader.last_sector
6332      - [X] CD image
6333      - [X] CD drive
6334      Used for calculating various disc IDs
6335***** DONE CDDAReader.is_image
6336      - [X] CD drive
6337      - [X] CD image
6338***** DONE CDDAReader.read(pcm_frames)
6339      - [X] CD image
6340      - [X] CD drive
6341***** DONE CDDAReader.seek(pcm_frames)
6342      - [X] CD image
6343      - [X] CD drive
6344***** DONE CDDAReader.set_speed(speed)
6345      - [X] CD image
6346      - [X] CD drive
6347***** DONE CDDAReader.log()
6348      - [X] CD image
6349      - [X] CD drive
6350***** DONE CDDAReader.reset_log()
6351***** DONE CDDAReader.close()
6352**** DONE Update documentation
6353**** DONE Update unit tests
6354**** DONE Detect nonexistent discs
6355     CDDAReader should detect when a physical drive doesn't contain a disc
6356     and raise an appropriate exception.
6357**** DONE Update utilities
6358***** DONE cd2track
6359***** DONE cdinfo
6360***** DONE cdplay
6361**** DONE Clean out old audiotools.CDDA object
6362     Remove outdated interface, its documentation and unit tests.
6363*** DONE Update utilities
6364    When verifying AccurateRip, make sure to populate the checksum
6365    with the last few samples of the previous track (if any)
6366    and the next few samples of the next track (if any)
6367    in order to find the checksum's offset.
6368**** DONE trackverify
6369**** DONE cd2track
6370** DONE Simplify lookup services interface
6371   The perform_lookup() functions should work on DiscID objects
6372   and DiscID objects should be generated from
6373   CDDAReaders, track lists or cuesheet/length combinations.
6374*** DONE FreeDB
6375    - [X] Add from_tracks classmethod
6376    - [X] Add from_sheet classmethod
6377    - [X] Add unit tests
6378*** DONE MusicBrainz
6379    - [X] Add from_tracks classmethod
6380    - [X] Add from_sheet classmethod
6381    - [X] Add unit tests
6382*** DONE AccurateRip
6383    - [X] Add from_tracks classmethod
6384    - [X] Add from_sheet classmethod
6385    - [X] Add unit tests
6386** DONE Add documentation for lookup services
6387   - [X] audiotools.freedb
6388   - [X] audiotools.musicbrainz
6389   - [X] audiotools.accuraterip
6390** DONE Convert TOC and CUE handling to proper grammars
6391*** DONE Convert audiotools.cue
6392*** DONE Convert audiotools.toc
6393** DONE Add AccurateRipV2 support
6394*** DONE Add ChecksumV2 object
6395**** DONE Add unit tests
6396*** DONE Update utilities
6397    - [X] cd2track
6398    - [X] trackverify
6399** DONE Update Sheet interface
6400   Although cuesheets are mostly a repository of index points,
6401   it would be useful to support a wider subset of options
6402   when converting one form to another.
6403*** DONE Sheet
6404    - [X] @classmethod converted(sheet) -> Sheet
6405    builds Sheet object from Sheet-compatible object
6406    - [X] __len__() -> total_tracks
6407    - [X] __getitem__(track_index) -> SheetTrack
6408    - [X] __eq__(sheet) -> boolean
6409    - [X] track_numbers() -> [int]
6410    - [X] track(track_number) -> SheetTrack
6411    - [X] get_metadata() -> MetaData
6412    MetaData contains catalog_number and CD-TEXT information
6413*** DONE SheetTrack
6414    - [X] @classmethod converted(sheet_track) -> SheetTrack
6415    builds SheetTrack object from SheetTrack-compatible object
6416    - [X] __len__() -> total_indexes
6417    - [X] __getitem__(index_index) -> SheetIndex
6418    - [X] __eq__(sheet_track) -> boolean
6419    - [X] indexes() -> [int]
6420    - [X] index(index_number) -> SheetIndex
6421    - [X] filename() -> string
6422    - [X] number() -> track_number
6423    - [X] get_metadata() -> MetaData
6424    MetaData contains track_number, ISRC and CD-TEXT information
6425    - [X] is_audio() -> boolean
6426    - [X] pre_emphasis() -> boolean
6427    - [X] copy_peritted() -> boolean
6428*** DONE SheetIndex
6429    - [X] @classmethod converted(sheet_index) -> SheetIndex
6430    - [X] __eq__(sheet_index) -> boolean
6431    - [X] number() -> index_number
6432    pre-gap is index_number 0
6433    - [X] offset() -> Fraction
6434    offset of index in seconds from start of stream
6435*** DONE Update audiotools.cue.Cuesheet
6436*** DONE Update audiotools.toc.TOCFile
6437*** DONE Update audiotools.flac.Flac_CUESHEET
6438*** DONE Ensure non-image cuesheets are handled properly
6439    Given a non-image cuesheet and list of track lengths,
6440    it may be possible to convert a non-image cuesheet
6441    to an image cuesheet.
6442*** DONE Handle 1st track pre-gap correctly
6443    Discs with a pre-gap before the 1st track
6444    should have that gap removed when split
6445    and re-added when concatenating them back together
6446    based on the cuesheet contents.
6447    - [X] update tracksplit
6448    - [X] update trackcat
6449    - [X] add unit tests
6450*** DONE Update utilities
6451    - [X] trackcat
6452    - [X] tracksplit
6453    - [X] track2cd
6454    - [X] trackinfo
6455*** DONE Update functions/methods which take cuesheets
6456    - [X] sheet_metadata_lookup
6457    - [X] accuraterip_sheet_lookup
6458*** DONE Update documentation
6459*** DONE Update unit tests
6460** DONE Converts AudioFile.seconds_length() method to Fraction
6461   This should be more useful than the Decmial it was using before.
6462*** DONE Update method
6463*** DONE Update documentation
6464*** DONE Update utilities
6465    - [X] track2cd
6466    - [X] trackcat
6467    - [X] trackinfo
6468    - [X] tracklength
6469    - [X] trackplay
6470    - [X] tracksplit
6471* TODO Add support for genre tag?
6472  I'm not a big fan of the genre tag.
6473  Unlike track number, album name, ISRC, etc. in which a value
6474  can be reliably determined from the source material (e.g. back of the CD),
6475  genre is akin to a "rating" tag. Its value varies from person to person
6476  and this makes it less valuable for archival purposes.
6477
6478  In addition, the genre tag itself is implemented in incompatible ways.
6479  APEv2 uses a chunk of text, ID3v1 uses an integer representing one
6480  of many designated genre labels, ID3v2 uses a mix of genre byte
6481  and/or text string, and so on.
6482
6483  That said, the genre field shows up in a lot of players.
6484  So, some grudging support for it would probably be appreciated.
6485* TODO Replace magic numbers with named constants
6486  There's still a few instances of magic numbers in use,
6487  in the __flac__.py module, for instance.
6488* TODO Support disc metadata submission
6489  If a disc is not found on FreeDB or MusicBrainz,
6490  there should be some mechanism to submit user-defined data
6491  to those services.
6492* TODO build track2cover utility
6493  It would be helpful to have tools that could query
6494  online cover databases and retrieve images for tagging purposes.
6495  track2cover can replace coverdump, functioning like trac2xmcd
6496  which allows it to pull from existing metadata or from online sources.
6497* TODO build trackverify utility
6498  Much like flac(1)'s --verify option, this should check
6499  a file for correctness and, if possible, verify its output
6500  matches any internal hashes/checksums.
6501  May work recursively to check a user's entire collection.
6502** DONE add a .verify() method to AudioFile classes
6503   Returns True if the file is okay.  Raises InvalidFile if not.
6504   Include unit test.
6505   - [X] AiffAudio
6506   - [X] ALACAudio
6507   - [X] AuAudio
6508   - [X] FlacAudio
6509   - [X] M4AAudio
6510   - [X] MP2Audio
6511   - [X] MP3Audio
6512   - [X] OggFlacAudio
6513   - [X] SpeexAudio
6514   - [X] ShortenAudio
6515   - [X] VorbisAudio
6516   - [X] WaveAudio
6517   - [X] WavPackAudio
6518** TODO add trackverify unit tests for all known verifiable problems
6519** DONE add trackverify man page
6520** DONE link trackverify man page to others
6521* TODO Add a -r/--no-results flag to trackcmp/trackverify
6522  Listing only the final results and not the running list
6523** TODO Update man page
6524* TODO Add DVD-A support
6525** DONE Build AOB parser/extractor
6526*** DONE Optimize AOB extractor for better offset handling
6527    The current version works, but is a little too naive
6528** TODO Build CPPM handler
6529*** DONE Create audiotools.prot module
6530*** TODO Create audiotools.prot.CPPMDecoder object
6531**** DONE Add CPPMDecoder.__init__() method
6532**** DONE Add CPPMDecoder.decode() method
6533**** TODO Ensure CPPMDecoder is cross-platform
6534** DONE Document AOB parser
6535** DONE Build raw PCM decoder
6536   The PCM data doesn't appear to be stored raw,
6537   so some additional calculation will likely be necessary
6538** TODO Build MLP decoder
6539*** DONE Create decoders.MLPDecoder class
6540**** DONE Add MLPDecoder.init() method
6541**** DONE Add MLPDecoder.sample_rate attribute
6542**** DONE Add MLPDecoder.channels attribute
6543**** DONE Add MLPDecoder.bits_per_sample attribute
6544**** DONE Add MLPDecoder.channel_mask attribute
6545**** DONE Add MLPDecoder.read() method
6546**** DONE Add MLPDecoder.analyze_frame() method
6547**** DONE Add MLPDecoder.close() method
6548*** DONE Perform MLP frame parsing
6549**** DONE Perform frame size parsing
6550**** DONE Perform major sync parsing
6551**** DONE Perform substream sizes parsing
6552**** DONE Perform substream datas parsing
6553***** DONE Perform block parsing
6554****** DONE Perform restart header parsing
6555****** DONE Perform decoding parameters parsing
6556******* DONE Perform parameter presence flags parsing
6557******* DONE Perform block size parsing
6558******* DONE Perform matrix parameters parsing
6559******* DONE Perform output shifts parsing
6560******* DONE Perform quant step sizes parsing
6561******* DONE Perform channel parameters parsing
6562******** DONE Perform FIR filter parameters parsing
6563******** DONE Perform IIR filter parameters parsing
6564******** DONE Perform Huffman offset parsing
6565******** DONE Perform Codebook parsing
6566******** DONE Perform Huffman least-significant bits parsing
6567****** DONE Perform block data parsing
6568***** DONE Perform block alignment
6569***** DONE Perform CRC parsing
6570*** TODO Perform MLP data decoding
6571**** DONE Handle FIR/IIR filtering
6572**** DONE Handle matrix reconstruction
6573***** DONE Build noise channels
6574**** DONE Handle quant_step_sizes
6575**** DONE Combine multiple substreams into single output frame
6576**** DONE Handle output shifts
6577**** TODO Handle checksums
6578***** TODO restart header lossless check
6579***** DONE end-of-frame parity
6580***** DONE end-of-frame checksum
6581**** DONE Build pcm.FrameList from PCM data arrays
6582***** DONE Reorder MLP channels to wave order
6583**** DONE Add bs_try for proper EOF aborts
6584**** DONE Add end-of-stream marker checking to read()
6585**** TODO Add end-of-stream marker checking to analyze_frame()
6586**** DONE Optimize to work faster
6587     Perhaps with multiple blocks per call to read()
6588**** TODO Support 1 substream output
6589     MLP decoding supports 1 or 2 substreams.
6590     In some cases, substream 1 will contain the first 2 channels
6591     while substream 2 will contain the rest,
6592     which provides two different "mixes" depending on whether
6593     one is decoding to 2 channels or not.
6594***** TODO Update dvda2track with 1 substream output option
6595      This would allow the user to decide which "mix" is desired
6596      during extraction.
6597*** DONE Perform sanity checks
6598** TODO Document MLP decoder
6599*** DONE major sync
6600*** DONE substream size
6601*** DONE substream data
6602*** DONE restart header
6603*** DONE decoding parameters
6604**** DONE parameter presence flags
6605**** DONE block size
6606**** DONE matrix parameters
6607**** DONE output shifts
6608**** DONE quant step sizes
6609*** DONE checksums
6610*** DONE block data
6611*** DONE channel filtering
6612*** DONE channel rematrixing
6613**** DONE noise channels
6614**** DONE rematrixing
6615*** DONE end of stream marker
6616*** TODO decoding XOR byte
6617** DONE Document PCM decoder
6618** DONE Link MLP decoder to AOB extractor
6619** DONE Link PCM decoder to AOB extractor
6620** TODO Build DVD-A postprocessor
6621** DONE Document Python interface
6622** DONE Build dvdainfo utility
6623*** DONE Add dvdainfo.1 man page
6624*** DONE Ensure -A with invalid dir generates error message
6625** DONE Build dvda2track utility
6626*** DONE Fix to work without -d
6627*** DONE Add --track-start / --track-total options
6628    Since some especially long discs split tracks across titles,
6629    it makes sense to have a way to specify the starting track number
6630    and the total number of tracks.
6631**** DONE Add options to man page
6632*** DONE Add dvda2track.1 man page
6633*** DONE Ensure -A with invalid dir generates error message
6634** DONE Build dvda2xmcd utility
6635   Querying FreeDB and MusicBrainz are possible,
6636   but I expect most of the time they'll generate empty templates.
6637*** DONE Add dvda2xmcd.1 man page
6638*** DONE Ensure -A with invalid dir generates error message
6639* TODO Add cdplay utility
6640  Should work similarly to trackplay,
6641  taking track number(s) and playing them to OSS/PulseAudio
6642  or whatever other audio output is available.
6643** DONE Add audiotools.player.CDPlayer class
6644*** DONE Add docstrings
6645*** DONE Add programming docs
6646** DONE Add audiotools.player.CDPlayerThread class
6647*** DONE Add docstrings
6648** DONE Add non-interactive mode to cdplay
6649** DONE Add interative mode to cdplay
6650   Urwid-based display should have various standard features
6651   - [X] display CD info
6652   - [X] progress monitor
6653   - [X] start
6654   - [X] pause
6655   - [X] next track
6656   - [X] previous track
6657** DONE Silence CD query messages
6658** DONE Support command-line tracks
6659   One might want to play only a subset of the whole CD
6660** DONE Add -V/--verbose option
6661** DONE Add --shuffle option
6662** DONE Add -x/--xmcd file support
6663** DONE Avoid spinning down CDs between tracks
6664   This makes playback less seamless than I'd like
6665** TODO Improve stability
6666** DONE Add man page for cdplay.1
6667** TODO Add basic unit tests
6668   - [ ] Check command-line arguments
6669* TODO Reorganize test suite
6670  The current haphazard layout makes it too difficult to deterimine
6671  if some feature has been fully unit tested.
6672  Or, if a new feature is added, the logical place to put new tests
6673  is not obvious.
6674** TODO Group tests for the audio formats
6675*** DONE AudioFile
6676    - [X] test_init
6677    - [X] test_is_type
6678    - [X] test_bits_per_sample
6679    - [X] test_metadata
6680    - [X] test_length
6681    - [X] test_track_number
6682    - [X] test_album_number
6683    - [X] test_track_name
6684    - [X] test_replay_gain
6685*** DONE LosslessAudioFile
6686    - [X] test_lossless
6687    - [X] test_channels
6688    - [X] test_channel_mask
6689    - [X] test_sample_rate
6690    - [X] test_pcm
6691    - [X] test_convert
6692*** DONE LossyAudioFile
6693    - [X] test_bits_per_sample
6694    - [X] test_lossless
6695    - [X] test_channels
6696    - [X] test_channel_mask
6697    - [X] test_sample_rate
6698    - [X] test_pcm
6699    - [X] test_convert
6700*** DONE ALACAudio
6701    - [X] test_init
6702    - [X] test_bits_per_sample
6703    - [X] test_channel_mask
6704    - [X] test_verify
6705    - [X] test_streams
6706    - [X] test_small_files
6707    - [X] test_full_scale_deflection
6708    - [X] test_sines
6709    - [X] test_wasted_bps
6710    - [X] test_blocksizes
6711    - [X] test_noise
6712    - [X] test_fractional
6713    - [X] test_frame_header_variations
6714*** TODO AiffAudio
6715    - [ ] test_init
6716    - [X] test_channel_mask
6717    - [X] test_verify
6718    - [X] test_roundtrip_aiff_chunks
6719    - [X] test_convert_aiff_chunks
6720*** TODO AuAudio
6721    - [ ] test_init
6722    - [X] test_verify
6723    - [X] test_channel_mask
6724*** TODO FlacAudio
6725    - [ ] test_init
6726    - [ ] test_metadata2
6727    - [X] test_verify
6728    - [ ] test_cuesheet
6729    - [X] test_roundtrip_aiff_chunks
6730    - [X] test_convert_aiff_chunks
6731    - [X] test_roundtrip_wave_chunks
6732    - [X] test_convert_wave_chunks
6733    - [X] test_streams
6734    - [X] test_small_files
6735    - [X] test_full_scale_deflection
6736    - [X] test_sines
6737    - [X] test_wasted_bps
6738    - [X] test_blocksizes
6739    - [X] test_frame_header_variations
6740    - [X] test_option_variations
6741    - [X] test_noise
6742    - [X] test_fractional
6743*** TODO M4AAudio
6744    - [ ] test_init
6745    - [ ] test_verify
6746*** TODO MP2Audio
6747    - [ ] test_init
6748    - [X] test_length
6749    - [ ] test_verify
6750*** TODO MP3Audio
6751    - [ ] test_init
6752    - [X] test_length
6753    - [ ] test_verify
6754*** TODO OggFlacAudio
6755    - [ ] test_init
6756    - [X] test_verify
6757    - [ ] test_cuesheet
6758*** TODO ShortenAudio
6759    - [ ] test_init
6760    - [X] test_bits_per_sample
6761    - [X] test_verify
6762    - [X] test_roundtrip_wave_chunks
6763    - [X] test_convert_wave_chunks
6764    - [X] test_streams
6765    - [X] test_small_files
6766    - [X] test_full_scale_deflection
6767    - [X] test_sines
6768    - [X] test_blocksizes
6769    - [X] test_noise
6770*** TODO VorbisAudio
6771    - [ ] test_init
6772    - [X] test_channels
6773    - [X] test_verify
6774*** TODO WavPackAudio
6775    - [ ] test_init
6776    - [X] test_verify
6777    - [ ] test_cuesheet
6778    - [X] test_roundtrip_wave_chunks
6779    - [X] test_convert_wave_chunks
6780    - [X] test_small_files
6781    - [X] test_full_scale_deflection
6782    - [X] test_wasted_bps
6783    - [X] test_blocksizes
6784    - [X] test_silence
6785    - [X] test_noise
6786    - [X] test_fractional
6787    - [X] test_multichannel
6788    - [X] test_sines
6789    - [X] test_option_variations
6790*** TODO WaveAudio
6791    - [ ] test_init
6792    - [X] test_verify
6793    - [X] test_roundtrip_wave_chunks
6794    - [X] test_convert_wave_chunks
6795** DONE Group tests for the metadata formats
6796*** DONE ApeTag
6797*** DONE FLAC
6798*** DONE ID3v1
6799*** DONE ID3v2.2
6800*** DONE ID3v2.3
6801*** DONE ID3v2.4
6802*** DONE IDCommentPair
6803*** DONE M4A
6804*** DONE VorbisComment
6805*** DONE Ensure that merge() works properly
6806      It has to handle all supported text fields
6807      *and* must handle images if supported.
6808** TODO Group tests for the core libraries
6809*** TODO audiotools.__init__
6810**** TODO AlbumMetaData
6811**** DONE BufferedPCMReader
6812**** DONE CDDA
6813**** DONE ChannelMask
6814**** DONE Image
6815**** DONE LimitedPCMReader
6816**** TODO Messenger
6817**** DONE PCMCat
6818**** DONE PCMConverter
6819**** DONE PCMReaderWindow
6820**** TODO ReorderedPCMReader
6821**** DONE ReplayGain
6822**** DONE applicable_replay_gain
6823**** DONE at_a_time
6824**** DONE build_timestamp
6825**** DONE calculate_replay_gain
6826**** DONE filename_to_type
6827**** DONE group_tracks
6828**** TODO make_dirs
6829**** DONE open
6830**** DONE open_directory
6831**** DONE open_files
6832**** DONE parse_timestamp
6833**** DONE pcm_frame_cmp
6834**** DONE pcm_split
6835**** DONE read_sheet
6836**** DONE str_width
6837**** DONE transfer_framelist_data
6838**** DONE XMCD
6839**** DONE MusicBrainzReleaseXML
6840*** DONE audiotools.pcm
6841**** DONE FloatFrameList
6842**** DONE FrameList
6843**** DONE from_channels
6844**** DONE from_float_Frames
6845**** DONE from_float_channels
6846**** DONE from_frames
6847**** DONE from_list
6848*** DONE audiotools.player
6849**** DONE Player
6850**** DONE CDPlayer
6851*** DONE audiotools.replaygain
6852**** DONE ReplayGain
6853**** DONE ReplayGainReader
6854*** DONE audiotools.decoders
6855**** DONE BitstreamReader
6856*** DONE audiotools.encoders
6857**** DONE BitstreamWriter
6858*** TODO audiotools.verify
6859**** TODO mpeg
6860**** TODO ogg
6861** TODO Group tests for the individual tools
6862*** TODO audiotools-config
6863**** TODO test_options
6864**** TODO test_errors
6865*** DONE cd2track
6866**** DONE test_options
6867**** DONE test_errors
6868*** DONE cd2xmcd
6869**** DONE test_options
6870**** DONE test_errors
6871*** TODO cdinfo
6872**** TODO test_options
6873**** TODO test_errors
6874*** TODO cdplay
6875**** TODO test_options
6876**** TODO test_errors
6877*** DONE coverdump
6878**** DONE test_options
6879**** DONE test_errors
6880*** DONE dvdainfo
6881**** DONE test_errors
6882*** DONE dvda2track
6883**** DONE test_errors
6884*** DONE dvda2xmcd
6885**** DONE test_errors
6886*** DONE track2track
6887**** DONE test_options
6888**** DONE test_errors
6889*** DONE track2xmcd
6890**** DONE test_options
6891**** DONE test_errors
6892*** DONE trackcat
6893**** DONE test_options
6894*** DONE trackcmp
6895**** DONE test_options
6896*** TODO trackinfo
6897**** TODO test_options
6898**** TODO test_errors
6899*** DONE tracklength
6900*** DONE tracklint
6901*** TODO trackplay
6902**** TODO test_options
6903**** TODO test_errors
6904*** DONE trackrename
6905**** DONE test_options
6906**** DONE test_errors
6907*** DONE tracksplit
6908**** DONE test_options
6909**** DONE test_errors
6910*** TODO tracktag
6911**** DONE test_options
6912**** TODO test_errors
6913*** TODO trackverify
6914**** TODO test_options
6915**** TODO test_errors
6916** DONE Split tests into multiple files
6917* TODO Handle write errors more gracefully
6918  Just as the BitstreamReader uses an exception mechanism to detect
6919  and handle read errors, the BitstreamWriter should use a similar
6920  mechanism to detect write errors and bubble-up a Python exception.
6921
6922  The problem is that invalid writes are difficult to unit test.
6923  With a reader, one can simply truncate a valid file.
6924  It's harder to constrict a writer to only have X number of bytes
6925  available so that any errors can be checked.
6926** TODO Add try/etry support to BitstreamWriter
6927** DONE Update BitstreamWriter methods to raise exceptions
6928** TODO Update Python-based BitstreamWriter to catch C exceptions
6929** TODO Update encoders to catch C exceptions and raise Python exceptions
6930** TODO Unit test write errors
6931* TODO Verify metadata against reference implementations
6932  Where feasible, try to ensure our metadata
6933  matches what's read/written by known reference implementations.
6934** TODO FlacAudio
6935   against metaflac
6936** TODO M4AAudio/ALACAudio
6937   against taglib?
6938** TODO MP3Audio/MP2Audio
6939   Against taglib, perhaps?  Venerable id3lib doesn't work with UCS-2.
6940** TODO VorbisAudio
6941   against vorbis-tools
6942** TODO WavPackAudio
6943   against tablib?
6944* TODO Add lyrics metadata support
6945  Supporting storing lyrics text in the audio files themselves
6946  would be quite useful.
6947** TODO Add .lyrics field to MetaData
6948   Analagous to the lengthy .comment field, most likely.
6949** TODO Add unit testing for .lyrics field
6950** TODO Find automated source for lyrics
6951   Analagous to CDDB.
6952   This will be challenging since online sources for lyrics are rare.
6953   Unfortunately, expecting everyone to transcribe lyrics for songs
6954   is unrealistic - even moreso than filling in song names
6955   or scanning in covers by hand.
6956   If it's too much work, there's little point in having it.
6957** TODO Add tool support for lyrics
6958** TODO Add unit testing to tools
6959* TODO Convert format reference to HTML
6960  Not only would it be more accessible,
6961  but I could better integrate examples and give it basic interactivity.
6962  All of the diagrams and formulas must be kept as vector art
6963  so that they'll remain crisp when scaled.
6964
6965  Unfortunately, Chrome doesn't do MathML and IE doesn't do SVG
6966  which makes it non-viable right now.
6967* TODO Add a dvdaplay utility
6968** TODO Unify trackplay/cdplay/dvdaplay widgets into audiotools.ui
6969** TODO Unify non-Urwid player into audiotools.ui
6970** TODO Add man page
6971* TODO Make internal functions static
6972  Encoders and decoders come with lots of helper functions.
6973  These should be defined statically whenever possible
6974  to avoid colliding with one another.
6975  As a side effect, these function names can now be shortened.
6976  (not applicable to FLAC and Ogg FLAC, which share a lot of functions)
6977** TODO decoders
6978   - [X] src/decoders/alac.c
6979   - [ ] src/decoders/aobpcm.c
6980   - [ ] src/decoders/mlp.c
6981   - [ ] src/decoders/ogg.c
6982   - [ ] src/decoders/shn.c
6983   - [ ] src/decoders/sine.c
6984   - [ ] src/decoders/vorbis.c
6985   - [X] src/decoders/wavpack.c
6986** TODO encoders
6987   - [X] src/encoders/alac.c
6988   - [ ] src/encoders/flac.c
6989   - [ ] src/encoders/shn.c
6990   - [X] src/encoders/wavpack.c
6991* TODO handle AIFF offset/block size in SSND chunk
6992* TODO Assimilate external codecs
6993  More of a long-term plan than anything else.
6994  It would be best to have as few external program
6995  dependencies as possible
6996** TODO Add internal MP3 codec
6997   This is likely the codec people are most interested in
6998   and so it should be folded in so it can be used
6999   without any external dependencies.
7000*** TODO Add C-based MP3 decoder
7001*** TODO Add MP3 decoding reference documentation
7002*** TODO Add C-based MP3 encoder
7003    Unlike lossless codecs where output can be verified automatically,
7004    lossy codecs offer no similar guarantees.
7005    So like libsamplerate, it would be better to import
7006    a known correct implementation like LAME
7007    than to reimplement the wheel.
7008*** TODO Add MP3 encoding reference documentation
7009    A rundown of how LAME operates, in the hope of demystifying
7010    the format.
7011** TODO Add internal MP2 encoder
7012** TODO Add internal Vorbis codec
7013*** TODO Add Vorbis decoder
7014**** TODO Add VorbisDecoder Python object to decoders
7015**** TODO Document Vorbis decoding
7016***** DONE identification header packet
7017***** DONE comment header packet
7018***** TODO setup header packet
7019****** TODO codebooks
7020****** TODO time domain transforms
7021****** TODO floors
7022****** TODO residues
7023****** TODO mappings
7024****** TODO modes
7025***** TODO audio packet decoding
7026****** TODO decode packet type flag
7027****** TODO decode mode number
7028****** TODO decode window shape
7029****** TODO decode floor
7030****** TODO decode residue into residue vectors
7031****** TODO inverse channel coupling of reside vectors
7032****** TODO generate floor curve from decoded floor data
7033****** TODO compute dot product of floor and residue
7034       producing audio spectrum vector
7035****** TODO inverse monolithic transform of audio spectrum vector
7036****** TODO overlap / add left-hand output with right-hand output
7037****** TODO store right-hand data from transform of current frame
7038       for future lapping
7039****** TODO if not first frame, return results of overlap/add as audio
7040**** TODO Add VorbisDecoder to VorbisAudio's to_pcm() method
7041***** DONE handle identification header packet
7042***** DONE skip comment header packet
7043***** TODO handle setup header packet
7044****** TODO codebooks
7045       store result of codebooks decoding somewhere
7046****** TODO time domain transforms
7047       store result of time domain transforms somewhere?
7048****** TODO floors
7049****** TODO residues
7050****** TODO mappings
7051****** TODO modes
7052***** TODO perform data decoding
7053****** TODO decode packet type flag
7054****** TODO decode mode number
7055****** TODO decode window shape
7056****** TODO decode floor
7057****** TODO decode residue into residue vectors
7058****** TODO inverse channel coupling of reside vectors
7059****** TODO generate floor curve from decoded floor data
7060****** TODO compute dot product of floor and residue
7061       producing audio spectrum vector
7062****** TODO inverse monolithic transform of audio spectrum vector
7063****** TODO overlap / add left-hand output with right-hand output
7064****** TODO store right-hand data from transform of current frame
7065       for future lapping
7066****** TODO if not first frame, return results of overlap/add as audio
7067***** TODO return FloatFrameList via read_float() method
7068***** TODO return FrameList via read() method
7069**** TODO Add unit tests for VorbisDecoder
7070*** TODO Add Vorbis encoder
7071** TODO Add internal Opus codec
7072** TODO Add internal Monkey's Audio codec
7073   This needs to be assimilated with a native decoder/encoder.
7074   I've seen these out in the wild, but only rarely.
7075*** TODO Add Python-based Monkey's Audio decoder
7076*** TODO Document Monkey's Audio decoding
7077*** TODO Add C-based Monkey's Audio decoder
7078*** TODO Add Python-based Monkey's Audio encoder
7079*** TODO Document Monkey's Audio encoding
7080*** TODO Add C-based Monkey's Audio encoder
7081*** TODO Add Monkey's Audio-specific unit tests
7082*** TODO Test encoder/decoder against reference
7083*** TODO Restore AudioFile-compatible Python interface
7084* TODO Finish version 3.0
7085** DONE Make compatible with Python 2.7 and 3.X
7086   Like Urwid and PLY, the same code should work on both versions.
7087*** DONE Update setup script
7088*** DONE Update C-based extensions
7089    Use #ifdefs as needed to support both Python versions.
7090
7091    - [X] audiotools.pcm
7092    - [X] audiotools.pcmconverter
7093    - [X] audiotools.replaygain
7094    - [X] audiotools.decoders
7095    - [X] audiotools.encoders
7096    - [X] audiotools.bitstream
7097    - [X] audiotools._ogg
7098    - [X] audiotools._accuraterip
7099    - [X] audiotools.output
7100*** DONE Update Python modules
7101**** DONE audiotools
7102     - [X] __init__
7103     - [X] accuraterip
7104     - [X] aiff
7105     - [X] ape
7106     - [X] au
7107     - [X] dvda
7108     - [X] flac
7109     - [X] freedb
7110     - [X] id3
7111     - [X] id3v1
7112     - [X] image
7113     - [X] m4a_atoms
7114     - [X] m4a
7115     - [X] mp3
7116     - [X] musicbrainz
7117     - [X] ogg
7118     - [X] opus
7119     - [X] player
7120     - [X] shn
7121     - [X] text
7122     - [X] tta
7123     - [X] ui
7124     - [X] vorbiscomment
7125     - [X] vorbis
7126     - [X] wavpack
7127     - [X] wav
7128**** DONE audiotools.cue
7129     - [X] __init__
7130     - [X] tokrules
7131     - [X] yaccrules
7132**** DONE audiotools.toc
7133     - [X] __init__
7134     - [X] tokrules
7135     - [X] yaccrules
7136*** DONE Update utilities
7137    - [X] audiotools-config
7138    - [X] cd2track
7139    - [X] cdinfo
7140    - [X] cdplay
7141    - [X] coverdump
7142    - [X] covertag
7143    - [X] coverview
7144    - [X] dvda2track
7145    - [X] dvdainfo
7146    - [X] track2cd
7147    - [X] track2track
7148    - [X] trackcat
7149    - [X] trackcmp
7150    - [X] trackinfo
7151    - [X] tracklength
7152    - [X] tracklint
7153    - [X] trackplay
7154    - [X] trackrename
7155    - [X] tracksplit
7156    - [X] tracktag
7157    - [X] trackverify
7158*** DONE Update Python-based decoders
7159    - [X] alac
7160    - [X] flac
7161    - [X] shn
7162    - [X] tta
7163    - [X] wavpack
7164*** DONE Update Python-based encoders
7165    - [X] alac
7166    - [X] flac
7167    - [X] shn
7168    - [X] tta
7169    - [X] wavpack
7170*** DONE Update documentation
7171*** DONE Update unit tests
7172    - [X] test
7173    - [X] test_core
7174    - [X] test_formats
7175    - [X] test_metadata
7176    - [X] test_streams
7177    - [X] test_utils
7178**** TODO Update unit test utilities
7179     - [ ] error.py
7180     - [ ] test_cdrdao
7181     - [ ] test_cdrecord
7182*** DONE Ensure unit tests pass
7183**** DONE Python 2.7
7184***** DONE Lib
7185      - [X] core
7186      - [X] cuesheet
7187      - [X] cdio
7188      - [X] freedb
7189      - [X] image
7190      - [X] musicbrainz
7191      - [X] accuraterip
7192      - [X] pcm
7193      - [X] bitstream
7194      - [X] replaygain
7195      - [X] resample
7196      - [X] tocfile
7197      - [X] verify
7198      - [X] ogg
7199***** DONE Format
7200      - [X] audiofile
7201      - [X] lossless
7202      - [X] lossy
7203      - [X] aiff
7204      - [X] alac
7205      - [X] au
7206      - [X] dvda
7207      - [X] flac
7208      - [X] m4a
7209      - [X] mp2
7210      - [X] mp3
7211      - [X] oggflac
7212      - [X] opus
7213      - [X] shorten
7214      - [X] sines
7215      - [X] tta
7216      - [X] vorbis
7217      - [X] wave
7218      - [X] wavpack
7219***** DONE Metadata
7220      - [X] metadata
7221      - [X] flac
7222      - [X] wavpack
7223      - [X] id3v1
7224      - [X] id3v2
7225      - [X] vorbis
7226      - [X] opus
7227      - [X] m4a
7228      - [X] tta
7229***** DONE Util
7230      - [X] audiotools-config
7231      - [X] cd2track
7232      - [ ] cdinfo
7233      - [X] cdplay
7234      - [X] coverdump
7235      - [X] covertag
7236      - [X] coverview
7237      - [X] dvda2track
7238      - [X] dvdainfo
7239      - [X] track2cd
7240      - [X] track2track
7241      - [X] trackcat
7242      - [X] trackcmp
7243      - [X] trackinfo
7244      - [X] tracklength
7245      - [X] tracklint
7246      - [X] trackplay
7247      - [X] trackrename
7248      - [X] tracksplit
7249      - [X] tracktag
7250      - [X] trackverify
7251**** DONE Python 3
7252***** DONE Lib
7253      - [X] core
7254      - [X] cuesheet
7255      - [X] cdio
7256      - [X] freedb
7257      - [X] image
7258      - [X] musicbrainz
7259      - [X] accuraterip
7260      - [X] pcm
7261      - [X] bitstream
7262      - [X] replaygain
7263      - [X] resample
7264      - [X] tocfile
7265      - [X] verify
7266      - [X] ogg
7267***** DONE Format
7268      - [X] audiofile
7269      - [X] lossless
7270      - [X] lossy
7271      - [X] aiff
7272      - [X] alac
7273      - [X] au
7274      - [X] dvda
7275      - [X] flac
7276      - [X] m4a
7277      - [X] mp2
7278      - [X] mp3
7279      - [X] oggflac
7280      - [X] opus
7281      - [X] shorten
7282      - [X] sines
7283      - [X] tta
7284      - [X] vorbis
7285      - [X] wave
7286      - [X] wavpack
7287***** DONE Metadata
7288      - [X] metadata
7289      - [X] flac
7290      - [X] wavpack
7291      - [X] id3v1
7292      - [X] id3v2
7293      - [X] vorbis
7294      - [X] opus
7295      - [X] m4a
7296      - [X] tta
7297***** DONE Util
7298      - [X] audiotools-config
7299      - [X] cd2track
7300      - [X] cdinfo
7301      - [X] cdplay
7302      - [X] coverdump
7303      - [X] covertag
7304      - [X] coverview
7305      - [X] dvda2track
7306      - [X] dvdainfo
7307      - [X] track2cd
7308      - [X] track2track
7309      - [X] trackcat
7310      - [X] trackcmp
7311      - [X] trackinfo
7312      - [X] tracklength
7313      - [X] tracklint
7314      - [X] trackplay
7315      - [X] trackrename
7316      - [X] tracksplit
7317      - [X] tracktag
7318      - [X] trackverify
7319** DONE Add context manager support
7320   Having file-like objects work with "with" context managers
7321   should hopefully make file handle management a bit simpler.
7322*** DONE BitstreamReader
7323    Call .close() on internal stream,
7324    eat any exception (like the "file" object does) and return a False value
7325**** DONE Add documentation
7326**** DONE Add unit tests
7327*** DONE BitstreamWriters
7328    If no exception, call .flush() on internal stream and eat any exception.
7329    Then call .close() on internal stream and eat any exception also.
7330    Returns a False value.
7331**** DONE Add documentation
7332**** DONE Add unit tests
7333*** DONE PCMReaders
7334    Call .close() and eat any exception (like the "file" object does).
7335    - [X] aiff/AIFFReader
7336    - [X] au/AuReader
7337    - [X] audiotools/BufferedPCMReader
7338    - [X] audiotools/CounterPCMReader
7339    - [X] audiotools/LimitedPCMReader
7340    - [X] audiotools/PCMCat
7341    - [X] audiotools/PCMReader
7342    - [X] audiotools/PCMReaderDeHead
7343    - [X] audiotools/PCMReaderError
7344    - [X] audiotools/PCMReaderHead
7345    - [X] audiotools/PCMReaderProgress
7346    - [X] audiotools/ReorderedPCMReader
7347    - [X] decoders/ALACDecoder
7348    - [X] decoders/DVDA_Title
7349    - [X] decoders/FlacDecoder
7350    - [X] decoders/MP3Decoder
7351    - [X] decoders/OggFlacDecoder
7352    - [X] decoders/OpusDecoder
7353    - [X] decoders/SHNDecoder
7354    - [X] decoders/SameSample
7355    - [X] decoders/Sine_Mono
7356    - [X] decoders/Sine_Simple
7357    - [X] decoders/Sine_Stereo
7358    - [X] decoders/TTADecoder
7359    - [X] decoders/VorbisDecoder
7360    - [X] decoders/WavPackDecoder
7361    - [X] player/ThreadedPCMReader
7362    - [X] py_decoders/ALACDecoder
7363    - [X] py_decoders/FlacDecoder
7364    - [X] py_decoders/SHNDecoder
7365    - [X] py_decoders/TTADecoder
7366    - [X] py_decoders/WavPackDecoder
7367    - [X] test/EXACT_RANDOM_PCM_Reader
7368    - [X] test/Join_Reader
7369    - [X] test/MD5_Reader
7370    - [X] test/RANDOM_PCM_Reader
7371    - [X] test/Variable_Reader
7372    - [X] test_formats/CLOSE_PCM_Reader
7373    - [X] test_formats/ERROR_PCM_Reader
7374    - [X] test_streams/FrameListReader
7375    - [X] test_streams/MD5Reader
7376    - [X] test_streams/Raw
7377    - [X] test_streams/Simple_Sine
7378    - [X] test_streams/WastedBPS16
7379    - [X] wav/WaveReader
7380**** DONE Add documentation
7381**** DONE Add unit tests
7382** DONE Update interactive utilities
7383   Ensure they run under Python 2 and 3
7384   - [X] audiotools/ui.py
7385   - [X] audiotools-config
7386   - [X] cd2track
7387   - [X] cdplay
7388   - [X] dvda2track
7389   - [X] track2track
7390   - [X] trackcat
7391   - [X] trackplay
7392   - [X] trackrename
7393   - [X] tracksplit
7394   - [X] tracktag
7395** TODO Improve test coverage
7396   - [X] set_metadata(None) to all formats
7397*** TODO Check repr on everything
7398    Verifying the contents isn't important;
7399    just make sure calling repr() doesn't throw an exception.
7400*** TODO audiotools/__init__
7401    - [X] output_text
7402    - [X] output_list
7403    - [X] output_table
7404    - [ ] threaded_transfer_framelist_data
7405    - [ ] pcm_cmp
7406    - [ ] pcm_frame_cmp
7407    - [ ] resampled_frame_count
7408    - [ ] read_sheet (IOError)
7409    - [X] iter_first
7410    - [X] iter_last
7411    - [X] most_numerous (len(item_list) == 0)
7412    - [X] most_numerous (all_differ)
7413    - [ ] cddareader_metadata_lookup
7414    - [ ] track_metadata_lookup
7415    - [ ] sheet_metadata_lookup
7416    - [ ] accuraterip_lookup (len(sorted_tracks) == 0)
7417**** TODO MetaData
7418     - [ ] __delattr__
7419     - [ ] empty_fields
7420     - [ ] __unicode__
7421     - [ ] delete_image
7422     - [ ] clean
7423**** TODO AudioFile
7424     - [ ] rich compare operators
7425**** DONE Sheet
7426     - [X] converted
7427     - [X] __eq__
7428     - [X] track_numbers
7429     - [X] track (KeyError)
7430     - [X] pre_gap
7431     - [X] track_offset
7432     - [X] track_length
7433**** DONE SheetTrack
7434     - [X] converted
7435     - [X] indexes
7436     - [X] index (KeyError)
7437     - [X] __eq__
7438**** DONE SheetIndex
7439     - [X] converted
7440     - [X] __eq__
7441**** TODO PCMReaderHead
7442     - [ ] check for negative pcm_frames
7443**** TODO PCMReaderDeHead
7444     - [ ] truncation longer than entire stream
7445     - [ ] read_closed
7446**** DONE ExecProgressQueue
7447*** DONE audiotools/accuraterip
7448    - [X] match_offset
7449*** TODO audiotools/aiff
7450**** TODO AiffAudio
7451     - [ ] verify
7452     - [ ] clean (with metadata)
7453*** TODO audiotools/ape
7454**** TODO ApeTag
7455     - [ ] get
7456     - [ ] index
7457**** DONE ApeTaggedAudio
7458     - [X] set_metadata(None)
7459     - [X] set_metadata with cuesheet
7460     - [X] delete_metadata with ReplayGain
7461     - [X] delete_metadata with cuesheet
7462*** TODO audiotools/cue
7463    - [ ] read_cuesheet_string (with ValueError)
7464**** DONE Track
7465     - [X] filename
7466     - [X] pre_emphasis
7467     - [X] copy_permitted
7468**** TODO audiotools/cue/tokrules
7469     - [ ] with illegal character
7470**** TODO audiotools/cue/yaccrules
7471     - [X] with flags
7472     - [ ] with syntax error
7473*** TODO audiotools/flac
7474**** DONE FlacMetaData
7475     - [X] converted(None)
7476     - [X] clean (multiple STREAMINFO)
7477     - [X] clean (multiple VORBIS_COMMENT)
7478     - [X] clean (multiple SEEKTABLE)
7479     - [X] clean (multiple CUESHEET)
7480     - [X] raw_info()
7481     check with all metadata blocks
7482**** DONE Flac_VORBISCOMMENT
7483     - [X] converted(Flac_VORBISCOMMENT)
7484     should return newly created object
7485**** TODO Flac_Cuesheet
7486     - [ ] track_length
7487     - [ ] get_metadata with catalog
7488     - [ ] converted with padding
7489**** TODO Flac_Cuesheet_track
7490     - [ ] filename
7491**** TODO Flac_PICTURE
7492     - [ ] setattr(type, ???)
7493     - [ ] type_string
7494**** TODO FlacAudio
7495     - [X] set_metadata(None)
7496     - [ ] set_metadata with channel mask in old comment
7497     - [X] get_cuesheet() -> None
7498     - [ ] from_pcm with UnsupportedChannelMask
7499     - [ ] has_foreign_wave_chunks() -> False
7500     - [ ] wave_header_footer() without chunks
7501     - [ ] has_foreign_aiff_chunks() -> False
7502     - [ ] aiff_header_footer() without chunks
7503     - [ ] convert() to WaveContainer
7504     - [ ] convert() to AiffContainer
7505     - [ ] delete_replay_gain() without VORBIS_COMMENT
7506     - [ ] clean on < 128 byte file
7507**** DONE FLAC_Data_Chunk
7508     is this still in use?
7509**** DONE FLAC_SSND_Chunk
7510     is this still in use?
7511**** DONE OggFlacMetaData
7512     - [X] converted(None)
7513**** TODO OggFlacAudio
7514     - [ ] from_pcm with UnsupportedChannelMask
7515*** TODO audiotools/freedb
7516    - [ ] xmcd_metadata without album artist
7517    - [ ] xmcd_metadata without track artist
7518*** TODO audiotools/id3
7519    - [ ] read_id3v2_comment (invalid header)
7520    - [ ] read_id3v2_comment (unsupported version)
7521    - [ ] skip_id3v2_comment (unsupported version)
7522    - [ ] skip_id3v2_comment (IOError)
7523    - [ ] total_id3v2_comments (unsupported version)
7524    - [ ] total_id3v2_comments (IOError)
7525**** TODO ID3v22Comment
7526     - [X] raw_info
7527     - [ ] parse
7528     - [ ] build
7529     - [ ] size
7530     - [ ] clean
7531**** TODO ID3v22_T__Frame
7532     - [ ] number() -> 0, no total
7533     - [ ] number() -> not numerical type
7534     - [ ] total() -> not numerical type
7535**** TODO ID3v22_TXX_Frame
7536**** TODO ID3v22_W__Frame
7537**** TODO ID3v22_WXX_Frame
7538**** TODO ID3v22_COM_Frame
7539     - [ ] clean
7540**** TODO ID3v22_PIC_Frame
7541     - [ ] invalid image from data
7542     - [ ] type_string
7543     - [ ] getattr -> AttributeError
7544     - [ ] setattr(type, ???)
7545     - [ ] setattr(desription, ???)
7546     - [ ] clean
7547**** TODO ID3v22Comment
7548     - [ ] copy
7549     - [ ] parse (invalid header)
7550     - [ ] parse (invalid major version)
7551     - [ ] parse (invalid minor version)
7552     - [ ] parse (TXX, WXX, W??)
7553     - [ ] delitem
7554     - [ ] items
7555     - [ ] getattr -> AttributeError
7556     - [ ] delattr(track/alum_number) unslashed
7557     - [ ] delattr unsupported field
7558     - [X] converted(None)
7559**** TODO ID3v23_TXXX_Frame
7560**** TODO ID3v23_WXXX_Frame
7561**** TODO ID3v23_APIC_Frame
7562     - [ ] __init__ invalid image
7563     - [ ] getattr -> AttributeError
7564     - [ ] setattr(pic_type)
7565     - [ ] setattr(description)
7566     - [ ] setattr(mime_type)
7567     - [ ] clean
7568**** TODO ID3v23Comment
7569     - [ ] parse invalid ID3 header
7570     - [ ] invalid major version
7571     - [ ] invalid minor version
7572     - [ ] parse (WXXX, W???)
7573**** TODO ID3v24_TXXX_Frame
7574     - [ ] __unicode__
7575     - [ ] parse
7576**** TODO ID3v24_APIC_Frame
7577     - [ ] setattr(type)
7578     - [ ] setattr(description)
7579     - [ ] setattr(mime_type)
7580     - [ ] clean
7581**** TODO ID3v24_WXXX_Frame
7582     - [ ] parse
7583**** TODO ID3v24_COMM_Frame
7584     - [ ] clean
7585**** TODO ID3v24Comment
7586     - [ ] parse (invalid ID3 header)
7587     - [ ] parse (invalid major version)
7588     - [ ] parse (invalid minor version)
7589     - [ ] parse (TXXX, WXXX, W???)
7590**** TODO ID3CommentPair
7591     - [ ] init id3v1 is not None
7592     - [X] converted(None)
7593     - [ ] convert(ID3v2Comment)
7594     - [X] raw_info
7595     - [ ] images (id3v2 is None)
7596     - [ ] clean
7597*** TODO audiotools/id3v1
7598**** TODO ID3v1Comment
7599     - [ ] getattr -> AttributeError
7600     - [ ] setattr(non field)
7601     - [ ] delattr(non field)
7602     - [X] converted(None)
7603     - [ ] images
7604***** TODO parse
7605      - [ ] track_name length != 30
7606      - [ ] artist_name length != 30
7607      - [ ] album_name length != 30
7608      - [ ] year length != 4
7609      - [ ] comment length != 28
7610      - [ ] track_number length != 1
7611      - [ ] genre length != 1
7612*** TODO audiotools/image
7613    - [ ] rename classes without __s
7614    - [ ] image_metrics -> InvalidImage
7615**** TODO __JPEG__
7616     - [ ] parse -> IOError
7617**** TODO __PNG__
7618     - [ ] parse -> InvalidPNG
7619     - [ ] parse -> IOError
7620     - [ ] parse -> grayscale
7621     - [ ] parse -> invalid PLTE
7622     - [ ] parse -> grayscale + alpha
7623**** TODO __BMP__
7624     - [ ] parse -> IOError
7625     - [ ] parse -> InvalidBMP
7626**** TODO __GIF__
7627     - [ ] parse -> IOError
7628     - [ ] parse -> InvalidGIF
7629**** TODO __TIFF__
7630     - [ ] parse big-endian TIFF
7631     - [ ] parse invalid magic number
7632*** TODO audiotools/m4a
7633    - [ ] get_m4a_atom -> KeyError
7634    - [ ] get_m4a_atom_offset -> KeyError
7635    - [ ] get_m4a_atom_offset -> IOError/KeyError
7636    - [ ] has_m4a_atom (if (length - 8) <= 0)
7637    - [ ] has_m4a_atom -> IOError
7638**** TODO M4ATaggedAduio
7639     - [ ] get_metadata -> KeyError if no meta
7640     - [ ] update_metadata (generate moov)
7641     - [ ] update_metadata (generate udta)
7642     - [ ] update_metadata (generate meta)
7643     - [X] set_metadata(None)
7644*** TODO audiotools/m4a_atoms
7645**** TODO M4A_Tree_Atom
7646     - [ ] has_child -> False
7647     - [ ] remove_child
7648     - [ ] child_offset -> KeyError
7649**** TODO M4A_META_Atom
7650     - [ ] getattr -> AttributeError
7651     - [ ] setattr -> replace_data_atom
7652     - [ ] delattr -> AttributeError
7653     - [X] converted(None)
7654*** TODO audiotools/mp3
7655**** TODO MP3Audio
7656     - [X] set_metadata(None)
7657     - [ ] update_metadata (id3v2 not None, id3v1 is None)
7658     - [ ] update_metadata (id3v2 None, id3v1 not None)
7659     - [ ] update_metadata (id3v2 None, id3v1 None)
7660     - [ ] clean (output_filename None, metadata None)
7661     - [ ] clean (output_filename not None, metadata None)
7662**** TODO MP2Audio
7663     - [ ] from_pcm (nonstandard sample rate)
7664*** TODO audiotools/opus
7665**** DONE OpusAudio
7666     - [X] init (invalid Ogg magic number)
7667     - [X] init (invalid Ogg version)
7668     - [X] init (invalid Opus type)
7669     - [X] init (invalid Opus version)
7670     - [X] init (invalid Opus channels)
7671     - [X] get_replay_gain
7672     - [X] set_replay_gain
7673     - [X] delete_replay_gain
7674*** TODO audiotools/shn
7675**** TODO ShortenAudio
7676     - [ ] __init__ -> IOError
7677     - [ ] has_foreign_wave_chunks -> True
7678     - [ ] has_foreign_wave_chunks -> odd-sized wave chunks
7679     - [ ] has_foreign_wave_chunks -> no foreign chunks found
7680     - [ ] has_foreign_aiff_chunks -> True
7681     - [ ] has_foreign_aiff_chunks -> odd-sized aiff chunks
7682     - [ ] has_foreign_aiff_chunks -> no foreign chunks found
7683     - [ ] from_pcm using external function
7684     - [ ] from_pcm (len(footer) == 0)
7685     - [ ] convert to wave container
7686     - [ ] convert to aiff container
7687*** TODO audiotools/toc
7688    - [ ] read_tocfile -> IOError
7689    - [ ] read_tocfile_string -> SheetException
7690    - [ ] write_tocfile
7691**** TODO TOCFile
7692     - [ ] get_metadata (catalog not None, cd_text not None)
7693**** TODO TOCTrack
7694     - [ ] get_metadata (isrc not None, cd_text not None)
7695     - [X] filename
7696     - [X] pre_emphasis
7697     - [X] copy_permitted (not False)
7698**** TODO audiotools/toc/tokrules
7699     - [ ] illegal character
7700**** TODO audiotools/toc/yaccrules
7701     - [ ] syntax error
7702*** TODO audiotools/tta
7703**** TODO TrueAudio
7704     - [ ] init -> invalid signature
7705     - [ ] init -> invalid format
7706     - [ ] from_pcm -> IOError to EncodingError
7707     - [ ] from_pcm -> frame count mismatch
7708     - [X] set_metadata(None)
7709     - [X] cuesheet in old metadata
7710     - [X] cuesheet in new metadata
7711     - [ ] get_cuesheet -> CueException
7712     - [X] set_cuesheet(None)
7713     - [ ] clean when metadata is None
7714*** TODO audiotools/vorbis
7715**** TODO VorbisAudio
7716     - [X] init -> invalid Ogg magic number
7717     - [X] init -> invalid Ogg version
7718     - [X] init -> invalid Vorbis type
7719     - [X] init -> invalid Vorbis header
7720     - [X] init -> invalid Vorbis version
7721     - [X] init -> invalid Vorbis framing bit
7722**** TODO VorbisChannelMask
7723     - [ ] channels
7724*** TODO audiotools/vorbiscomment
7725**** TODO VorbisComment
7726     - [ ] values
7727     - [ ] __contains__
7728     - [ ] getattr(non-MetaData attrib)
7729     - [ ] delattr(non-MetaData attrib)
7730     - [X] converted(None)
7731     - [ ] converted(FlacMetaData without VORBISCOMMENT)
7732     - [ ] clean(track_total/album_total with leading 0s)
7733*** TODO audiotools/wav
7734    - [X] wave_header -> total size too large
7735**** TODO RIFF_Chunk
7736     - [ ] handle odd sized chunks
7737**** TODO WaveReader
7738     - [ ] init -> file too small
7739     - [ ] init -> invalid RIFF
7740     - [ ] init -> invalid WAVE
7741     - [ ] init -> chunk header too small
7742     - [ ] init -> invalid chunk ID
7743     - [ ] init -> data chunk before fmt chunk
7744     - [ ] init -> no audio chunks
7745     - [ ] init -> no data chunk
7746     - [ ] read -> data chunk too small
7747**** TODO WaveAudio
7748     - [X] from_pcm -> total_pcm_frames too large
7749     - [X] from_pcm -> total_pcm_frames mismatch
7750     - [ ] track_name(format=None)
7751     - [ ] verify -> not enough data chunk
7752     - [ ] clean -> non audio chunk
7753*** TODO audiotools/wavpack
7754**** TODO WavPackAudio
7755     - [ ] blocks -> blocks_iter -> non wvpk block header
7756     - [ ] __read_info__ -> non wvpk block ID
7757     - [ ] __read_info__ -> nonstandard sample rate
7758     - [ ] __read_info__ -> no channel info sub block
7759     - [X] from_pcm -> total_pcm_frames mismatch
7760     - [ ] fmt_chunk
7761     - [ ] get_cuesheet -> CueException
7762     - [X] set_cuesheet(None)
7763*** TODO audiotools/py_decoders/flac
7764**** TODO FlacDecoder
7765     - [ ] init -> invalid FLAC file
7766     - [ ] read -> MD5 checksum mismatch
7767     - [ ] read -> invalid channel assignment
7768     - [ ] read -> CRC16 mismatch
7769     - [ ] read_frame_header -> invalid sync code
7770     - [ ] read_frame_header -> invalid sample rate
7771     - [ ] read_frame_header -> invalid bits-per-sample
7772     - [ ] read_frame_header -> CRC8 mismatch
7773     - [ ] read_subframe_header -> invalid subframe type
7774     - [ ] read_fixed_subframe -> invalid order
7775     - [ ] read_residual_partition(coding 0) -> escape code
7776     - [ ] read_residual_partition(coding 1) -> escape code
7777     - [ ] close
7778*** TODO audiotools/py_decoders/shn
7779**** TODO SHNDecoder
7780     - [ ] init -> unsupported file type
7781     - [ ] init -> non-audio RIFF chunks
7782     - [ ] init -> AIFF chunks
7783     - [ ] read_header -> invalid magic number
7784     - [ ] read_header -> unsupported version
7785     - [ ] read -> QLPC options
7786*** TODO audiotools/py_decoders/tta
7787**** TODO TTADecoder
7788     - [ ] init -> CRC mismatch in header
7789     - [ ] init -> CRC mismatch in seektable
7790     - [ ] init -> CRC mismatch in frame
7791     - [ ] close
7792*** TODO audiotools/py_decoders/wavpack
7793**** TODO WavPackDecoder
7794     - [ ] init -> nonstandard sample rate
7795     - [ ] read -> MD5 mismatch at end of stream
7796     - [ ] read -> IOError/ValueError
7797     - [ ] close
7798**** TODO Block_Header
7799     - [ ] invalid block ID
7800**** TODO Sub_Block
7801     - [ ] total_size large
7802**** TODO read_block
7803     - [ ] weights before terms
7804     - [ ] samples before terms
7805     - [ ] invalid samples byte count
7806     - [ ] bitstream before entropy vars
7807     - [ ] decorrelation weights not found
7808     - [ ] decorrelation samples not found
7809     - [ ] bitstream not found
7810     - [ ] CRC mismatch
7811**** TODO read_decorrelation_terms
7812     - [ ] invalid passes count
7813     - [ ] invalid term
7814**** TODO read_decorrelation_weights
7815     - [ ] invalid weight count
7816**** TODO read_decorrelation_samples
7817     - [ ] sub_block_size < 8, two channels
7818     - [ ] sub_block_size < 8, one channel
7819     - [ ] invalid term
7820*** TODO audiotools/py_encoders/alac
7821    - [ ] encode_frame -> residual overflow
7822    - [ ] tukey_window -> n > window2
7823    - [ ] encode_residuals -> residual overflow
7824*** TODO audiotools/py_encoders/flac
7825    - [ ] write_utf8 (value > 127)
7826    - [ ] tukey_window (n > window2)
7827    - [ ] compute_lpc_coefficients (estimate_best_order)
7828    - [ ] estimate_best_order
7829*** TODO audiotools/py_encoders/shn
7830**** TODO encode_shn
7831     - [X] all zeroes
7832     - [ ] write footer
7833*** TODO audiotools/py_encoders/wavpack
7834**** TODO encode_wavpack
7835     - [ ] write wave footer
7836**** TODO write_block
7837     - [ ] all samples are 0
7838** DONE Calculate ReplayGain at rip-time
7839   For utilities that operated single-threaded,
7840   calculate ReplayGain during operation rather
7841   than put off until ripping is done.
7842*** DONE cd2track
7843*** DONE dvda2track
7844** TODO Improve utility test coverage
7845*** TODO Add some basic tests to ensure they still works
7846    - [ ] cdinfo
7847    - [ ] cdplay
7848    - [ ] trackplay
7849    - [ ] trackverify
7850** DONE Re-add DVD-A support using libdvdaudio
7851   Supporting DVD-A in an external library should provide a useful decoupling.
7852*** DONE Add audiotools.dvda module
7853    This contains thin wrappers around libdvdaudio structures and functions.
7854**** DONE Add DVDA object
7855**** DONE Add Titleset object
7856**** DONE Add Title object
7857**** DONE Add Track object
7858**** DONE Add TrackReader object
7859**** DONE Add documentation
7860*** DONE Re-add dvda2track script
7861**** DONE Re-add dvda2track.1 man page
7862*** DONE Re-add dvdainfo script
7863**** DONE Re-add dvdainfo.1 man page
7864*** DONE Update setup script
7865    Probe for libdvdaudio and add module and scripts if present,
7866    analagous to libcdio.
7867* TODO Finish version 3.1
7868** TODO Cleanup chunk-based interface (again)
7869   There needs to be a single-pass approach to encoding chunked files
7870   that doesn't involve pulling out the chunk data ahead of time.
7871
7872   Perhaps some sort of chunk iterator which indicates the appropriate
7873   place where the PCM data will go.
7874** TODO Make AudioFile.available() classmethod finer-grained
7875   That is, formats can be readable but not writable, or taggable but
7876   not readable or writeable.  This will allow read-only support for
7877   some obscure formats like .tak or .ape by porting implementations
7878   from ffmpeg but without having to build encoders for them as well.
7879*** TODO Add AudioFile.supports_to_pcm()
7880    - [ ] AudioFile
7881    - [ ] ALACAudio
7882    - [ ] AiffAudio
7883    - [ ] AuAudio
7884    - [ ] FlacAudio
7885    - [ ] M4AAudio
7886    - [ ] MP2Audio
7887    - [ ] MP3Audio
7888    - [ ] OggFlacAudio
7889    - [ ] OpusAudio
7890    - [ ] ShortenAudio
7891    - [ ] TrueAudio
7892    - [ ] VorbisAudio
7893    - [ ] WavPackAudio
7894    - [ ] WaveAudio
7895*** TODO Add AudioFile.supports_from_pcm()
7896    - [ ] AudioFile
7897    - [ ] ALACAudio
7898    - [ ] AiffAudio
7899    - [ ] AuAudio
7900    - [ ] FlacAudio
7901    - [ ] M4AAudio
7902    - [ ] MP2Audio
7903    - [ ] MP3Audio
7904    - [ ] OggFlacAudio
7905    - [ ] OpusAudio
7906    - [ ] ShortenAudio
7907    - [ ] TrueAudio
7908    - [ ] VorbisAudio
7909    - [ ] WavPackAudio
7910    - [ ] WaveAudio
7911*** TODO Add AudioFile.supports_verify()
7912    in most instances, this should be the same as supports_to_pcm()
7913    - [ ] AudioFile
7914    - [ ] ALACAudio
7915    - [ ] AiffAudio
7916    - [ ] AuAudio
7917    - [ ] FlacAudio
7918    - [ ] M4AAudio
7919    - [ ] MP2Audio
7920    - [ ] MP3Audio
7921    - [ ] OggFlacAudio
7922    - [ ] OpusAudio
7923    - [ ] ShortenAudio
7924    - [ ] TrueAudio
7925    - [ ] VorbisAudio
7926    - [ ] WavPackAudio
7927    - [ ] WaveAudio
7928*** DONE Add AudioFile.supports_metadata()
7929*** TODO Remove AudioFile.missing_components() ?
7930    Leave this job to setup.py and audiotools-config, perhaps.
7931*** TODO Utilities
7932**** TODO audiotools-config
7933     Needs to display format availability in a more fine-grained fashion.
7934**** TODO cd2track
7935     - [ ] output format must supports_from_pcm()
7936**** TODO track2cd
7937     - [ ] input format must supports_to_pcm()
7938**** TODO track2track
7939     - [ ] input format(s) must supports_to_pcm()
7940     - [ ] output format must supports_from_pcm()
7941**** TODO trackcat
7942     - [ ] input format(s) must supports_to_pcm()
7943     - [ ] output format must supports_from_pcm()
7944**** TODO trackcmp
7945     - [ ] input format(s) must supports_to_pcm()
7946**** TODO trackplay
7947     - [ ] input format(s) must supports_to_pcm()
7948**** TODO tracksplit
7949     - [ ] input format must supports_to_pcm()
7950     - [ ] output format must supports_from_pcm()
7951**** TODO tracktag
7952     - [ ] files must supports_metadata()
7953**** TODO trackverify
7954     - [ ] input format must supports_verify()
7955*** TODO Update documentation
7956*** TODO Update unit tests
7957** TODO Add seeking to Ogg FLAC decoder
7958   Might as well make the format consistent
7959** TODO Support building cue sheets from discs
7960   Find some way of grabbing the index points from a disc
7961   so cuesheets can be populated.
7962** TODO Cleanup AAC support
7963   Although the fdk-aac library doesn't have a compatible license,
7964   there must be *some* way to support AAC without the nasty
7965   hacks in place now.
7966