Added VHSStyler
This commit is contained in:
parent
d0a0c92170
commit
d98293667b
42
README.md
42
README.md
|
@ -1,8 +1,9 @@
|
||||||
# vaporwave generator 旺育栄
|
# vaporwave generator 旺育栄
|
||||||
|
|
||||||
A vaporwave music (+art, +video soon, I promise) generator bodged together using code from various sources. Runs on Python3
|
A vaporwave music (+art, +video soon, I promise) generator bodged together using code from various sources. Runs on Python3
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: main.py [-h] [-M] [-V] [-i INPUT]
|
usage: main.py [-h] [-M] [-P] [-V] [-i INPUT]
|
||||||
|
|
||||||
This program takes YouTube URL or title of a song and converts it into
|
This program takes YouTube URL or title of a song and converts it into
|
||||||
vaporwave
|
vaporwave
|
||||||
|
@ -10,13 +11,26 @@ vaporwave
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-M, --music generate v a p o r w a v e music
|
-M, --music generate v a p o r w a v e music
|
||||||
|
-P, --picture generate VHS Style image
|
||||||
-V, --version show program version
|
-V, --version show program version
|
||||||
-i INPUT, --input INPUT
|
-i INPUT, --input INPUT
|
||||||
input url
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If the program gives an error for sox, try running `ulimit -n 999'`
|
If the program gives an error for sox, try running `ulimit -n 999'`
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
### M U S I C
|
||||||
|
|
||||||
|
Linking to Bandcamp soon
|
||||||
|
|
||||||
|
### V H S I M A G E
|
||||||
|
|
||||||
|
![]("assets/in-vhs.jpg")
|
||||||
|
|
||||||
|
![]("assets/out-vhs.jpg")
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
This was tested on macOS Catalina ( so should work on almost all macOS versions).
|
This was tested on macOS Catalina ( so should work on almost all macOS versions).
|
||||||
|
@ -43,28 +57,38 @@ pip install -r requirements.txt
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### YouTube URL
|
### M U S I C
|
||||||
|
|
||||||
|
#### YouTube URL
|
||||||
```
|
```
|
||||||
python3 main.py -M -i <YOUTUBE_URL>
|
python3 main.py -M -i <YOUTUBE_URL>
|
||||||
```
|
```
|
||||||
### Song Title
|
#### Song Title
|
||||||
```
|
```
|
||||||
python3 main.py -M -i Song Title
|
python3 main.py -M -i Song Title
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### V H S I M A G E S
|
||||||
|
|
||||||
|
`python3 main.py -P -i "image.jpg"`
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
This project is a result of bodging and therefore has tons of bugs which need to be ironed out
|
This project is a result of bodging and therefore has tons of bugs which need to be ironed out
|
||||||
|
|
||||||
## To-Do
|
## To-Do
|
||||||
|
|
||||||
[ ] Move away from using os.system calls, and use Python modules instead ( Looking at you, Sox and aubio)
|
[] Move away from using os.system calls, and use Python modules instead ( Looking at you, Sox and aubio)
|
||||||
[ ] Clean the Code
|
[] Clean the Code
|
||||||
[ ] Add Artwork Generator
|
[] Add Artwork Generator
|
||||||
[ ] Add Video Generator
|
[x] VHS Picture Styler ( Added in v1.5 )
|
||||||
|
[] Add Video Generator
|
||||||
|
[] Add Custom Date to VHS Styler
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
@WJLiddy His repo `Macintech` forms the base code for the music generator
|
@WJLiddy His repo `Macintech` forms the base code for the music generator
|
||||||
|
|
||||||
@felipecustodio Using his repo `virtualdreamsbot` YouTube DL code ( Hopefully I will be able to integrate this project as a Telegram Bot)
|
@felipecustodio Using his repo `virtualdreamsbot` YouTube DL code ( Hopefully I will be able to integrate this project as a Telegram Bot)
|
||||||
|
|
||||||
|
@Ragex04 His repo `VHS_BingImages` forms the base code for the VHS Image Styler
|
182
VaporSong.py
182
VaporSong.py
|
@ -1,182 +0,0 @@
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
from random import randint
|
|
||||||
|
|
||||||
import logzero
|
|
||||||
from logzero import logger
|
|
||||||
from logzero import setup_logger
|
|
||||||
|
|
||||||
CONFIDENCE_THRESH = 0.02
|
|
||||||
|
|
||||||
class VaporSong:
|
|
||||||
|
|
||||||
# Slows down Track
|
|
||||||
|
|
||||||
def slow_down(src, rate, dest):
|
|
||||||
cmd = "sox -G -D " + src + " " + dest + " speed " + str(rate)
|
|
||||||
os.system(cmd)
|
|
||||||
return dest
|
|
||||||
|
|
||||||
# Adds Reverb
|
|
||||||
|
|
||||||
def reverbize(src, dest):
|
|
||||||
cmd = "sox -G -D " + src + " " + dest + " reverb 100 fade 5 -0 7" # idk what this does tbh, https://stackoverflow.com/a/57767238/8386344
|
|
||||||
os.system(cmd)
|
|
||||||
return dest
|
|
||||||
|
|
||||||
|
|
||||||
# Crops "src" from "start" plus "start + dur" and return it in "dest"
|
|
||||||
def crop(src,dest,start,dur):
|
|
||||||
cmd = "sox " + src + " " + dest + " trim " + " " + str(start) + " " + str(dur)
|
|
||||||
os.system(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
# Randomly crops a part of the song of at most max_sec_len.
|
|
||||||
def random_crop(src, max_sec_len, dest):
|
|
||||||
out = subprocess.check_output(["soxi","-D",src]).rstrip()
|
|
||||||
f_len = int(float(out))
|
|
||||||
if (f_len <= max_sec_len):
|
|
||||||
os.system("cp " + src + " " + dest)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
start_region = f_len - max_sec_len
|
|
||||||
start = randint(0,start_region)
|
|
||||||
VaporSong.crop(src,dest,start,max_sec_len)
|
|
||||||
|
|
||||||
|
|
||||||
# Given a file, returns a list of [beats, confidence], executable based on audibo's test-beattracking.c
|
|
||||||
# TODO: Move away from executable and use aubio's Python module
|
|
||||||
def fetchbeats(src):
|
|
||||||
beat_matrix = []
|
|
||||||
if os.name == 'posix':
|
|
||||||
beats = subprocess.check_output(["noah", "get-beats",src]).rstrip()
|
|
||||||
else:
|
|
||||||
beats = subprocess.check_output(["get-beats",src]).rstrip()
|
|
||||||
beats_ary = beats.splitlines()
|
|
||||||
for i in beats_ary:
|
|
||||||
record = i.split()
|
|
||||||
record[0] = float(record[0])/1000.0
|
|
||||||
record[1] = float(record[1])
|
|
||||||
beat_matrix.append(record)
|
|
||||||
return beat_matrix
|
|
||||||
|
|
||||||
# Splits an audio file into beats according to beat_matrix list
|
|
||||||
|
|
||||||
def split_beat(src,beat_matrix):
|
|
||||||
split_files = []
|
|
||||||
for i in range(0,len(beat_matrix)-1):
|
|
||||||
|
|
||||||
if(beat_matrix[i][1] > CONFIDENCE_THRESH):
|
|
||||||
dur = (beat_matrix[i+1][0] - beat_matrix[i][0])
|
|
||||||
out = src.split(".")[0]+str(i)+".wav"
|
|
||||||
VaporSong.crop(src,out,beat_matrix[i][0],dur)
|
|
||||||
split_files.append(out)
|
|
||||||
return split_files
|
|
||||||
|
|
||||||
# Combines a list of sections
|
|
||||||
|
|
||||||
def combine(sections,dest):
|
|
||||||
tocomb = []
|
|
||||||
tocomb.append("sox")
|
|
||||||
tocomb.append("-G")
|
|
||||||
for section in sections:
|
|
||||||
for sample in section:
|
|
||||||
tocomb.append(sample)
|
|
||||||
tocomb.append(dest)
|
|
||||||
tmpFileLimit = len(tocomb) + 256 # in case the program messes up, it does not actually frick up your system
|
|
||||||
n = str(tmpFileLimit)
|
|
||||||
#logger.info("Setting file limit to ", n)
|
|
||||||
os.system("ulimit -n " + n)
|
|
||||||
subprocess.check_output(tocomb)
|
|
||||||
return dest
|
|
||||||
|
|
||||||
# Arbitrarily groups beats into lists of 4, 6, 8, or 9, perfect for looping.
|
|
||||||
|
|
||||||
def generate_sections(ary):
|
|
||||||
sections = []
|
|
||||||
beats = [4,6,8,9]
|
|
||||||
index = 0
|
|
||||||
while(index != len(ary)):
|
|
||||||
current_beat = beats[randint(0,len(beats)-1)]
|
|
||||||
new_section = []
|
|
||||||
while((current_beat != 0) and (index != len(ary))):
|
|
||||||
new_section.append(ary[index])
|
|
||||||
current_beat -= 1
|
|
||||||
index += 1
|
|
||||||
sections.append(new_section)
|
|
||||||
return sections
|
|
||||||
|
|
||||||
|
|
||||||
# given a list of sections, selects some of them and duplicates them, perfect for that vaporwave looping effect
|
|
||||||
def dup_sections(sections):
|
|
||||||
new_section = []
|
|
||||||
for section in sections:
|
|
||||||
new_section.append(section)
|
|
||||||
if(randint(0,1) == 0):
|
|
||||||
new_section.append(section)
|
|
||||||
return new_section
|
|
||||||
|
|
||||||
# a passage is a list of sections. This takes some sections and groups them into passages.
|
|
||||||
|
|
||||||
def make_passages(sections):
|
|
||||||
passages = []
|
|
||||||
index = 0
|
|
||||||
while(index != len(sections)):
|
|
||||||
passage_len = randint(1,4)
|
|
||||||
passage = []
|
|
||||||
while(index != len(sections) and passage_len > 0):
|
|
||||||
passage.append(sections[index])
|
|
||||||
index += 1
|
|
||||||
passage_len -= 1
|
|
||||||
passages.append(passage)
|
|
||||||
return passages
|
|
||||||
|
|
||||||
# Given all of our passages, picks some of them and inserts them into a list some number of times.
|
|
||||||
|
|
||||||
def reorder_passages(passages):
|
|
||||||
new_passages = []
|
|
||||||
passage_count = randint(5,12)
|
|
||||||
while(passage_count != 0):
|
|
||||||
passage = passages[randint(0,len(passages)-1)]
|
|
||||||
passage_count -= 1
|
|
||||||
dup = randint(1,4)
|
|
||||||
while(dup != 0):
|
|
||||||
dup -= 1
|
|
||||||
new_passages.append(passage)
|
|
||||||
return new_passages
|
|
||||||
|
|
||||||
# converts a list of passages to a list of sections.
|
|
||||||
|
|
||||||
def flatten(passages):
|
|
||||||
sections = []
|
|
||||||
for passage in passages:
|
|
||||||
for section in passage:
|
|
||||||
sections.append(section)
|
|
||||||
return sections
|
|
||||||
|
|
||||||
# It's all coming together
|
|
||||||
|
|
||||||
def vaporize_song(fname, title):
|
|
||||||
logger.info("Slowing down the music")
|
|
||||||
VaporSong.slow_down(fname, 0.7, "beats/out.wav")
|
|
||||||
#logger.info("Cropping")
|
|
||||||
#VaporSong.random_crop("beats/out.wav",150,"beats/outcrop.wav")
|
|
||||||
logger.info("Doing Beat Analysis")
|
|
||||||
bm = VaporSong.fetchbeats("beats/out.wav")
|
|
||||||
logger.info("Split into beats")
|
|
||||||
splitd = VaporSong.split_beat("beats/out.wav",bm)
|
|
||||||
#group beats to sections
|
|
||||||
logger.info("Divide into sections")
|
|
||||||
sections = VaporSong.generate_sections(splitd)
|
|
||||||
logger.info("Duping Sections")
|
|
||||||
sdup = VaporSong.dup_sections(sections)
|
|
||||||
# group sections into passages
|
|
||||||
paslist = VaporSong.make_passages(sdup)
|
|
||||||
# reorder packages
|
|
||||||
pasloop = VaporSong.reorder_passages(paslist)
|
|
||||||
sectionflat = VaporSong.flatten(pasloop)
|
|
||||||
logger.info("Mastering & Reverbing")
|
|
||||||
VaporSong.combine(sectionflat,"beats/out_norev.wav")
|
|
||||||
VaporSong.reverbize("beats/out_norev.wav","./" + (re.sub(r"\W+|_", " ", title)).replace(" ","_") + ".wav")
|
|
||||||
logger.info("Generated V A P O R W A V E")
|
|
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
18
main.py
18
main.py
|
@ -1,4 +1,5 @@
|
||||||
from VaporSong import VaporSong
|
from src.VaporSong import VaporSong
|
||||||
|
from src.VHSImage import generateVHSStyle
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import youtube_dl
|
import youtube_dl
|
||||||
|
@ -18,20 +19,28 @@ text = 'This program takes YouTube URL or title of a song and converts it into v
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description = text)
|
parser = argparse.ArgumentParser(description = text)
|
||||||
parser.add_argument("-M", "--music", help="generate v a p o r w a v e music", action="store_true")
|
parser.add_argument("-M", "--music", help="generate v a p o r w a v e music", action="store_true")
|
||||||
|
parser.add_argument("-P", "--picture", help="generate VHS Style image", action="store_true")
|
||||||
parser.add_argument("-V", "--version", help="show program version", action="store_true")
|
parser.add_argument("-V", "--version", help="show program version", action="store_true")
|
||||||
parser.add_argument("-i", "--input", help="input url")
|
parser.add_argument("-i", "--input")
|
||||||
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
music = False
|
||||||
|
picture = False
|
||||||
|
|
||||||
if args.version:
|
if args.version:
|
||||||
print("vaporwave generator 旺育栄", version)
|
print("vaporwave generator 旺育栄", version)
|
||||||
exit
|
exit
|
||||||
if args.music:
|
if args.music:
|
||||||
music = True
|
music = True
|
||||||
|
elif args.picture:
|
||||||
|
picture = True
|
||||||
if args.input:
|
if args.input:
|
||||||
query = args.input
|
query = args.input
|
||||||
else:
|
else:
|
||||||
query = input("Enter target song's name or YouTube URL: ")
|
parser.print_help()
|
||||||
|
exit
|
||||||
|
|
||||||
MAX_DURATION = 600 # In-case the program finds a compilation
|
MAX_DURATION = 600 # In-case the program finds a compilation
|
||||||
youtube_urls = ('youtube.com', 'https://www.youtube.com/', 'http://www.youtube.com/', 'http://youtu.be/', 'https://youtu.be/', 'youtu.be')
|
youtube_urls = ('youtube.com', 'https://www.youtube.com/', 'http://www.youtube.com/', 'http://youtu.be/', 'https://youtu.be/', 'youtu.be')
|
||||||
|
@ -149,3 +158,6 @@ for s in sys.argv:
|
||||||
if music:
|
if music:
|
||||||
name, title = download_file(query)
|
name, title = download_file(query)
|
||||||
gen_vapor(name, title)
|
gen_vapor(name, title)
|
||||||
|
elif picture:
|
||||||
|
generateVHSStyle(query,"out.jpg")
|
||||||
|
|
Loading…
Reference in New Issue