
Recherche avancée
Médias (91)
-
Spoon - Revenge !
15 septembre 2011, par
Mis à jour : Septembre 2011
Langue : English
Type : Audio
-
My Morning Jacket - One Big Holiday
15 septembre 2011, par
Mis à jour : Septembre 2011
Langue : English
Type : Audio
-
Zap Mama - Wadidyusay ?
15 septembre 2011, par
Mis à jour : Septembre 2011
Langue : English
Type : Audio
-
David Byrne - My Fair Lady
15 septembre 2011, par
Mis à jour : Septembre 2011
Langue : English
Type : Audio
-
Beastie Boys - Now Get Busy
15 septembre 2011, par
Mis à jour : Septembre 2011
Langue : English
Type : Audio
-
Granite de l’Aber Ildut
9 septembre 2011, par
Mis à jour : Septembre 2011
Langue : français
Type : Texte
Autres articles (3)
-
Submit bugs and patches
13 avril 2011Unfortunately a software is never perfect.
If you think you have found a bug, report it using our ticket system. Please to help us to fix it by providing the following information : the browser you are using, including the exact version as precise an explanation as possible of the problem if possible, the steps taken resulting in the problem a link to the site / page in question
If you think you have solved the bug, fill in a ticket and attach to it a corrective patch.
You may also (...) -
Le plugin : Gestion de la mutualisation
2 mars 2010, parLe plugin de Gestion de mutualisation permet de gérer les différents canaux de mediaspip depuis un site maître. Il a pour but de fournir une solution pure SPIP afin de remplacer cette ancienne solution.
Installation basique
On installe les fichiers de SPIP sur le serveur.
On ajoute ensuite le plugin "mutualisation" à la racine du site comme décrit ici.
On customise le fichier mes_options.php central comme on le souhaite. Voilà pour l’exemple celui de la plateforme mediaspip.net :
< ?php (...) -
MediaSPIP : Modification des droits de création d’objets et de publication définitive
11 novembre 2010, parPar défaut, MediaSPIP permet de créer 5 types d’objets.
Toujours par défaut les droits de création et de publication définitive de ces objets sont réservés aux administrateurs, mais ils sont bien entendu configurables par les webmestres.
Ces droits sont ainsi bloqués pour plusieurs raisons : parce que le fait d’autoriser à publier doit être la volonté du webmestre pas de l’ensemble de la plateforme et donc ne pas être un choix par défaut ; parce qu’avoir un compte peut servir à autre choses également, (...)
Sur d’autres sites (793)
-
Ffmpeg error with output file not specified on linux when executing from dockerized new process in .net-core
29 août 2017, par DutchGuySome time back i created a small program in full .net framework that could rip mp3 and then add meta-info and a thumbnail to it. For innovation sake i started porting the application to .net core. After that i thought it would be cool to run this in docker on Linux. It took some time but i got it working. Now i’m running into the issue where i can rip the mp3 from the video file but when i try to combine the meta-info, thumbnail and mp3 ffmpeg tells me "At least one output file must be specified"
I added some checks so the program can run on Linux and Windows (to switch paths etc)
What i tried :
- running same argument structure on windows (works)
- Manualy starting the encode on linux (works)
- Simplifying the names, paths and titles used in the command
- replacing extra spaces and special characters, only "-" and single spaces allowed
Below i have an example of the command that does work when i execute it manually but produces the earlier mentioned error when my program executes it. Also keep in mind that ripping the mp3 from the video does result in the desired outputfile.
ffmpeg -i "temp/vid-id.mp3" -i "temp/vid-id.jpg" -map 0:0 -map 1:0 -c copy -y -id3v2_version 3 -metadata title="title" -metadata artist="artist" -metadata album="album" "completed/file - name.mp3"
To start this from .net-core context i use the snippet below :
using (var ffmpegProcess = new Process())
{
ffmpegProcess.EnableRaisingEvents = false;
ffmpegProcess.StartInfo.CreateNoWindow = true;
ffmpegProcess.StartInfo.UseShellExecute = false;
ffmpegProcess.StartInfo.FileName = ffmpegPath(); (returns "/bin/bash")
string command = "ffmpeg " + arguments;
ffmpegProcess.StartInfo.Arguments = "-c \" " + command + " \"";
ffmpegProcess.Start();
ffmpegProcess.WaitForExit();
ffmpegProcess.Dispose();
}arguments :
String.Format("-i \"{0}\" -i \"{1}.jpg\" -map 0:0 -map 1:0 -c copy -y -id3v2_version 3 -metadata title=\"{2}\" -metadata artist=\"{3}\" -metadata album=\"{4}\" \"{5}\"",inputFile, albumArtFile, outputTitle, artist, outputAlbum, outputFile);
To rip the mp3 from the video file i use the same method mentioned above with the following arguments :
-i "temp/vid-id.mp4" -vn -ab 320k -y -threads 2 "temp/vid-id.mp3"
This works as expected.
Arguments :String.Format("-i \"{0}\" -vn -ab 320k -y -threads 2 \"{1}\"", inputFile, outputFile);
Vid-id does not contain spaces, below is and example of how it could look :
"abcd12ef-gh34i"
Since the initial ripping is working i cant figure out why Ffmpeg complains about the second operation. The only real difference between the two operations is that in the second one the output-file name can contain spaces. But even then, running the same command manually does work and produces the desired file in the completed dir.
Update
After more troubleshooting i have now narrowed the problem down to the usage of the metadata arguments. Still can’t figure out whyUpdate
The problem is with the spaces in the naming i have chosen. On linux the spaces are interpreted as end of file name. Any pointer how i can still use the naming with spaces ? -
How to set crontab in order to run multiple python and a shell scripts ?
5 janvier 2021, par Alexander MitsouI need to start three python3 scripts and a shell script using crontab. These scripts should run at the same time without any delay. Each script runs exactly for one minute. For instance I have scheduled crontab to run these scripts every 5 minutes.


My problem is that, if I attempt to run each script individually from terminal it executes with no further errors, but using crontab nothing happens.


DISCLAIMER : If I set up the Python3 scripts individually in crontab, they work fine !


Here's my crontab set up :


*/5 * * * * cd /home/user/Desktop/ && /usr/bin/python3 script1.py >> report1.log

*/5 * * * * cd /home/user/Desktop/ && /usr/bin/python3 script2.py >> report2.log

*/5 * * * * cd /home/user/Desktop/ && /usr/bin/python3 script3.py >> report3.log

*/5 * * * * cd /home/user/Desktop/ && /usr/bin/sh script4.sh >> report4.log 



In addition I need to mention that the shell script contains this command (FFMPEG) :


#!/bin/bash

parent_dir=`dirname \`pwd\`` 
folder_name="/Data/Webcam" 
new_path=$parent_dir$folder_name 


if [ -d "$new_path" ]; then
 echo "video_audio folder exists..."
else
 echo "Creating video_audio folder in the current directory..."
 mkdir -p -- "$new_path"
 sudo chmod 777 "$new_path"
 echo "Folder created"
 echo
fi

now=$(date +%F) 
now="$( echo -e "$now" | tr '-' '_' )"
sub_dir=$new_path'/'$now 

if [ -d "$sub_dir" ]; then
 echo "Date Sub-directory exists..."
 echo
else
 echo "Error: ${sub_dir} not found..."
 echo "Creating date sub-directory..."
 mkdir -p -- "$sub_dir"
 sudo chmod 777 "$sub_dir"
 echo "Date sub-directory created..."
 echo
fi

fname=$(date +%H_%M_%S)".avi"
video_dir=$sub_dir'/'$fname
ffmpeg -f pulse -ac 1 -i default -f v4l2 -i /dev/video0 -vcodec libx264 -t 00:01:00 $video_dir 



The log file of that script contain the following :


video_audio folder exists...
Date Sub-directory exists...

Package ffmpeg is already installed...
Package v4l-utils is already installed...

Package: ffmpeg
Status: install ok installed
Priority: optional
Section: video
Installed-Size: 2010
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Architecture: amd64
Multi-Arch: foreign
Version: 7:4.2.4-1ubuntu0.1
Replaces: libav-tools (<< 6:12~~), qt-faststart (<< 7:2.7.1-3~)
Depends: libavcodec58 (= 7:4.2.4-1ubuntu0.1), libavdevice58 (= 7:4.2.4-1ubuntu0.1), libavfilter7 (= 7:4.2.4-1ubuntu0.1), libavformat58 (= 7:4.2.4-1ubuntu0.1), libavresample4 (= 7:4.2.4-1ubuntu0.1), libavutil56 (= 7:4.2.4-1ubuntu0.1), libc6 (>= 2.29), libpostproc55 (= 7:4.2.4-1ubuntu0.1), libsdl2-2.0-0 (>= 2.0.10), libswresample3 (= 7:4.2.4-1ubuntu0.1), libswscale5 (= 7:4.2.4-1ubuntu0.1)
Suggests: ffmpeg-doc
Breaks: libav-tools (<< 6:12~~), qt-faststart (<< 7:2.7.1-3~), winff (<< 1.5.5-5~)
Description: Tools for transcoding, streaming and playing of multimedia files
 FFmpeg is the leading multimedia framework, able to decode, encode, transcode,
 mux, demux, stream, filter and play pretty much anything that humans and
 machines have created. It supports the most obscure ancient formats up to the
 cutting edge.
 .
 This package contains:
 * ffmpeg: a command line tool to convert multimedia files between formats
 * ffplay: a simple media player based on SDL and the FFmpeg libraries
 * ffprobe: a simple multimedia stream analyzer
 * qt-faststart: a utility to rearrange Quicktime files
Homepage: https://ffmpeg.org/
Original-Maintainer: Debian Multimedia Maintainers <debian-multimedia@lists.debian.org>
Package: v4l-utils
Status: install ok installed
Priority: optional
Section: utils
Installed-Size: 2104
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Architecture: amd64
Version: 1.18.0-2build1
Replaces: ivtv-utils (<< 1.4.1-2), media-ctl
Depends: libv4l-0 (= 1.18.0-2build1), libv4l2rds0 (= 1.18.0-2build1), libc6 (>= 2.17), libgcc-s1 (>= 3.0), libstdc++6 (>= 5.2), libudev1 (>= 183)
Breaks: ivtv-utils (<< 1.4.1-2), media-ctl
Description: Collection of command line video4linux utilities
 v4l-utils contains the following video4linux command line utilities:
 .
 decode_tm6000: decodes tm6000 proprietary format streams
 rds-ctl: tool to receive and decode Radio Data System (RDS) streams
 v4l2-compliance: tool to test v4l2 API compliance of drivers
 v4l2-ctl, cx18-ctl, ivtv-ctl: tools to control v4l2 controls from the cmdline
 v4l2-dbg: tool to directly get and set registers of v4l2 devices
 v4l2-sysfs-path: sysfs helper tool
Original-Maintainer: Gregor Jasny <gjasny@googlemail.com>
Homepage: https://linuxtv.org/downloads/v4l-utils/



Due to the reason that the python files are of the same structure I'm uploading a sample file here :


# -*- coding: utf-8 -*-
from threading import Timer
from pynput.mouse import Listener
import logging
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(
 os.path.realpath(__file__)), "../"))

from Functions import utils as ut

if __name__=='__main__':

 ut.initialize_dirs()
 rec_file = ''.join(('mouse_',ut.get_date(),'.txt'))
 raw_data = ut.get_name('Mouse')
 rec_file = os.path.join(raw_data,rec_file)
 logging.basicConfig(filename=rec_file,level=logging.DEBUG,format="%(asctime)s %(message)s")

 try:
 with Listener(on_move=ut.on_move, on_click=ut.on_click,on_scroll=ut.on_scroll) as listener:
 Timer(60, listener.stop).start()
 listener.join()
 except KeyboardInterrupt as err:
 print(err)
 sys.exit(0)

 print('Exiting logger...')




I'm also uploading the functions that I use :


# -*- coding: utf-8 -*-
from serial import Serial
from datetime import datetime, timedelta
import pandas as pd
import collections
import logging
import shutil
import serial
import time
import sys
import os

click_held = False
button = None


def on_move(x,y):
 """The callback to call when mouse move events occur

 Args:
 x (float): The new pointer position
 y (float): The new pointer poisition
 """
 if click_held:
 logging.info("MV {0:>8} {1:>8} {2:>8}:".format(x,y,str(None)))
 else:
 logging.info("MV {0:>8} {1:>8} {2:>8}:".format(x,y,str(None)))


def on_click(x,y,button,pressed):
 """The callback to call when a mouse button is clicked

 Args:
 x (float): Mouse coordinates on screen
 y (float): Mouse coordinates on screen
 button (str): one of the Button values
 pressed (bool): Pressed is whether the button was pressed
 """
 global click_held
 if pressed:
 click_held = True
 logging.info("CLK {0:>7} {1:>6} {2:>13}".format(x,y,button))
 else:
 click_held = False
 logging.info("RLS {0:>7} {1:>6} {2:>13}".format(x,y,button))


