Skip to content

Instantly share code, notes, and snippets.

@fergusq
Last active June 17, 2018 15:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fergusq/16d9805e4f24aef492c74aef53685528 to your computer and use it in GitHub Desktop.
Save fergusq/16d9805e4f24aef492c74aef53685528 to your computer and use it in GitHub Desktop.
Radio Gem song generator
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Radio Gem</title>
<meta name="description" content="Tekoälyn toimittama radiokanava." />
<meta content="width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no" name="viewport">
<style>
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
h1 {
text-align: center;
border-top: 1px solid darkblue;
border-bottom: 1px solid darkblue;
background-color: #6A6FFA;
}
body {
background-color: lightblue;
margin: 0;
}
.player {
text-align: center;
font-size: large;
}
.card {
padding: 1em;
max-width: 26rem;
margin-top: 1rem;
margin-left: auto;
margin-right: auto;
background-color: white;
box-shadow: 0px 1px 2px;
}
.card h2 {
margin-top: 0;
}
</style>
</head>
<body>
<h1>Radio Gem</h1>
<div class="player">
<audio xmlns="http://www.w3.org/1999/xhtml" controls="controls" preload="none">
<source src="http://kaivos.org:8000/gem.ogg" type="application/ogg"/>
</audio>
<div id="current_song"></div>
</div>
<div class="card">
<h2>Mikä on Radio Gem?</h2>
<p>
Radio Gem on tietokoneen luoma radiokanava, jonka kaikki sisältö on generoitua.
Tekoäly on siis säveltänyt musiikit ja keksinyt uutiset.
Koska kaikki sisältö on satunnaista ja tuhotaan heti soittamisen jälkeen,
kaikki kappaleet ovat olemassa vain pienen hetken.
Jokainen hetki Radio Gemin seurassa on siis ainutlaatuinen.
</p>
<p>Tällä hetkellä Gem perustuu seuraaviin ohjelmiin:</p>
<ul>
<li><em>Pyco</em>, säveltävä ohjelma.
Tekijät <strong>Iikka Hauhio</strong> ja <strong>Lassi Kaimio</strong>.</li>
<li><em>Folklaine</em>, säveltävä ohjelma.
Tekijä <strong>Pauli Laine</strong>.</li>
<li><em>Ylekov</em>, uutisgeneraattori.
Tekijä <strong>Mikko Tampio</strong>.</li>
</ul>
</div>
<div class="card">
Jos Radio Gem ei toimi, otathan yhteyttä ylläpitäjä Iikka Hauhioon.
</div>
<script src="//cdn.jsdelivr.net/jquery/1.9.1/jquery-1.9.1.min.js"></script>
<script>
"use strict";
$(document).ready(function(){
setInterval(function() {
$.ajax({
url: "http://kaivos.org:8000/status-json.xsl",
dataType: "json"
}).done(function( json ) {
$( "#current_song" ).html(json.icestats.source.artist + " - " + json.icestats.source.title);
});
}, 5000);
});
</script>
</body>
</html>
#!/usr/bin/env röda
{
fileutil := require("fileutil")
mv := fileutil.mv
rm := fileutil.rm
cp := fileutil.cp
logExec := fileutil.logExec
}
rndn a, b {
return a+abs(randomInteger()%((b-a)+1))
}
function renderOgg midifile, oggfile, artist, songname, wild=FALSE {
if [ wild ] do
logExec("wildmidi", "-o", midifile..".wav", midifile)
else
logExec("gst-launch-1.0",
"filesrc", "location="..midifile,
"!", "decodebin", "!", "audioconvert", "!", "wavenc",
"!", "filesink", "location="..midifile..".wav")
done
logExec("espeak", "-v", "fi", "-s", "130", "-w", "outro.wav",
`Se oli $artist-yhtyeen tuore single $songname. Minä olen Espeak ja tämä on Radio Gem.`)
logExec("sox", midifile..".wav", "-b", "16", "music.wav", "channels", "1", "rate", "44100")
logExec("sox", "outro.wav", "-b", "16", "speak.wav", "channels", "1", "rate", "44100")
logExec("sox", "music.wav", "speak.wav", "song.wav")
logExec("oggenc", "--utf8",
"-q", "4",
"-a", artist, "-t", songname,
"-o", oggfile, "song.wav")
rm midifile..".wav"
rm "music.wav"
rm "outro.wav"
rm "speak.wav"
rm "song.wav"
}
function generateFolklaine songname, filename {
logExec "python", "../../3party/folklaine/folklaine.py"
renderOgg "folk0.mid", filename, "Folklaine", songname
rm "folk0.mid"
}
function generatePyco songname, filename {
randomize songname
type := rndn(1,3)
scriptfile := filename..".txt"
{
print "seed = ", songname
print "tempo = ", rndn(40, 220)
print "strings\\nonZeroIntervals = 10000" if [ type = 3 ]
verses := []
seq(0, rndn(1,3)) | for i do
verse := chr(ord("A")+i)
letters := []
seq(0, rndn(1,6)) | for j do
letters += chr(ord("a")+rndn(0,5))..(push(".") if randomBoolean else push(""))
done
if [ type = 3 ] do
print "*", verse, " = ", letters&" ", " (strings)"
else
print verse, " = ", letters&" "
done
verses += verse
done
struct := []
seq(0, rndn(6,13)) | for _ do
struct += verses[rndn(0,#verses-1)]
done
print struct&" "
} | writeStrings scriptfile
if [ type = 1 ] do
logExec "python3", "../../Python/Sävellys/satunnainen_script.py", "--out", "pyco", scriptfile
else
instrument := push("--random-instruments") if [ type = 2 ] else push("--strings")
logExec "python3", "../../Python/Sävellys/satunnainen_script.py", instrument, "--out", "pyco", scriptfile
done
renderOgg "sats-pyco.mid", filename, "Pyco", songname
rm "sats-pyco.mid"
}
oneOf s...{[s[rndn(0,#s-1)]]}
function generateName {
adj := oneOf(*[readLines("words/adjs.txt")])
noun := oneOf(*[readLines("words/nouns.txt")])
song := chr(ord(adj[0:1])-32)..adj[1:].." "..noun
return song, adj.."_"..noun
}
function getnews {
return [{}()|bufferedExec("java", "-jar", "ylekov.jar", "generate")][0]
}
function gettitle {
if fileExists("news/news.ogg") do
title := [{}()|bufferedExec("vorbiscomment", "-l", "news/news.ogg")][0]
return parseInteger(match("(?i)title=(.+)", title)[1])
done
return 0
}
function generateNews songname, filename {
news := getnews()
num = gettitle()
if [ num > 1000 ] do
logExec("java", "-jar", "ylekov.jar", "update")
num = 0
done
logExec("espeak", "-v", "fi", "-s", "130", "-w", "news.wav", "Sitten seuraa uutinen. "..news)
logExec("oggenc", "-q", "4,", "-a", "espeak", "-t", num+1, "-o", "news/news.ogg", "news.wav")
rm "news.wav"
cp "news/news.ogg", filename
}
main {
rm file for file in [ls(".")] if [ file =~ ".*\\.ogg" ]
generateName | pull songname, filename
(oneOf(generatePyco, generatePyco, generatePyco, generateFolklaine, generateNews)) songname, filename..".ogg"
print filename..".ogg"
}
<?xml version="1.0"?>
<ices>
<!-- run in background -->
<background>0</background>
<!-- where logs, etc go. -->
<logpath>./log</logpath>
<logfile>ices.log</logfile>
<!-- 1=error,2=warn,3=info,4=debug -->
<loglevel>4</loglevel>
<!-- set this to 1 to log to the console instead of to the file above -->
<consolelog>0</consolelog>
<!-- optional filename to write process id to -->
<!-- <pidfile>/home/ices/ices.pid</pidfile> -->
<stream>
<!-- metadata used for stream listing (not currently used) -->
<metadata>
<name>Radio Gem</name>
<genre>Algorithmic</genre>
<description>Algorithmic music station</description>
</metadata>
<!-- input module
The module used here is the playlist module - it has
'submodules' for different types of playlist. There are
two currently implemented, 'basic', which is a simple
file-based playlist, and 'script' which invokes a command
to returns a filename to start playing. -->
<input>
<module>playlist</module>
<param name="type">script</param>
<param name="program">./next_song.röd</param>
</input>
<!-- Stream instance
You may have one or more instances here. This allows you to
send the same input data to one or more servers (or to different
mountpoints on the same server). Each of them can have different
parameters. This is primarily useful for a) relaying to multiple
independent servers, and b) encoding/reencoding to multiple
bitrates.
If one instance fails (for example, the associated server goes
down, etc), the others will continue to function correctly.
This example defines two instances as two mountpoints on the
same server. -->
<instance>
<!-- Server details:
You define hostname and port for the server here, along with
the source password and mountpoint. -->
<hostname>localhost</hostname>
<port>8000</port>
<password>PASSWORD HERE</password>
<mount>/gem.ogg</mount>
<!-- Reconnect parameters:
When something goes wrong (e.g. the server crashes, or the
network drops) and ices disconnects from the server, these
control how often it tries to reconnect, and how many times
it tries to reconnect. Delay is in seconds.
If you set reconnectattempts to -1, it will continue
indefinately. Suggest setting reconnectdelay to a large value
if you do this.
-->
<reconnectdelay>2</reconnectdelay>
<reconnectattempts>5</reconnectattempts>
<!-- maxqueuelength:
This describes how long the internal data queues may be. This
basically lets you control how much data gets buffered before
ices decides it can't send to the server fast enough, and
either shuts down or flushes the queue (dropping the data)
and continues.
For advanced users only.
-->
<maxqueuelength>80</maxqueuelength>
<!-- Live encoding/reencoding:
Currrently, the parameters given here for encoding MUST
match the input data for channels and sample rate. That
restriction will be relaxed in the future.
Remove this section if you don't want your files getting reencoded.
-->
<encode>
<nominal-bitrate>64000</nominal-bitrate> <!-- bps. e.g. 64000 for 64 kbps -->
<samplerate>44100</samplerate>
<channels>1</channels>
</encode>
</instance>
</stream>
</ices>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment