浏览代码

[Feature] #234 player modularity : converting mp3 to wav using param

Done : 
-> Travis back using python requirements instead of setup.py
-> convert_to_wav param
ThiBuff 8 年之前
父节点
当前提交
6be81ca1b7

+ 1 - 2
.travis.yml

@@ -21,8 +21,7 @@ before_install:
 - sudo python get-pip.py
 - sudo python get-pip.py
 
 
 install:
 install:
-  - sudo python setup.py install
-  # - pip install -r install/files/python_requirements.txt
+  - pip install -r install/files/python_requirements.txt
   - pip install python2-pythondialog || pip install pythondialog
   - pip install python2-pythondialog || pip install pythondialog
   - pip install coveralls
   - pip install coveralls
 
 

+ 1 - 0
install/files/deb-packages_requirements.txt

@@ -20,3 +20,4 @@ build-essential
 sox
 sox
 libatlas3-base
 libatlas3-base
 mplayer
 mplayer
+ffmpeg

+ 41 - 0
kalliope/PlayerModule.py

@@ -0,0 +1,41 @@
+# coding: utf8
+import logging
+import os
+import subprocess
+
+from kalliope.core.Utils.FileManager import FileManager
+
+logging.basicConfig()
+logger = logging.getLogger("kalliope")
+
+
+class PlayerModule(object):
+    """
+    Mother class of Players. 
+    Ability to convert mp3 to wave format.
+    """
+
+    def __init__(self, **kwargs):
+
+        # set parameter from what we receive from the settings
+        self.convert = kwargs.get('convert_to_wav', True)
+
+    @staticmethod
+    def convert_mp3_to_wav(file_path_mp3):
+        """ 
+        PyAudio, AlsaPlayer, sounddevices  do not support mp3 files 
+        MP3 files must be converted to a wave in order to be played
+        This function assumes ffmpeg is available on the system
+        :param file_path_mp3: the file path to convert from mp3 to wav
+        """
+        logger.debug("Converting mp3 file to wav file: %s" % file_path_mp3)
+        fnull = open(os.devnull, 'w')
+        # temp file
+        tmp_file_wav = file_path_mp3 + ".wav"
+        # Convert mp3 to wave
+        subprocess.call(['ffmpeg', '-y', '-i', file_path_mp3, tmp_file_wav],
+                        stdout=fnull, stderr=fnull)
+        # remove the original file
+        FileManager.remove_file(file_path_mp3)
+        # rename the temp file with the same name as the original file
+        os.rename(tmp_file_wav, file_path_mp3)

+ 1 - 21
kalliope/core/TTS/TTSModule.py

@@ -157,24 +157,4 @@ class TTSModule(object):
             logger.debug("TTSModule, File already in cache: %s" % file_path)
             logger.debug("TTSModule, File already in cache: %s" % file_path)
         else:
         else:
             logger.debug("TTSModule, File not yet in cache: %s" % file_path)
             logger.debug("TTSModule, File not yet in cache: %s" % file_path)
-        return exist_in_cache
-
-    @staticmethod
-    def convert_mp3_to_wav(file_path_mp3):
-        """ 
-        PyAudio does not support mp3 files 
-        MP3 files must be converted to a wave in order to be played
-        This function assumes ffmpeg is available on the system
-        :param file_path_mp3: the file path to convert from mp3 to wav
-        """
-        logger.debug("Converting mp3 file to wav file: %s" % file_path_mp3)
-        fnull = open(os.devnull, 'w')
-        # temp file
-        tmp_file_wav = file_path_mp3 + ".wav"
-        # Convert mp3 to wave
-        subprocess.call(['ffmpeg', '-y', '-i', file_path_mp3, tmp_file_wav],
-                           stdout=fnull, stderr=fnull)
-        # remove the original file
-        FileManager.remove_file(file_path_mp3)
-        # rename the temp file with the same name as the original file
-        os.rename(tmp_file_wav, file_path_mp3)
+        return exist_in_cache

+ 9 - 6
kalliope/players/pyalsaaudio/pyalsaaudio.py

@@ -3,6 +3,8 @@ import alsaaudio
 import logging
 import logging
 import wave
 import wave
 
 
+from kalliope.PlayerModule import PlayerModule
+
 logging.basicConfig()
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 logger = logging.getLogger("kalliope")
 
 
@@ -30,12 +32,13 @@ def bits_to_samplefmt(bits):
         raise ValueError('Unsupported format')
         raise ValueError('Unsupported format')
 
 
 
 
-class Pyalsaaudio(object):
+class Pyalsaaudio(PlayerModule):
     """
     """
     This Class is representing the Player Object used to play the all sound of the system.
     This Class is representing the Player Object used to play the all sound of the system.
     """
     """
 
 
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
+        super(Pyalsaaudio, self).__init__(**kwargs)
         # List devices
         # List devices
         logger.debug("[pyalsaaudio.__init__] instance")
         logger.debug("[pyalsaaudio.__init__] instance")
         logger.debug("[pyalsaaudio.__init__] devices : %s " % (str(self.get_devices(DEVICE_TYPE_OUTPUT))))
         logger.debug("[pyalsaaudio.__init__] devices : %s " % (str(self.get_devices(DEVICE_TYPE_OUTPUT))))
@@ -58,14 +61,13 @@ class Pyalsaaudio(object):
 
 
     def play(self, file_path):
     def play(self, file_path):
 
 
+        if self.convert:
+            self.convert_mp3_to_wav(file_path_mp3=file_path)
         f = wave.open(file_path, 'rb')
         f = wave.open(file_path, 'rb')
         pcm_type = alsaaudio.PCM_PLAYBACK
         pcm_type = alsaaudio.PCM_PLAYBACK
         stream = alsaaudio.PCM(type=pcm_type,
         stream = alsaaudio.PCM(type=pcm_type,
                                mode=alsaaudio.PCM_NORMAL,
                                mode=alsaaudio.PCM_NORMAL,
-                               device=self.device)  # this is just for testing
-                               # device='sysdefault:CARD=ALSA')  # this is just for testing
-                                # on RPI3 this is fine; pulse (usually also default) is not working
-                                # device should be configurable; default schould be "default"
+                               device=self.device)
         # Set attributes
         # Set attributes
         stream.setchannels(f.getnchannels())
         stream.setchannels(f.getnchannels())
         stream.setrate(f.getframerate())
         stream.setrate(f.getframerate())
@@ -74,7 +76,8 @@ class Pyalsaaudio(object):
         stream.setperiodsize(CHUNK)
         stream.setperiodsize(CHUNK)
         
         
         logger.debug("[PyAlsaAudioPlayer] %d channels, %d sampling rate, %d bit" % (f.getnchannels(),
         logger.debug("[PyAlsaAudioPlayer] %d channels, %d sampling rate, %d bit" % (f.getnchannels(),
-                                                                            f.getframerate(),  bits))
+                                                                                    f.getframerate(),
+                                                                                    bits))
         
         
         data = f.readframes(CHUNK)
         data = f.readframes(CHUNK)
         while data:
         while data:

+ 7 - 3
kalliope/players/pyaudioplayer/pyaudioplayer.py

@@ -4,28 +4,32 @@ import wave
 
 
 import pyaudio
 import pyaudio
 
 
+from kalliope.PlayerModule import PlayerModule
+
 logging.basicConfig()
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 logger = logging.getLogger("kalliope")
 
 
 CHUNK = 1024
 CHUNK = 1024
 
 
 
 
-class Pyaudioplayer(object):
+class Pyaudioplayer(PlayerModule):
     """
     """
     This Class is representing the Player Object used to play the all sound of the system.
     This Class is representing the Player Object used to play the all sound of the system.
     """
     """
 
 
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
+        super(Pyaudioplayer, self).__init__(**kwargs)
         logger.debug("[Pyaudioplayer.__init__] instance")
         logger.debug("[Pyaudioplayer.__init__] instance")
         logger.debug("[Pyaudioplayer.__init__] args : %s " % str(kwargs))
         logger.debug("[Pyaudioplayer.__init__] args : %s " % str(kwargs))
 
 
-    @classmethod
-    def play(cls, file_path):
+    def play(self, file_path):
         """
         """
         Play the sound located in the provided file_path
         Play the sound located in the provided file_path
         :param file_path: The file path of the sound to play. Must be wav format
         :param file_path: The file path of the sound to play. Must be wav format
         :type file_path: str              
         :type file_path: str              
         """
         """
+        if self.convert:
+            self.convert_mp3_to_wav(file_path_mp3=file_path)
         # open the wave file
         # open the wave file
         wf = wave.open(file_path, 'rb')
         wf = wave.open(file_path, 'rb')
 
 

+ 7 - 3
kalliope/players/sounddeviceplayer/sounddeviceplayer.py

@@ -3,24 +3,28 @@ import sounddevice as sd
 import soundfile as sf
 import soundfile as sf
 import logging
 import logging
 
 
+from kalliope.PlayerModule import PlayerModule
+
 logging.basicConfig()
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 logger = logging.getLogger("kalliope")
 
 
 FS = 48000
 FS = 48000
 
 
 
 
-class Sounddeviceplayer(object):
+class Sounddeviceplayer(PlayerModule):
     """
     """
     This Class is representing the Player Object used to play the all sound of the system.
     This Class is representing the Player Object used to play the all sound of the system.
     """
     """
 
 
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
+        super(Sounddeviceplayer, self).__init__(**kwargs)
         logger.debug("[Sounddeviceplayer.__init__] instance")
         logger.debug("[Sounddeviceplayer.__init__] instance")
         logger.debug("[Sounddeviceplayer.__init__] args : %s " % str(kwargs))
         logger.debug("[Sounddeviceplayer.__init__] args : %s " % str(kwargs))
 
 