def on_scroll(x,y,dx,dy):
 """The callback to call when mouse scroll events occur

 Args:
 x (float): The new pointer position on screen
 y (float): The new pointer position on screen
 dx (int): The horizontal scroll. The units of scrolling is undefined
 dy (int): The vertical scroll. The units of scrolling is undefined
 """
 if dy == -1:
 logging.info("SCRD {0:>6} {1:>6} {2:>6}".format(x,y,str(None)))
 elif dy == 1:
 logging.info("SCRU {0:>6} {1:>6} {2:>6}".format(x,y,str(None)))
 else:
 pass


def on_press_keys(key):
 """The callback to call when a button is pressed.

 Args:
 key (str): A KeyCode,a Key or None if the key is unknown
 """
 subkeys = [
 'Key.alt','Key.alt_gr','Key.alt_r','Key.backspace',
 'Key.space','Key.ctrl','Key.ctrl_r','Key.down',
 'Key.up','Key.left','Key.right','Key.page_down',
 'Key.page_up','Key.enter','Key.shift','Key.shift_r'
 ]

 key = str(key).strip('\'')
 if(key in subkeys):
 #print(key)
 logging.info(key)
 else:
 pass


def record_chair(output_file):
 """Read the data stream coming from the serial monitor
 in order to get the sensor readings

 Args:
 output_file (str): The file name, where the data stream will be stored
 """
 serial_port = "/dev/ttyACM0"
 baud_rate = 9600
 ser = serial.Serial(serial_port,baud_rate)
 logging.basicConfig(filename=output_file,level=logging.DEBUG,format="%(asctime)s %(message)s")
 flag = False
 start = time.time()
 while time.time() - start < 60.0:
 try:
 serial_data = str(ser.readline().decode().strip('\r\n'))
 time.sleep(0.2)
 tmp = serial_data.split(' ')[0] #Getting Sensor Id
 if(tmp == 'A0'):
 flag = True
 if (flag and tmp != 'A4'):
 #print(serial_data)
 logging.info(serial_data)
 if(flag and tmp == 'A4'):
 flag = False
 #print(serial_data)
 logging.info(serial_data)
 except (UnicodeDecodeError, KeyboardInterrupt) as err:
 print(err)
 print(err.args)
 sys.exit(0)


def initialize_dirs():
 """Create the appropriate directories in order to save
 and process the collected data
 """
 current_path = os.path.abspath(os.getcwd())
 os.chdir('..')
 current_path = (os.path.abspath(os.curdir)) #/Multodal_User_Monitoring
 current_path = os.path.join(current_path,'Data')
 create_subdirs([current_path])

 #Create mouse log folder
 mouse = os.path.join(current_path,'Mouse')
 create_subdirs([mouse])
 #Create mouse subfolders
 names = concat_names(mouse)
 create_subdirs(names)

 #Create keyboard log folder
 keyboard = os.path.join(current_path,'Keyboard')
 create_subdirs([keyboard])
 #Create keyboard subfolders
 names = concat_names(keyboard)
 create_subdirs(names)

 #Create the chair log folder
 chair = os.path.join(current_path,'Chair')
 create_subdirs([chair])
 #Create chair subfolders
 names = concat_names(chair)
 create_subdirs(names)

 #Create webcam log folder
 webcam = os.path.join(current_path,'Webcam')
 create_subdirs([webcam])

def concat_names(dir) -> str:
 """Concatenate the given folder names
 with the appropriate path

 Args:
 dir (str): The directory to create the subfolders

 Returns:
 list: The new absolute paths
 """
 raw_data = os.path.join(dir,'Raw')
 edited_data = os.path.join(dir,'Edited_logs')
 csv_data = os.path.join(dir,'CSV')
 features = os.path.join(dir,'Features')
 dirs = [raw_data,edited_data,csv_data,features]
 return dirs


def create_subdirs(paths):
 """Create sub directories given some absolute paths

 Args:
 paths (list): A list containing the paths to be created
 """
 for index,path in enumerate(paths):
 if(os.path.isdir(paths[index])):
 pass
 else:
 os.mkdir(paths[index])


def round_down(num,divisor) -> int:
 """Round the number of lines contained into the recording file,
 down to the nearest multiple of the given divisor

 Args:
 num (int): The number of lines contained into the given log file
 divisor (int): The divisor in order to get tuples of divisor

 Returns:
 int: The nearest multiple of five
 """
 return num-(num%divisor)


