Example #1
0
    def playback(self):
        """Plays the saved .wav file, as just recorded or resampled
        """
        if not self.savedFile or not os.path.isfile(self.savedFile):
            msg = '%s: Playback requested but no saved file' % self.loggingId
            logging.error(msg)
            raise ValueError(msg)

        # prepare a player for this file:
        t0 = core.getTime()
        self.sfplayer = SfPlayer(self.savedFile, speed=1, loop=False)
        self.sfplayer2 = self.sfplayer.mix(
            2)  # mix(2) -> 2 outputs -> 2 speakers
        self.sfplayer2.out()
        logging.exp('%s: Playback: prep %.3fs' %
                    (self.loggingId, core.getTime() - t0))

        # play the file; sfplayer was created during record:
        t0 = core.getTime()
        self.sfplayer.play()
        core.wait(self.duration)  # set during record()
        t1 = core.getTime()

        logging.exp('%s: Playback: play %.3fs (est) %s' %
                    (self.loggingId, t1 - t0, self.savedFile))
Example #2
0
    def playback(self):
        """Plays the saved .wav file which was just recorded
        """
        if not self.savedFile or not os.path.isfile(self.savedFile):
            msg = '%s: Playback requested but no saved file' % self.loggingId
            logging.error(msg)
            raise ValueError(msg)
    
        # prepare a player for this file:
        t0 = time.time()
        self.sfplayer = SfPlayer(self.savedFile, speed=1, loop=False)
        self.sfplayer2 = self.sfplayer.mix(2) # mix(2) -> 2 outputs -> 2 speakers
        self.sfplayer2.out()
        logging.exp('%s: Playback: prep %.3fs' % (self.loggingId, time.time()-t0))

        # play the file; sfplayer was created during record:
        t0 = time.time()
        self.sfplayer.play()
        time.sleep(self.duration) # set during record()
        t1 = time.time()

        logging.exp('%s: Playback: play %.3fs (est) %s' % (self.loggingId, t1-t0, self.savedFile))
Example #3
0
class SimpleAudioCapture():
    """Capture a sound sample from the default sound input, and save to a file.
        
        Execution will block until the recording is finished.
        
        **Example**::
        
            from psychopy import microphone
            
            microphone.switchOn(sampleRate=16000) # do once when starting, can take 2-3s
            
            mic = microphone.SimpleAudioCapture()  # prepare to record; only one can be active
            mic.record(1)  # record for 1.000 seconds, save to a file
            mic.playback()
            savedFileName = mic.savedFile
            
            microphone.switchOff() # do once, at exit 
        
        Also see Builder Demo "voiceCapture"
            
        :Author: Jeremy R. Gray, March 2012
    """ 
    def __init__(self, name='mic', file='', saveDir=''):
        """
        :Parameters:
                name :
                    Stem for the output file, also used in logging.
                file :
                    optional file name to use; default = 'name-onsetTimeEpoch.wav'
                saveDir :
                    Directory to use for output .wav files.
                    If a saveDir is given, it will return 'saveDir/file'. 
                    If no saveDir, then return abspath(file)
        """
        self.name = name
        self.saveDir = saveDir
        if file:
            self.wavOutFilename = file
        else:
            self.wavOutFilename = os.path.join(self.saveDir, name + ONSET_TIME_HERE +'.wav')
        if not self.saveDir:
            self.wavOutFilename = os.path.abspath(self.wavOutFilename)

        self.onset = None # becomes onset time, used in filename
        self.savedFile = False # becomes saved file name
        self.status = NOT_STARTED # for Builder component
        
        # pyo server good to go? 
        if not serverCreated():
            raise AttributeError('pyo server not created')
        if not serverBooted():
            raise AttributeError('pyo server not booted')
        
        self.loggingId = self.__class__.__name__
        if self.name:
            self.loggingId += ' ' + self.name

    def __del__(self):
        pass
    def reset(self):
        """Restores to fresh state, ready to record again"""
        logging.exp('%s: resetting at %.3f' % (self.loggingId, time.time()))
        self.__del__()
        self.__init__(name=self.name, saveDir=self.saveDir)
    def record(self, sec, block=True):
        """Capture sound input for duration <sec>, save to a file.
        
        Return the path/name to the new file. Uses onset time (epoch) as
        a meaningful identifier for filename and log.
        """
        RECORD_SECONDS = float(sec)
        self.onset = time.time() # note: report onset time in log, and use in filename
        logging.data('%s: Record: onset %.3f, capture %.3fs' %
                     (self.loggingId, self.onset, RECORD_SECONDS) )
        
        self.savedFile = self.wavOutFilename.replace(ONSET_TIME_HERE, '-%.3f' % self.onset)
        inputter = Input(chnl=0, mul=1) # chnl=[0,1] for stereo input
        t0 = time.time()
        # launch the recording, saving to file:
        recorder = Record(inputter,
                        self.savedFile,
                        chnls=2,
                        fileformat=0, # .wav format
                        sampletype=0,
                        buffering=4) # 4 is default
        # launch recording, block as needed, and clean up:
        clean = Clean_objects(RECORD_SECONDS, recorder) # set up to stop recording
        clean.start() # the timer starts now, ends automatically whether block or not
        if block:
            time.sleep(RECORD_SECONDS - 0.0008) # Clean_objects() set-up takes ~0.0008s, for me
            logging.exp('%s: Record: stop. %.3f, capture %.3fs (est)' %
                     (self.loggingId, time.time(), time.time() - t0) )
        else:
            logging.exp('%s: Record: return immediately, no blocking' %
                     (self.loggingId) )
        
        self.duration = RECORD_SECONDS # used in playback()

        return self.savedFile # filename, or None
        
    def playback(self):
        """Plays the saved .wav file which was just recorded
        """
        if not self.savedFile or not os.path.isfile(self.savedFile):
            msg = '%s: Playback requested but no saved file' % self.loggingId
            logging.error(msg)
            raise ValueError(msg)
    
        # prepare a player for this file:
        t0 = time.time()
        self.sfplayer = SfPlayer(self.savedFile, speed=1, loop=False)
        self.sfplayer2 = self.sfplayer.mix(2) # mix(2) -> 2 outputs -> 2 speakers
        self.sfplayer2.out()
        logging.exp('%s: Playback: prep %.3fs' % (self.loggingId, time.time()-t0))

        # play the file; sfplayer was created during record:
        t0 = time.time()
        self.sfplayer.play()
        time.sleep(self.duration) # set during record()
        t1 = time.time()

        logging.exp('%s: Playback: play %.3fs (est) %s' % (self.loggingId, t1-t0, self.savedFile))
