Last active
April 2, 2020 17:12
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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