def get_date() -> str:
 """Get the current date in order to properly name
 the recored log files
 Returns:
 str: The current date in: YY_MM_DD format
 """
 return datetime.now().strftime('%Y_%m_%d')


def get_name(modality) -> str:
 """Save the recorded log into /Data//Raw

 Args:
 modality (str): The log data source

 Returns:
 str: The absolute path where each recording is saved
 """
 current_path = os.path.abspath(os.getcwd())
 current_path = os.path.join(current_path,'Data')

 if modality == 'Chair':
 chair_path = os.path.join(current_path,modality,'Raw')
 return chair_path

 elif modality == 'Mouse':
 mouse_path = os.path.join(current_path,modality,'Raw')
 return mouse_path

 elif modality == 'Keyboard':
 keyboard_path = os.path.join(current_path,modality,'Raw')
 return keyboard_path


def crawl_dir(target,folder) -> str:
 """Enumerate all the given files in a directory
 based on the given file extension

 Args:
 target (str): The file to search for
 folder (str): The folder to search

 Returns:
 [type]: A list containing the file names
 """
 current_path = os.path.abspath(os.getcwd())
 path = os.path.join(current_path,folder)
 file_names =[]
 for f in os.listdir(path):
 if(f.endswith(target)):
 fname=os.path.join(path,f)
 file_names.append(fname)
 return file_names


def convert_keys2_csv(input_file,output_file):
 """Convert the data stream file(keylogger recording) from .txt to .csv format

 Args:
 input_file (str): The data stream file in .txt format
 output_file (str): The csv extension file name
 """
 df = pd.read_fwf(input_file)
 col_names = ['Date','Time','Key']
 df.to_csv(output_file,header=col_names,encoding='utf-8',index=False)


def convert_mouse2_csv(input_file,output_file):
 """Convert the data stream file(mouselogger recording) from .txt to .csv format

 Args:
 input_file (str): The data stream file in .txt format
 output_file (str): The csv extension file name
 """
 df = pd.read_fwf(input_file)
 col_names = ['Date','Time','Action','PosX','PosY','Button']
 df.to_csv(output_file,header=col_names,encoding='utf-8',index=False)