-    @classmethod
-    def play(cls, file_path):
+    def play(self, file_path):
 
 
+        if self.convert:
+            self.convert_mp3_to_wav(file_path_mp3=file_path)
         data, fs = sf.read(file_path)
         data, fs = sf.read(file_path)
         sd.play(data, fs)
         sd.play(data, fs)
         sd.wait()
         sd.wait()

+ 16 - 14
kalliope/settings.yml

@@ -37,15 +37,15 @@ speech_to_text:
   - google:
   - google:
       language: "fr-FR"
       language: "fr-FR"
   - wit:
   - wit:
-      key: "B5JI3YUSLYOYWNIDBINBVM34XUODME2K"
+      key: "fakekey" # example : "B5JI3YUSLYOYWNIDBINBVM34XUODME2K"
   - bing: # API not working : credential fails ..
   - bing: # API not working : credential fails ..
-      key: "9e48ddaf75904838bedc11aea6b36fb0"
+      key: "fakekey" # example : "9e48ddaf75904838bedc11aea6b36fb0"
   - apiai:
   - apiai:
-      key: "e0cbff154af44944a6b9f82c0668b527"
+      key: "fakekey" # example : "e0cbff154af44944a6b9f82c0668b527"
       language: "fr"
       language: "fr"
   - houndify:
   - houndify:
-      key: "7zj90T7qAV74OYXk4X4vI2Xhk7wPsJu4aEZ0G5Ll-BMmV1JGtFpCxtSH9SmTY4G3bpEJ7a5y_GTQid-CAKI6vw=="
-      client_id: "lN4JXeaSticbSo9-llczbA=="
+      key: "fakekey" # example : "7zj90T7qAV74OYXk4X4vI2Xhk7wPsJu4aEZ0G5Ll-BMmV1JGtFpCxtSH9SmTY4G3bpEJ7a5y_GTQid-CAKI6vw=="
+      client_id: "fakeclientid" # example : "lN4JXeaSticbSo9-llczbA=="
   #- cmusphinx
   #- cmusphinx
 
 
 
 
@@ -60,10 +60,9 @@ cache_path: "/tmp/kalliope_tts_cache"
 # Text to Speech engines configuration
 # Text to Speech engines configuration
 # Available engine are:
 # Available engine are:
 # - pico2wave
 # - pico2wave
-# - acapela
-# - pico2wave
-# - googletts
-# - voicerss
+# - acapela     # MP3
+# - googletts   # MP3
+# - voicerss    # MP3
 text_to_speech:
 text_to_speech:
   - pico2wave:
   - pico2wave:
       language: "fr-FR"
       language: "fr-FR"
@@ -88,15 +87,18 @@ default_player: "mplayer"
 # players configuration
 # players configuration
 # Available engine are:
 # Available engine are:
 # - mplayer
 # - mplayer
-# - pyalsaaudio
-# - pyaudioplayer
-# - sounddeviceplayer
+# - pyalsaaudio       # no mp3
+# - pyaudioplayer     # no mp3
+# - sounddeviceplayer # no mp3
 players:
 players:
   - mplayer: {}
   - mplayer: {}
   - pyalsaaudio:
   - pyalsaaudio:
      device: "default"
      device: "default"
-  - pyaudioplayer: {}
-  - sounddeviceplayer: {}
+     convert_to_wav: True
+  - pyaudioplayer:
+     convert_to_wav: True
+  - sounddeviceplayer:
+     convert_to_wav: True
 
 
 # ---------------------------
 # ---------------------------
 # Wake up answers
 # Wake up answers

+ 0 - 3
kalliope/tts/acapela/acapela.py

@@ -77,9 +77,6 @@ class Acapela(TTSModule):
         # OK we get the audio we can write the sound file
         # OK we get the audio we can write the sound file
         FileManager.write_in_file(self.file_path, r.content)
         FileManager.write_in_file(self.file_path, r.content)
 
 
-        # the received file use MP3 format. It must be converted to wav in order to be played by pyaudio
-        self.convert_mp3_to_wav(self.file_path)
-
     def get_payload(self):
     def get_payload(self):
         """
         """
         Generic method used load the payload used to access the remote api
         Generic method used load the payload used to access the remote api

+ 1 - 1
setup.py

@@ -67,7 +67,7 @@ setup(
         'markupsafe>=1.0',
         'markupsafe>=1.0',
         'pyaudio>=0.2.10',
         'pyaudio>=0.2.10',
         'pyasn1>=0.2.3',
         'pyasn1>=0.2.3',
-        'ansible>=2.2',
+        'ansible>=2.3',
         py2_prefix + 'pythondialog>=3.4.0',
         py2_prefix + 'pythondialog>=3.4.0',
         'jinja2>=2.8,<=2.9.6',
         'jinja2>=2.8,<=2.9.6',
         'cffi>=1.9.1',
         'cffi>=1.9.1',