2019-08-20 12:11:41 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
from glob import glob
|
2019-08-25 07:01:33 +02:00
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import sys
|
2019-08-20 16:43:47 +02:00
|
|
|
from threading import Thread
|
2019-08-25 07:01:33 +02:00
|
|
|
import time
|
2019-08-20 16:15:08 +02:00
|
|
|
from typing import Union, List, Mapping
|
2019-08-25 07:01:33 +02:00
|
|
|
|
|
|
|
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
2019-08-20 16:43:47 +02:00
|
|
|
from pygame import mixer
|
2019-08-20 12:11:41 +02:00
|
|
|
|
2019-08-25 07:01:33 +02:00
|
|
|
from collection import SampleCollection
|
2019-08-20 14:29:29 +02:00
|
|
|
from atmorepl import AtmoReplRunner
|
2019-08-20 12:11:41 +02:00
|
|
|
|
2019-08-20 12:41:54 +02:00
|
|
|
VERBOSITY = 0
|
2019-08-20 12:11:41 +02:00
|
|
|
|
2019-08-25 07:01:33 +02:00
|
|
|
channel_index = 0
|
2019-08-20 16:15:08 +02:00
|
|
|
|
|
|
|
|
2019-08-20 13:30:25 +02:00
|
|
|
class CampAtmo:
|
|
|
|
tracks_per_type = 2
|
2019-08-25 07:01:33 +02:00
|
|
|
single_track_types = ["lines", "reactions"]
|
2019-08-20 16:15:08 +02:00
|
|
|
background_track_types = ["background", "buzzwords"]
|
2019-08-25 07:01:33 +02:00
|
|
|
type_mix = {
|
|
|
|
"lines": 0.8,
|
|
|
|
"reactions": 0.7,
|
|
|
|
"buzzwords": 0.6,
|
|
|
|
"background": 0.3,
|
|
|
|
"other": 0.5,
|
|
|
|
}
|
2019-08-20 16:15:08 +02:00
|
|
|
lines_delta_sec = (5, 15)
|
|
|
|
line_reaction_wait_sec = 0.8
|
2019-08-20 12:41:54 +02:00
|
|
|
|
2019-08-20 13:30:25 +02:00
|
|
|
def __init__(self):
|
2019-08-20 16:43:47 +02:00
|
|
|
self.stopped = False
|
2019-08-20 13:30:25 +02:00
|
|
|
mixer.init(frequency=22050 * 2)
|
2019-08-25 07:01:33 +02:00
|
|
|
type_dirs = glob("samples/*/")
|
|
|
|
self.types = {}
|
|
|
|
for type_dir in type_dirs:
|
|
|
|
type_name = type_dir.split("/")[1]
|
|
|
|
self.types[type_name] = SampleCollection(
|
|
|
|
type_dir, volume=self.type_mix[type_name]
|
|
|
|
)
|
|
|
|
i = 0
|
|
|
|
mixer.set_num_channels(
|
|
|
|
sum(
|
|
|
|
[
|
|
|
|
1 if name in self.single_track_types else self.tracks_per_type
|
|
|
|
for name in self.types
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
v(f"{mixer.get_num_channels()} channels set")
|
|
|
|
self.type_channels = {}
|
|
|
|
for type_name in self.types:
|
|
|
|
self.type_channels[type_name] = []
|
|
|
|
for k in range(
|
|
|
|
1 if type_name in self.single_track_types else self.tracks_per_type
|
|
|
|
):
|
|
|
|
self.type_channels[type_name].append(mixer.Channel(i))
|
|
|
|
i += 1
|
2019-08-20 12:41:54 +02:00
|
|
|
|
2019-08-20 16:43:47 +02:00
|
|
|
def start(self):
|
|
|
|
self.background_thread = Thread(
|
|
|
|
None, self.run_forever, "atmo-background", daemon=True
|
|
|
|
)
|
|
|
|
self.background_thread.start()
|
|
|
|
self.lines_thread = Thread(
|
|
|
|
None, self.run_lines_forever, "atmo-lines", daemon=True
|
|
|
|
)
|
|
|
|
self.lines_thread.start()
|
|
|
|
|
2019-08-20 17:05:14 +02:00
|
|
|
def stop(self):
|
2019-08-20 16:43:47 +02:00
|
|
|
self.stopped = True
|
2019-08-20 17:06:39 +02:00
|
|
|
self.background_thread.join(1.2)
|
2019-08-20 16:43:47 +02:00
|
|
|
self.lines_thread.join(2)
|
|
|
|
|
2019-08-20 13:30:25 +02:00
|
|
|
def load_sounds(self):
|
2019-08-25 07:01:33 +02:00
|
|
|
for _, collection in self.types.items():
|
|
|
|
collection.load()
|
2019-08-20 13:12:59 +02:00
|
|
|
|
2019-08-20 13:30:25 +02:00
|
|
|
def run_forever(self):
|
2019-08-20 16:43:47 +02:00
|
|
|
while not self.stopped:
|
2019-08-25 07:01:33 +02:00
|
|
|
for type_name, channels in self.type_channels.items():
|
2019-08-20 16:15:08 +02:00
|
|
|
if (
|
2019-08-25 07:01:33 +02:00
|
|
|
type_name not in self.background_track_types
|
|
|
|
or len(self.types[type_name]) == 0
|
2019-08-20 16:15:08 +02:00
|
|
|
):
|
2019-08-20 12:11:41 +02:00
|
|
|
continue
|
2019-08-25 07:01:33 +02:00
|
|
|
self.play_type_sounds(type_name)
|
2019-08-20 12:11:41 +02:00
|
|
|
time.sleep(1)
|
2019-08-20 16:43:47 +02:00
|
|
|
|
|
|
|
def run_lines_forever(self):
|
|
|
|
while not self.stopped:
|
|
|
|
time.sleep(random.randrange(*self.lines_delta_sec))
|
|
|
|
length_sec = self.play_type_sounds("lines")
|
|
|
|
time.sleep(length_sec + self.line_reaction_wait_sec)
|
2019-08-25 07:01:33 +02:00
|
|
|
length_sec = self.play_type_sounds("reactions")
|
|
|
|
time.sleep(length_sec + self.line_reaction_wait_sec)
|
2019-08-20 16:43:47 +02:00
|
|
|
self.play_type_sounds("reactions")
|
|
|
|
|
2019-08-25 07:01:33 +02:00
|
|
|
def play_type_sounds(self, type_name) -> int:
|
|
|
|
if type_name in self.types and len(self.types[type_name]) == 0:
|
|
|
|
return
|
|
|
|
max_sound_length = 0
|
|
|
|
for channel in self.type_channels[type_name]:
|
|
|
|
if channel.get_queue() is not None and len(self.types[type_name]) > 1:
|
|
|
|
continue
|
|
|
|
sample = self.types[type_name].next()
|
|
|
|
max_sound_length = max(max_sound_length, sample.sound.get_length())
|
|
|
|
channel.queue(sample.sound)
|
|
|
|
if (
|
|
|
|
type_name not in self.single_track_types
|
|
|
|
and channel.get_queue() is None
|
|
|
|
and len(self.types[type_name]) > 1
|
|
|
|
):
|
|
|
|
channel.queue(self.types[type_name].next_sound())
|
|
|
|
return max_sound_length
|
|
|
|
|
|
|
|
def get_status(self):
|
|
|
|
ret_strs = []
|
|
|
|
for type_name, channels in self.type_channels.items():
|
|
|
|
samples = []
|
|
|
|
for chan in channels:
|
|
|
|
sample = self.types[type_name].get_sample_from_channel(chan)
|
|
|
|
if sample is not None:
|
|
|
|
samples.append(sample)
|
|
|
|
if samples:
|
|
|
|
sample_infos = [
|
|
|
|
f"{s.path} @{s.sound.get_volume():.0%}vol" for s in samples
|
|
|
|
]
|
|
|
|
ret_strs.append(f"{type_name}: {sample_infos}")
|
|
|
|
return "\n".join(ret_strs)
|
|
|
|
|
|
|
|
def pause(self):
|
|
|
|
if mixer.get_busy():
|
|
|
|
mixer.pause()
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
mixer.unpause()
|
|
|
|
return True
|
|
|
|
|
2019-08-20 13:30:25 +02:00
|
|
|
|
|
|
|
def main():
|
|
|
|
atmo = CampAtmo()
|
2019-08-20 14:29:29 +02:00
|
|
|
atmoRepl = AtmoReplRunner(atmo, port=7723)
|
|
|
|
atmoRepl.start()
|
2019-08-20 16:43:47 +02:00
|
|
|
atmo.start()
|
2019-08-20 13:30:25 +02:00
|
|
|
try:
|
2019-08-20 16:43:47 +02:00
|
|
|
while True:
|
2019-08-25 07:01:33 +02:00
|
|
|
time.sleep(10)
|
2019-08-20 12:11:41 +02:00
|
|
|
except KeyboardInterrupt:
|
2019-08-20 16:43:47 +02:00
|
|
|
print("exiting...")
|
|
|
|
atmo.stop()
|
2019-08-20 12:11:41 +02:00
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
2019-08-20 12:41:54 +02:00
|
|
|
def v(*msg):
|
|
|
|
if VERBOSITY >= 1:
|
|
|
|
print(*msg)
|
|
|
|
|
|
|
|
|
2019-08-20 12:11:41 +02:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|