def convert_chair_2_csv(input_file,output_file):
 """Convert the data stream file(chair recording)
 from .txt to .csv format

 Args:
 input_file (str): The data stream file in .txt format
 output_file (str): The csv extension file name
 """
 if(os.path.isfile(input_file)):
 pass
 else:
 print('Invalid working directory...')
 print('Aborting...')
 sys.exit(0)

 tmp0,tmp1,tmp2,tmp3,tmp4 = 0,1,2,3,4

 line_number = 0
 for line in open(input_file).readlines():
 line_number += 1

 rounded_line = round_down(line_number,5)
 d = collections.defaultdict(list)

 with open(input_file,'r') as f1:
 lines = f1.readlines()
 for i in range(rounded_line // 5):
 #Sensor:Analog input 0 values
 Sid0 = lines[i+tmp0]
 temp = Sid0.split()
 d['Sid0'].append([temp[0],temp[1],temp[2],temp[3]])
 #Sensor:Analog input 1 values
 Sid1 = lines[i+tmp1]
 temp = Sid1.split()
 d['Sid1'].append([temp[0],temp[1],temp[2],temp[3]])
 #Sensor:Analog input 2 values
 Sid2 = lines[i+tmp2]
 temp = Sid2.split()
 d['Sid2'].append([temp[0],temp[1],temp[2],temp[3]])
 #Sensor:Analog input 3 values
 Sid3 = lines[i+tmp3]
 temp = Sid3.split()
 d['Sid3'].append([temp[0],temp[1],temp[2],temp[3]])
 #Sensor:Analog input 4 values
 Sid4 = lines[i+tmp4]
 temp = Sid4.split()
 d['Sid4'].append([temp[0],temp[1],temp[2],temp[3]])

 tmp0 += 4
 tmp1 += 4
 tmp2 += 4
 tmp3 += 4
 tmp4 += 4

 l = []
 for i in range(rounded_line // 5):
 date = d['Sid0'][i][0]
 time = d['Sid0'][i][1]
 A0_val = d['Sid0'][i][3]
 A1_val = d['Sid1'][i][3]
 A2_val = d['Sid2'][i][3]
 A3_val = d['Sid3'][i][3]
 A4_val = d['Sid4'][i][3]
 l.append([date,time,A0_val,A1_val,A2_val,A3_val,A4_val])

 sensor_readings_df = pd.DataFrame.from_records(l)
 sensor_readings_df.columns = ['Date','Time','A0','A1','A2','A3','A4']
 sensor_readings_df.to_csv(output_file, encoding='utf-8', index=False)
 del l


def parse_raw_data(modality):
 """Convert each modality's raw data into csv format and move
 the edited raw data into the appropriate Edited_logs folder

 Args:
 modality (str): The data source
 """
 #Change directories
 current_path = os.path.abspath(os.getcwd()) #/Functions
 os.chdir('..')
 current_path = (os.path.abspath(os.curdir)) #/Multimodal_User_Monitoring
 os.chdir('./Data')#/Multimodal_User_Monitoring/Data
 current_path = (os.path.abspath(os.curdir)) #/Multimodal_User_Monitoring/Data
 current_path = os.path.join(current_path,modality) #example: /Multimodal_User_Monitoring/Data/<modality>
 raw_data_path = os.path.join(current_path,'Raw')
 csv_data_path = os.path.join(current_path,'CSV')
 edited_logs_path = os.path.join(current_path,'Edited_logs')

 txt_names = crawl_dir('.txt',raw_data_path)
 csv_names = []
 for elem in txt_names:
 name = elem.split('/')[-1].split('.')[0]
 csv_name = name+'.csv'
 tmp = os.path.join(csv_data_path,csv_name)
 csv_names.append(tmp)

 if modality == 'Mouse':
 if len(txt_names) == len(csv_names):
 for i, elem in enumerate(txt_names):
 #for i in range(len(txt_names)):
 convert_mouse2_csv(txt_names[i],csv_names[i])
 shutil.move(txt_names[i],edited_logs_path)

 elif modality == 'Keyboard':
 if len(txt_names) == len(csv_names):
 for i, elem in enumerate(txt_names):
 #for i in range(len(txt_names)):
 convert_keys2_csv(txt_names[i],csv_names[i])
 shutil.move(txt_names[i],edited_logs_path)

 elif modality == 'Chair':
 if len(txt_names) == len(csv_names):
 for i, elem in enumerate(txt_names):
 #for i in range(len(txt_names)):
 convert_chair_2_csv(txt_names[i],csv_names[i])
 shutil.move(txt_names[i],edited_logs_path)

</modality>


I need to mention that the logs of the python3 scripts are empty


-
Releasing GME Players and Tools
I just can’t stop living in the past. To that end, I’ve been playing around with the Game Music Emu (GME) library again. This is a software library that plays an impressive variety of special music files extracted from old video games.
I have just posted a series of GME tools and associated utilities up on Github.
Clone the repo and try them out. The repo includes a small test corpus since one of the most tedious parts about playing these files tends to be tracking them down in the first place.
Players
At first, I started with trying to write some simple command line audio output programs based on GME. GME has to be the simplest software library that it has ever been my pleasure to code against. All it took was a quick read through the gme.h header file and it was immediately obvious how to write a simple program.First, I wrote a command line tool that output audio through PulseAudio on Linux. Then I made a second program that used ALSA. Guess what I learned through this exercise ? PulseAudio is actually far easier to program than ALSA.
I also created an SDL player, seen in my last post regarding how to write an oscilloscope. I think I have the A/V sync correct now. It’s a little more fun to use than the command line tools. It also works on non-Linux platforms (tested at least on Mac OS X).
Utilities
I also wrote some utilities. I’m interested in exporting metadata from these rather opaque game music files in order to make them a bit more accessible. To that end, I wrote gme2json, a program that uses the GME library to fetch data from a game music file and then print it out in JSON format. This makes it trivial to extract the data from a large corpus of game music files and work with it in many higher level languages.Finally, I wrote a few utilities that repack certain ad-hoc community-supported game music archives into... well, an ad-hoc game music archive of my own device. Perhaps it’s a bit NIH syndrome, but I don’t think certain of these ad-hoc community formats were very well thought-out, or perhaps made sense a decade or more ago. I guess I’m trying to bring a bit of innovation to this archival process.
Endgame
I haven’t given up on that SaltyGME idea (playing these game music files directly in a Google Chrome web browser via Google Chrome). All of this ancillary work is leading up to that goal.Silly ? Perhaps. But I still think it would be really neat to be able to easily browse and play these songs, and make them accessible to a broader audience.