|
- #!/usr/bin/env python
- # Copyright 2009 John-Mark Gurney <jmg@funkthat.com>
- '''Audio Raw Converter'''
-
- from DIDLLite import AudioItem, Resource, ResourceList
- from FSStorage import registerklassfun
-
- from twisted.web import resource, server
- from twisted.internet.interfaces import IPullProducer
- from zope.interface import implements
-
- decoders = {}
- try:
- import flac
- decoders['flac'] = flac.FLACDec
- except ImportError:
- pass
-
- mtformat = {
- 16: 'audio/l16', # BE signed
- # 8: 'audio/l8', unsigned
- }
-
- def makemtfromdec(dec):
- return '%s;rate=%d;channels=%d' % (mtformat[dec.bitspersample],
- dec.samplerate, dec.channels)
-
- class DecoderProducer:
- implements(IPullProducer)
-
- def __init__(self, consumer, decoder, tbytes, skipbytes):
- self.decoder = decoder
- self.consumer = consumer
- self.tbytes = tbytes
- self.skipbytes = skipbytes
- consumer.registerProducer(self, False)
-
- def resumeProducing(self):
- r = self.decoder.read(8*1024)
- if r:
- #print 'DPrP:', len(r)
- if self.skipbytes:
- cnt = min(self.skipbytes, len(r))
- r = r[cnt:]
- self.skipbytes -= cnt
-
- send = min(len(r), self.tbytes)
- r = r[:send]
- self.tbytes -= len(r)
- self.consumer.write(r)
- if self.tbytes:
- return
-
- print 'DPurP'
- self.consumer.unregisterProducer()
-
- def stopProducing(self):
- print 'DPsP'
- self.decoder.close()
- self.decoder = None
-
- class AudioResource(resource.Resource):
- isLeaf = True
-
- def __init__(self, f, dec):
- resource.Resource.__init__(self)
- self.f = f
- self.dec = dec
-
- def calcrange(self, rng, l):
- rng = rng.strip()
- unit, rangeset = rng.split('=')
- assert unit == 'bytes', `unit`
- start, end = rangeset.split('-')
- start = int(start)
- if end:
- end = int(end)
- else:
- end = l
-
- return start, end - start + 1
-
- def render(self, request):
- decoder = self.dec(self.f)
- request.setHeader('content-type', makemtfromdec(decoder))
- bytespersample = decoder.channels * decoder.bitspersample / 8
- tbytes = decoder.totalsamples * bytespersample
- skipbytes = 0
-
- request.setHeader('content-length', tbytes)
- request.setHeader('accept-ranges', 'bytes')
-
- if request.requestHeaders.hasHeader('range'):
- print 'range req:', `request.requestHeaders.getRawHeaders('range')`
- start, cnt = self.calcrange(
- request.requestHeaders.getRawHeaders('range')[0],
- tbytes)
- skipbytes = start % bytespersample
-
- print 'going:', start / bytespersample
- decoder.goto(start / bytespersample)
- print 'there'
-
- request.setHeader('content-length', cnt)
- request.setHeader('content-range', 'bytes %s-%s/%s' %
- (start, start + cnt - 1, tbytes))
- tbytes = cnt
-
- if request.method == 'HEAD':
- return ''
-
- DecoderProducer(request, decoder, tbytes, skipbytes)
- print 'producing render'
-
- # and make sure the connection doesn't get closed
- return server.NOT_DONE_YET
-
- class AudioRaw(AudioItem):
- def __init__(self, *args, **kwargs):
- self.file = kwargs.pop('file')
- nchan = kwargs.pop('channels')
- samprate = kwargs.pop('samplerate')
- bitsps = kwargs.pop('bitspersample')
- samples = kwargs.pop('samples')
- self.totalbytes = nchan * samples * bitsps / 8
-
- try:
- mt = mtformat[bitsps]
- except KeyError:
- raise KeyError('No mime-type for audio format: %s.' %
- `origfmt`)
-
- self.mt = '%s;rate=%d;channels=%d' % (mt, samprate, nchan)
-
- kwargs['content'] = AudioResource(self.file,
- kwargs.pop('decoder'))
- AudioItem.__init__(self, *args, **kwargs)
-
- self.url = '%s/%s' % (self.cd.urlbase, self.id)
- self.res = ResourceList()
- r = Resource(self.url, 'http-get:*:%s:*' % self.mt)
- r.size = self.totalbytes
- r.duration = float(samples) / samprate
- r.bitrate = nchan * samprate * bitsps / 8
- r.sampleFrequency = samprate
- r.bitsPerSample = bitsps
- r.nrAudioChannels = nchan
- self.res.append(r)
-
- def detectaudioraw(origpath, fobj):
- for i in decoders.itervalues():
- try:
- obj = i(origpath)
- # XXX - don't support down sampling yet
- if obj.bitspersample not in (8, 16):
- continue
-
- return AudioRaw, {
- 'decoder': i,
- 'file': origpath,
- 'channels': obj.channels,
- 'samplerate': obj.samplerate,
- 'bitspersample': obj.bitspersample,
- 'samples': obj.totalsamples,
- }
- except:
- #import traceback
- #traceback.print_exc()
- pass
-
- return None, None
-
- registerklassfun(detectaudioraw, True)
|