Example #4
0
class AudioCapture(object):
    """Capture a sound sample from the default sound input, and save to a file.
        
        Untested whether you can have two recordings going on simultaneously.
        
        **Examples**::
        
            from psychopy import microphone
            from psychopy import event, visual  # for key events
            
            microphone.switchOn(sampleRate=16000)  # do once
            
            # Record for 1.000 seconds, save to mic.savedFile
            mic = microphone.AudioCapture()
            mic.record(1)
            mic.playback()
            
            # Resample, creates a new file discards orig
            mic.resample(48000, keep=False)
            
            # Record new file for 60 sec or until key 'q'
            w = visual.Window()  # needed for key-events
            mic.reset()
            mic.record(60, block=False)
            while mic.recorder.running:
                if 'q' in event.getKeys():
                    mic.stop()
            
            microphone.switchOff()  # do once 
        
        Also see Builder Demo "voiceCapture".
            
        :Author: Jeremy R. Gray, March 2012
    """
    class _Recorder(object):
        """Class for internal object to make an audio recording using pyo.
        
        Never needed by end-users; only used internally in __init__:
            self.recorder = _Recorder(None) # instantiate, global
        Then in record(), do:
            self.recorder.run(file, sec)
        This sets recording parameters, starts recording.
        To stop a recording that is in progress, do
            self.stop()
        This class never handles blocking; AudioCapture has to do that.
        
        Motivation: Doing pyo Record from within a function worked most of the time,
        but failed catastrophically ~1% of time with a bus error. Seemed to be due to
        a namespace scoping issue, which using globals seemed to fix; see pyo mailing
        list, 7 April 2012. This draws heavily on Olivier Belanger's solution.
        """
        def __init__(self, file, sec=0, sampletype=0):
            self.running = False
            if file:
                inputter = Input(chnl=0, mul=1)
                self.recorder = Record(inputter,
                                       file,
                                       chnls=2,
                                       fileformat=0,
                                       sampletype=sampletype,
                                       buffering=4)
                self.clean = Clean_objects(sec, self.recorder)

        def run(self, file, sec, sampletype):
            self.__init__(file, sec, sampletype)
            self.running = True
            self.clean.start(
            )  # controls recording onset (now) and offset (later)
            threading.Timer(sec, self._stop).start()  # set running flag False

        def stop(self):
            self.recorder.stop()
            self._stop()

        def _stop(self):
            self.running = False

    def __init__(self, name='mic', file='', saveDir='', sampletype=0):
        """
        :Parameters:
            name :
                Stem for the output file, also used in logging.
            file :
                optional file name to use; default = 'name-onsetTimeEpoch.wav'
            saveDir :
                Directory to use for output .wav files.
                If a saveDir is given, it will return 'saveDir/file'. 
                If no saveDir, then return abspath(file)
            sampletype : bit depth
                pyo recording option: 0=16 bits int, 1=24 bits int; 2=32 bits int
        """
        self.name = name
        self.saveDir = saveDir
        if file:
            self.wavOutFilename = file
        else:
            self.wavOutFilename = os.path.join(self.saveDir, name + '.wav')
        if not self.saveDir:
            self.wavOutFilename = os.path.abspath(self.wavOutFilename)
        else:
            if not os.path.isdir(self.saveDir):
                os.makedirs(self.saveDir, 0770)

        self.onset = None  # becomes onset time, used in filename
        self.savedFile = False  # becomes saved file name
        self.status = NOT_STARTED  # for Builder component

        # pyo server good to go?
        if not serverCreated():
            raise AttributeError('pyo server not created')
        if not serverBooted():
            raise AttributeError('pyo server not booted')

        self.loggingId = self.__class__.__name__
        if self.name:
            self.loggingId += ' ' + self.name

        # the recorder object needs to persist, or else get bus errors:
        self.recorder = self._Recorder(None)
        self.sampletype = sampletype  # pass through .run()

    def __del__(self):
        pass

    def stop(self):
        """Interrupt a recording that is in progress; close & keep the file.
        
        Ends the recording before the duration that was initially specified. The
        same file name is retained, with the same onset time but a shorter duration.
        
        The same recording cannot be resumed after a stop (it is not a pause),
        but you can start a new one.
        """
        if not self.recorder.running:
            logging.exp('%s: Stop requested, but no record() in progress' %
                        self.loggingId)
            return
        self.duration = core.getTime() - self.onset  # new shorter duration
        self.recorder.stop()
        logging.data('%s: Record stopped early, new duration %.3fs' %
                     (self.loggingId, self.duration))

    def reset(self):
        """Restores to fresh state, ready to record again"""
        logging.exp('%s: resetting at %.3f' % (self.loggingId, core.getTime()))
        self.__del__()
        self.__init__(name=self.name, saveDir=self.saveDir)

    def record(self, sec, file='', block=True):
        """Capture sound input for duration <sec>, save to a file.
        
        Return the path/name to the new file. Uses onset time (epoch) as
        a meaningful identifier for filename and log.
        """
        while self.recorder.running:
            pass
        self.duration = float(sec)
        self.onset = core.getTime(
        )  # note: report onset time in log, and use in filename
        logging.data('%s: Record: onset %.3f, capture %.3fs' %
                     (self.loggingId, self.onset, self.duration))
        if not file:
            onsettime = '-%.3f' % self.onset
            self.savedFile = onsettime.join(
                os.path.splitext(self.wavOutFilename))
        else:
            self.savedFile = os.path.abspath(file).strip('.wav') + '.wav'

        t0 = core.getTime()
        self.recorder.run(self.savedFile, self.duration, self.sampletype)
        self.rate = pyoServer.getSamplingRate()

        if block:
            core.wait(self.duration -
                      .0008)  # .0008 fudge factor for better reporting
            # actual timing is done by Clean_objects
            logging.exp('%s: Record: stop. %.3f, capture %.3fs (est)' %
                        (self.loggingId, core.getTime(), core.getTime() - t0))
        else:
            logging.exp('%s: Record: return immediately, no blocking' %
                        (self.loggingId))

        return self.savedFile

    def playback(self):
        """Plays the saved .wav file, as just recorded or resampled
        """
        if not self.savedFile or not os.path.isfile(self.savedFile):
            msg = '%s: Playback requested but no saved file' % self.loggingId
            logging.error(msg)
            raise ValueError(msg)

        # prepare a player for this file:
        t0 = core.getTime()
        self.sfplayer = SfPlayer(self.savedFile, speed=1, loop=False)
        self.sfplayer2 = self.sfplayer.mix(
            2)  # mix(2) -> 2 outputs -> 2 speakers
        self.sfplayer2.out()
        logging.exp('%s: Playback: prep %.3fs' %
                    (self.loggingId, core.getTime() - t0))

        # play the file; sfplayer was created during record:
        t0 = core.getTime()
        self.sfplayer.play()
        core.wait(self.duration)  # set during record()
        t1 = core.getTime()

        logging.exp('%s: Playback: play %.3fs (est) %s' %
                    (self.loggingId, t1 - t0, self.savedFile))

    def resample(self, newRate=16000, keep=True):
        """Re-sample the saved file to a new rate, return the full path.
        
        Can take several visual frames to resample a 2s recording.
        
        The default values for resample() are for google-speech, keeping the
        original (presumably recorded at 48kHz) to archive.
        A warning is generated if the new rate not an integer factor / multiple of the old rate.
        
        To control anti-aliasing, use pyo.downsamp() or upsamp() directly.
        """
        if not self.savedFile or not os.path.isfile(self.savedFile):
            msg = '%s: Re-sample requested but no saved file' % self.loggingId
            logging.error(msg)
            raise ValueError(msg)
        if newRate <= 0 or type(newRate) != int:
            msg = '%s: Re-sample bad new rate = %s' % (self.loggingId,
                                                       repr(newRate))
            logging.error(msg)
            raise ValueError(msg)

        # set-up:
        if self.rate >= newRate:
            ratio = float(self.rate) / newRate
            info = '-ds%i' % ratio
        else:
            ratio = float(newRate) / self.rate
            info = '-us%i' % ratio
        if ratio != int(ratio):
            logging.warn('%s: old rate is not an integer factor of new rate' %
                         self.loggingId)
        ratio = int(ratio)
        newFile = info.join(os.path.splitext(self.savedFile))

        # use pyo's downsamp or upsamp based on relative rates:
        if not ratio:
            logging.warn('%s: Re-sample by %sx is undefined, skipping' %
                         (self.loggingId, str(ratio)))
        elif self.rate >= newRate:
            t0 = time.time()
            downsamp(self.savedFile, newFile,
                     ratio)  # default 128-sample anti-aliasing
            logging.exp(
                '%s: Down-sampled %sx in %.3fs to %s' %
                (self.loggingId, str(ratio), time.time() - t0, newFile))
        else:
            t0 = time.time()
            upsamp(self.savedFile, newFile,
                   ratio)  # default 128-sample anti-aliasing
            logging.exp(
                '%s: Up-sampled %sx in %.3fs to %s' %
                (self.loggingId, str(ratio), time.time() - t0, newFile))

        # clean-up:
        if not keep:
            os.unlink(self.savedFile)
            self.savedFile = newFile
            self.rate = newRate

        return os.path.abspath(newFile)
