Skip to content

Instantly share code, notes, and snippets.

@ArthurBeaulieu
Last active April 2, 2020 17:12
Show Gist options
  • Save ArthurBeaulieu/578dbb4a54bc2d0a8c4dd05d38dda5a2 to your computer and use it in GitHub Desktop.
Save ArthurBeaulieu/578dbb4a54bc2d0a8c4dd05d38dda5a2 to your computer and use it in GitHub Desktop.
Reads a binary .mood file to get RGB values and some basic stats about track and samples
#!/usr/bin/env python3
import argparse
import statistics
global moodbarSamples
moodbarSamples = 1000 # This value is according to Moodbar definition (https://en.wikipedia.org/wiki/Moodbar)
## -------------------- Script execution context -------------------- ##
# Script main function
# Usage : `$ python ./PyMood.py -v /path/to/file.mood`
# Verbose argument is optional, file path isn't (obviously)
# Will build a MoodbarFile object (see class definition) from a .mood binary file
# The main purpose of this is to extract Bass, medium and high interresting values
# Futur features in ManaZeak project will analyse those data (see https://github.com/ManaZeak/ManaZeak)
# DM me (https://github.com/ArthurBeaulieu) if you have any questions about moodbar
def main():
# Init argparse arguments
ap = argparse.ArgumentParser()
ap.add_argument('filePath', help='The .mood file path on system') # Mandatory .mood file path
ap.add_argument('-v', '--verbose', help='Log detailled progress when running', action='store_true') # Optional verbose option
args = vars(ap.parse_args()) # Parse sent arguments according to definition
# Open .mood given as an argument file
with open(args['filePath'], 'rb') as file:
intArray = convertStreamToArray(file) # Bytes to int
samples = convertArrayToSamples(intArray) # Int array to MoodbarSample array
output = MoodbarFile(samples) # MoodbarSample array to MoodbarFile object
if args['verbose'] == True: # Display MoodbarFile if verbose arg
output.display()
file.close() # Safely close .mood file
# Take a file an iterate through its bytes to converts byte value into int array
def convertStreamToArray(file):
output = [] # Output int array
byte = file.read(1) # Get first byte of given file
while byte: # While bytes are found
byte = file.read(1) # Get next byte
output.append(int.from_bytes(byte, byteorder='big')) # Append its int value in output
return output # Send back int array
# Convert 3k length int array into 1k length MoodbarSample array
def convertArrayToSamples(array):
output = [] # Output MoodbarSample array
for i in range(0, moodbarSamples, 3): # Iterate 3 by 3 over int array (RGB values)
# Append MoodbarSample with given R, G and B values
output.append(MoodbarSample(array[i], array[i + 1], array[i + 2]))
return output # Send back MoodbarSample array
## -------------------- Util classes -------------------- ##
# The script output object
class MoodbarFile:
def __init__(self, samples):
self.samples = samples # Store MoodbarSamples in MoodbarFile
# Basic file stats
self.mean = 0
self.median = 0
self.mode = 0
# Bass frequencies stats
self.rMean = 0
self.rMedian = 0
self.rMode = 0
# Middle frequencies stats
self.gMean = 0
self.gMedian = 0
self.gMode = 0
# High frequencies stats
self.bMean = 0
self.bMedian = 0
self.bMode = 0
# Compute all class attributes
self._computeInternals()
# The internal method will fill all MoodbarFile internals according to given samples
def _computeInternals(self):
rs, gs, bs, means, medians, modes = [], [], [], [], [], [] # Init working arrays
for sample in self.samples:
rs.append(sample.r)
gs.append(sample.g)
bs.append(sample.b)
means.append(sample.mean)
medians.append(sample.median)
modes.append(sample.mode)
# Basic stats
self.mean = statistics.mean(means)
self.median = statistics.median(medians)
self.mode = statistics.mode(modes)
# Bass frequencies stats
self.rMean = statistics.mean(rs)
self.rMedian = statistics.median(rs)
self.rMode = statistics.mode(rs)
# Middle frequencies stats
self.gMean = statistics.mean(gs)
self.gMedian = statistics.median(gs)
self.gMode = statistics.mode(gs)
# High frequencies stats
self.bMean = statistics.mean(bs)
self.bMedian = statistics.median(bs)
self.bMode = statistics.mode(bs)
# Log MoodbarFile
def display(self):
print(' Track global colors')
print('> Track mean color is rgb({}, {}, {})'.format(self.mean, self.mean, self.mean))
print('> Track median color is rgb({}, {}, {})'.format(self.mean, self.mean, self.mean))
print('> Track mode color is rgb({}, {}, {})\n'.format(self.mean, self.mean, self.mean))
print(' Bass colors')
print('> Bass mean color is rgb({}, {}, {})'.format(self.rMean, self.rMean, self.rMean))
print('> Bass median color is rgb({}, {}, {})'.format(self.rMedian, self.rMedian, self.rMedian))
print('> Bass mode color is rgb({}, {}, {})\n'.format(self.rMode, self.rMode, self.rMode))
print(' Medium colors')
print('> Medium mean color is rgb({}, {}, {})'.format(self.gMean, self.gMean, self.gMean))
print('> Medium median color is rgb({}, {}, {})'.format(self.gMedian, self.gMedian, self.gMedian))
print('> Medium mode color is rgb({}, {}, {})\n'.format(self.gMode, self.gMode, self.gMode))
print(' High colors')
print('> High mean color is rgb({}, {}, {})'.format(self.bMean, self.bMean, self.bMean))
print('> High median color is rgb({}, {}, {})'.format(self.bMedian, self.bMedian, self.bMedian))
print('> High mode color is rgb({}, {}, {})'.format(self.bMode, self.bMode, self.bMode))
# One single mood sample (track length by 1k section)
class MoodbarSample:
def __init__(self, r, g, b):
self.r = r # Sample bass value
self.g = g # Sample medium value
self.b = b # Sample high value
# Basic sample stats
self.mean = statistics.mean([r, g, b]) # Sample mean color
self.median = statistics.median([r, g, b]) # Sample median color
self.mode = statistics.mode([r, g, b]) # Sample mode color
# Script start point
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment