Music Information Retrieval

Danzabilidad

Algoritmo de danzabilidad

La danzabilidad es una medida de que tan bailable puede ser una cancion respecto a una musica bailable de su genero o a cualquier otra musica bailable. Utilizamos el analisis de detrendimiento de fluctuacion con un metodo de regresion que utiliza un algoritmo de descenso de gradiente basado en una funcion de atencion. El proposito es analizar si una pieza sonora se desvia mucho de una tendencia tomada como bailable y en base al desvio se computa cuanto movimiento que se pueda correlacionar puede haber en la pieza analizada.

El algoritmo de danzabilidad que proponemos utiliza el metodo de descenso de gradiente con funciones de atencion para calcular la tendencia mientras que definimos como targets para calcular la tendencia tendencias presentes en canciones bailables. Esto mejora la estimacion en canciones donde la duracion del tono es mas larga, en lentos de piano, en canciones de reggaeton, en piezas de musica electronica, en piezas de rock.

Danzabilidad en APICultor

Calculando onsets

El primer paso antes de calcular la danzabilidad como tal es computar onsets percusivos en una cancion utilizando el contenido de alta frecuencia. De esta forma se espera que se utilizen partes donde hayan repiques de bateria o tonos similares para saber donde estan los onsets.


In [5]:
#importamos las funciones en el modulo de algoritmos MIR
from apicultor.utils.algorithms import * 

#importamos soundfile de las dependencias de apicultor
from soundfile import read 

tags_dir = 'bajo' 

#abrimos nuestro sonido de RedPanal
audio, fs = read('710.ogg') 

#lo convertimos a mono para poder analizar
audio = mono_stereo(audio) 

#llamamos a la clase MIR para obtener nuestros onsets
retrieve = MIR(audio, fs) 

#aplicamos un filtro para remover el ruido hasta 40 Hz
retrieve.signal = retrieve.IIR(retrieve.signal, 40, 'highpass')

#importamos nuestra funcion para obtener los onsets
from apicultor.sonification.Sonification import hfc_onsets

#calculamos el contenido de frecuencia alta en todos los frames
hfcs = []                          
for frame in retrieve.FrameGenerator():
    retrieve.window()    
    retrieve.Spectrum()    
    hfcs.append(retrieve.hfc())   
hfcs /= max(hfcs) 

#filtramos y seleccionamos los frames con optimo contenido
fir = firwin(11, 1.0 / 8, window = "hamming")                      
retrieve.filtered = np.convolve(hfcs, fir, mode="same")
retrieve.climb_hills()
retrieve.hfcs = np.array([i for i, x in enumerate(retrieve.Filtered) if x > 0]) * retrieve.N


---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-5-0c281773027a> in <module>()
      8 
      9 #abrimos nuestro sonido de RedPanal
---> 10 audio, fs = read('710.ogg')
     11 
     12 #lo convertimos a mono para poder analizar

/usr/local/lib/python3.6/dist-packages/PySoundFile-0.9.0.post1-py3.6.egg/soundfile.py in read(file, frames, start, stop, dtype, always_2d, fill_value, out, samplerate, channels, format, subtype, endian, closefd)
    373                    subtype, endian, format, closefd) as f:
    374         frames = f._prepare_read(start, stop, frames)
--> 375         data = f.read(frames, dtype, always_2d, fill_value, out)
    376     return data, f.samplerate
    377 

/usr/local/lib/python3.6/dist-packages/PySoundFile-0.9.0.post1-py3.6.egg/soundfile.py in read(self, frames, dtype, always_2d, fill_value, out)
    963             if frames < 0 or frames > len(out):
    964                 frames = len(out)
--> 965         frames = self._array_io('read', out, frames)
    966         if len(out) > frames:
    967             if fill_value is None:

/usr/local/lib/python3.6/dist-packages/PySoundFile-0.9.0.post1-py3.6.egg/soundfile.py in _array_io(self, action, array, frames)
   1408         assert array.dtype.itemsize == _ffi.sizeof(ctype)
   1409         cdata = _ffi.cast(ctype + '*', array.__array_interface__['data'][0])
-> 1410         return self._cdata_io(action, cdata, ctype, frames)
   1411 
   1412     def _cdata_io(self, action, data, ctype, frames):

/usr/local/lib/python3.6/dist-packages/PySoundFile-0.9.0.post1-py3.6.egg/soundfile.py in _cdata_io(self, action, data, ctype, frames)
   1417             curr = self.tell()
   1418         func = getattr(_snd, 'sf_' + action + 'f_' + ctype)
-> 1419         frames = func(self._file, data, frames)
   1420         _error_check(self._errorcode)
   1421         if self.seekable():

KeyboardInterrupt: 
Analisis de detrendimiento de flujo de nuestro primer onset

Nosotros partiremos de la base de que teniendo un onset percusivo de una cancion se puede saber la danzabilidad del resto de la musica ya que si una parte es muy bailable tiene que ser probable que el resto lo sea debido a los procesos a largo plazo. Es por esto que utilizaremos los ocho segundos siguientes de hallar el primer onset de la pieza musical analizada para calcular la danzabilidad de la cancion. En este ejemplo la cancion es una pista de bajo por lo cual no se hara ninguna asociacion con una media especifica de un genero sino que la media sera tomada aleatoriamente.


In [ ]:
#danceability
if tag_dir == 'electronic':                                
        meany = np.loadtxt('means/y1.txt') #one more time
if tag_dir == 'pop':                                
        meany = np.loadtxt('means/y2.txt') #you should be dancing
if tag_dir == 'rock':                                
        meany = np.loadtxt('means/y3.txt') #sultans of swing
if tag_dir == 'mapuche':                                
        meany = np.loadtxt('means/y4.txt') #mapuche dance
if tag_dir == 'reggaeton':                                
        meany = np.loadtxt('means/y5.txt') #despacito
if tag_dir != 'reggaeton' and tag_dir != 'electronic' and tag_dir != 'pop' and tag_dir != 'rock' and tag_dir != 'mapuche':                
        meany = random.choice(['y1.txt','y2.txt','y3.txt','y4.txt','y5.txt'])
        meany = np.loadtxt('means/'+meany)

print("DANZABILIDAD DE LA CANCION: ")        
print(danceability(retrieve.signal[retrieve.hfcs[0]:retrieve.hfcs[0]+(retrieve.fs*8)], meany, retrieve.fs))