Example #5
0
class AudioCapture(object):
    """Capture a sound sample from the default sound input, and save to a file.
        
        Untested whether you can have two recordings going on simultaneously.
        
        **Example**::
        
            from psychopy import microphone
            
            microphone.switchOn(sampleRate=16000) # do once when starting, can take 2-3s
            
            mic = microphone.AudioCapture()  # prepare to record; only one can be active
            mic.record(1)  # record for 1.000 seconds, save to mic.savedFile
            mic.playback()
            mic.resample(48000, keep=False) # creates a new file, discards original
            mic.resample(24000, keep=False) # only int ratios are supported
            # mic.savedFile is now the name of the re-re-sampled file
            
            microphone.switchOff() # do once, at exit 
        
        Also see Builder Demo "voiceCapture".
            
        :Author: Jeremy R. Gray, March 2012
    """
    
    class _Recorder(object):
        """Class for internal object to make an audio recording using pyo.
        
        Never needed by end-users; only used internally in __init__:
            self.recorder = _Recorder(None) # instantiate, global
        Then in record(), do:
            self.recorder.run(file, sec)
        This sets recording parameters, starts recording.
        This class never handles blocking; SimpleAudioCapture has to do that.
        
        Motivation: Doing pyo Record from within a function worked most of the time,
        but failed catastrophically ~1% of time with a bus error. Seemed to be due to
        a namespace scoping issue, which using globals seemed to fix; see pyo mailing
        list, 7 April 2012. This draws heavily on Olivier Belanger's solution.
        """
        def __init__(self, file, sec=0, sampletype=0):
            self.running = False
            if file:
                inputter = Input(chnl=0, mul=1)
                recorder = Record(inputter,
                               file,
                               chnls=2,
                               fileformat=0,
                               sampletype=sampletype,
                               buffering=4)
                self.clean = Clean_objects(sec, recorder)
        def run(self, file, sec, sampletype):
            self.__init__(file, sec, sampletype)
            self.running = True
            self.clean.start() # controls recording onset (now) and offset (later)
            threading.Timer(sec, self.stop).start() # set running flag False
        def stop(self):
            self.running = False
            
    def __init__(self, name='mic', file='', saveDir='', sampletype=0):
        """
        :Parameters:
            name :
                Stem for the output file, also used in logging.
            file :
                optional file name to use; default = 'name-onsetTimeEpoch.wav'
            saveDir :
                Directory to use for output .wav files.
                If a saveDir is given, it will return 'saveDir/file'. 
                If no saveDir, then return abspath(file)
            sampletype : bit depth
                pyo recording option: 0=16 bits int, 1=24 bits int; 2=32 bits int
        """
        self.name = name
        self.saveDir = saveDir
        if file:
            self.wavOutFilename = file
        else:
            self.wavOutFilename = os.path.join(self.saveDir, name + '.wav')
        if not self.saveDir:
            self.wavOutFilename = os.path.abspath(self.wavOutFilename)
        else:
            if not os.path.isdir(self.saveDir):
                os.makedirs(self.saveDir, 0770)

        self.onset = None # becomes onset time, used in filename
        self.savedFile = False # becomes saved file name
        self.status = NOT_STARTED # for Builder component
        
        # pyo server good to go? 
        if not serverCreated():
            raise AttributeError('pyo server not created')
        if not serverBooted():
            raise AttributeError('pyo server not booted')
        
        self.loggingId = self.__class__.__name__
        if self.name:
            self.loggingId += ' ' + self.name
        
        # the recorder object needs to persist, or else get bus errors:
        self.recorder = self._Recorder(None)
        self.sampletype = sampletype # pass through .run()

    def __del__(self):
        pass
    def reset(self):
        """Restores to fresh state, ready to record again"""
        logging.exp('%s: resetting at %.3f' % (self.loggingId, core.getTime()))
        self.__del__()
        self.__init__(name=self.name, saveDir=self.saveDir)
    def record(self, sec, file='', block=True):
        """Capture sound input for duration <sec>, save to a file.
        
        Return the path/name to the new file. Uses onset time (epoch) as
        a meaningful identifier for filename and log.
        """
        while self.recorder.running:
            pass
        self.duration = float(sec)
        self.onset = core.getTime() # note: report onset time in log, and use in filename
        logging.data('%s: Record: onset %.3f, capture %.3fs' %
                     (self.loggingId, self.onset, self.duration) )
        if not file:
            onsettime = '-%.3f' % self.onset
            self.savedFile = onsettime.join(os.path.splitext(self.wavOutFilename))
        else:
            self.savedFile = os.path.abspath(file).strip('.wav') + '.wav'
        
        t0 = core.getTime()
        self.recorder.run(self.savedFile, self.duration, self.sampletype)
        self.rate = pyoServer.getSamplingRate()
        
        if block:
            core.wait(self.duration - .0008) # .0008 fudge factor for better reporting
                # actual timing is done by Clean_object in _theGlobalRecordingThread()
            logging.exp('%s: Record: stop. %.3f, capture %.3fs (est)' %
                     (self.loggingId, core.getTime(), core.getTime() - t0) )
        else:
            logging.exp('%s: Record: return immediately, no blocking' %
                     (self.loggingId) )

        return self.savedFile
    
    def playback(self):
        """Plays the saved .wav file, as just recorded or resampled
        """
        if not self.savedFile or not os.path.isfile(self.savedFile):
            msg = '%s: Playback requested but no saved file' % self.loggingId
            logging.error(msg)
            raise ValueError(msg)
    
        # prepare a player for this file:
        t0 = core.getTime()
        self.sfplayer = SfPlayer(self.savedFile, speed=1, loop=False)
        self.sfplayer2 = self.sfplayer.mix(2) # mix(2) -> 2 outputs -> 2 speakers
        self.sfplayer2.out()
        logging.exp('%s: Playback: prep %.3fs' % (self.loggingId, core.getTime()-t0))

        # play the file; sfplayer was created during record:
        t0 = core.getTime()
        self.sfplayer.play()
        core.wait(self.duration) # set during record()
        t1 = core.getTime()

        logging.exp('%s: Playback: play %.3fs (est) %s' % (self.loggingId, t1-t0, self.savedFile))
    
    def resample(self, newRate=16000, keep=True):
        """Re-sample the saved file to a new rate, return the full path.
        
        Can take several visual frames to resample a 2s recording.
        
        The default values for resample() are for google-speech, keeping the
        original (presumably recorded at 48kHz) to archive.
        A warning is generated if the new rate not an integer factor / multiple of the old rate.
        
        To control anti-aliasing, use pyo.downsamp() or upsamp() directly.
        """
        if not self.savedFile or not os.path.isfile(self.savedFile):
            msg = '%s: Re-sample requested but no saved file' % self.loggingId
            logging.error(msg)
            raise ValueError(msg)
        if newRate <= 0 or type(newRate) != int:
            msg = '%s: Re-sample bad new rate = %s' % (self.loggingId, repr(newRate))
            logging.error(msg)
            raise ValueError(msg)
        
        # set-up:
        if self.rate >= newRate:
            ratio = float(self.rate) / newRate
            info = '-ds%i' % ratio
        else:
            ratio = float(newRate) / self.rate
            info = '-us%i' % ratio
        if ratio != int(ratio):
            logging.warn('%s: old rate is not an integer factor of new rate'% self.loggingId)
        ratio = int(ratio)
        newFile = info.join(os.path.splitext(self.savedFile))
        
        # use pyo's downsamp or upsamp based on relative rates:
        if not ratio:
            logging.warn('%s: Re-sample by %sx is undefined, skipping' % (self.loggingId, str(ratio)))
        elif self.rate >= newRate:
            t0 = time.time()
            downsamp(self.savedFile, newFile, ratio) # default 128-sample anti-aliasing
            logging.exp('%s: Down-sampled %sx in %.3fs to %s' % (self.loggingId, str(ratio), time.time()-t0, newFile))
        else:
            t0 = time.time()
            upsamp(self.savedFile, newFile, ratio) # default 128-sample anti-aliasing
            logging.exp('%s: Up-sampled %sx in %.3fs to %s' % (self.loggingId, str(ratio), time.time()-t0, newFile))    
            
        # clean-up:
        if not keep:
            os.unlink(self.savedFile)
            self.savedFile = newFile
            self.rate = newRate
        
        return os.path.abspath(newFile)
Example #6
0
 def reloadAudioFile(self):
     self.sfplayer = SfPlayer(self.outputPath, loop=False, mul=1).out()