diff --git a/Dockerfile b/Dockerfile index acfb15d..ab87e6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,8 +19,5 @@ RUN pip install -r requirements.txt COPY *.py ./ RUN python install.py -# Copy the app's code -COPY main.py . - # Run the application CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/animate_fractal.py b/animate_fractal.py new file mode 100644 index 0000000..b8cad80 --- /dev/null +++ b/animate_fractal.py @@ -0,0 +1,39 @@ +import numpy as np +import matplotlib.pyplot as plt +from matplotlib import animation + +from mandelbrot import mandelbrot + + +def animategif(): + x_start, y_start = -2, -1.5 # an interesting region starts here + width, height = 3, 3 # for 3 units up and right + density_per_unit = 100 # how many pixels per unit + + # real and imaginary axis + re = np.linspace(x_start, x_start + width, width * density_per_unit) + im = np.linspace(y_start, y_start + height, height * density_per_unit) + + fig = plt.figure(figsize=(5, 5)) # instantiate a figure to draw + ax = plt.axes() # create an axes object + + def animate(i): + ax.clear() # clear axes object + ax.set_xticks([], []) # clear x-axis ticks + ax.set_yticks([], []) # clear y-axis ticks + + X = np.empty((len(re), len(im))) # re-initialize the array-like image + threshold = round(1.15 ** (i + 1)) # calculate the current threshold + + # iterations for the current threshold + for i in range(len(re)): + for j in range(len(im)): + X[i, j] = mandelbrot(re[i], im[j], threshold) + + # associate colors to the iterations with an iterpolation + img = ax.imshow(X.T, interpolation="bicubic", cmap='magma') + + return [img] + + anim = animation.FuncAnimation(fig, animate, frames=45, interval=120, blit=True) + return anim diff --git a/bell/417116__dersinnsspace__tibetan-bowl_right-hit-mod.wav b/bell/417116__dersinnsspace__tibetan-bowl_right-hit-mod.wav new file mode 100644 index 0000000..04a9ae5 Binary files /dev/null and b/bell/417116__dersinnsspace__tibetan-bowl_right-hit-mod.wav differ diff --git a/main.py b/main.py index bd30b54..f5fb24f 100644 --- a/main.py +++ b/main.py @@ -9,6 +9,7 @@ from AccelBrainBeat.brainbeat.binaural_beat import BinauralBeat import pydub import random +from animate_fractal import animategif from blend_av import blend_av app = FastAPI() @@ -37,7 +38,7 @@ class VideoProperties(BaseModel): main_frequency: int breath_pattern_in: int breath_pattern_out: int - color_palette = str + color_scheme = str class AvProperties(BaseModel): @@ -66,12 +67,11 @@ async def get_generate_audio(audio_properties: AudioProperties): :param audio_properties: :return: """ - # Generate a random 32 hexadecimal string tmp_dir = pathlib.Path("tmp_sound") tmp_dir.mkdir(parents=True, exist_ok=True) # Remove existing files in tmp_dir for filename in os.listdir(tmp_dir): - os.remove(str(tmp_dir / filename)) + os.remove(tmp_dir / str(filename)) bell_dir = pathlib.Path("bell") filename_prefix = ''.join([random.choice('0123456789ABCDEF') for i in range(32)]) filename = f"{filename_prefix}.wav" @@ -80,18 +80,32 @@ async def get_generate_audio(audio_properties: AudioProperties): b.save_beat(output_file_name=str(tmp_dir / "binaural_beat.wav"), frequencys=(audio_properties.main_frequency, audio_properties.lag_frequency), - play_time=(audio_properties.breath_pattern_in + audio_properties.breath_pattern_out)*2, + play_time=(audio_properties.breath_pattern_in + audio_properties.breath_pattern_out) * 2, volume=audio_properties.volume ) pydub_background = pydub.AudioSegment.from_wav(tmp_dir / "binaural_beat.wav") - pydub_bell = pydub.AudioSegment.from_wav(bell_dir / audio_properties.bell_sound) - pydub_mix = pydub_background. \ - overlay(pydub_bell, position=0). \ - overlay(pydub_bell, position=audio_properties.breath_pattern_in * 1000). \ - overlay(pydub_bell, position=(audio_properties.breath_pattern_in+audio_properties.breath_pattern_out) * 1000). \ - overlay(pydub_bell, position=(audio_properties.breath_pattern_in*2+audio_properties.breath_pattern_out) * 1000) + bell_file = "" + if audio_properties.bell_sound == "Cloche": + bell_file = "339810__inspectorj__hand-bells-a-single-mod.wav" + skip_bell = False + elif audio_properties.bell_sound == "Bol tibétain": + bell_file = "417116__dersinnsspace__tibetan-bowl_right-hit-mod.wav" + skip_bell = False + else: + skip_bell = True + if skip_bell: + pydub_mix = pydub_background + else: + pydub_bell = pydub.AudioSegment.from_wav(bell_dir / bell_file) + pydub_mix = pydub_background. \ + overlay(pydub_bell, position=0). \ + overlay(pydub_bell, position=audio_properties.breath_pattern_in * 1000). \ + overlay(pydub_bell, + position=(audio_properties.breath_pattern_in + audio_properties.breath_pattern_out) * 1000). \ + overlay(pydub_bell, + position=(audio_properties.breath_pattern_in * 2 + audio_properties.breath_pattern_out) * 1000) pydub_mix.export(tmp_dir / filename, format="wav") - return filename + return FileResponse(tmp_dir / filename) @app.get("/generate_video") @@ -101,7 +115,16 @@ async def get_generate_video(video_properties: VideoProperties): :param video_properties: :return: """ - return None + tmp_dir = pathlib.Path("tmp_video") + tmp_dir.mkdir(parents=True, exist_ok=True) + # Remove existing files in tmp_dir + for filename in os.listdir(tmp_dir): + os.remove(tmp_dir / str(filename)) + anim = animategif() + filename_prefix = ''.join([random.choice('0123456789ABCDEF') for i in range(32)]) + filename = f"{filename_prefix}.gif" + anim.save(str(tmp_dir / filename), writer='imagemagick') + return FileResponse(str(tmp_dir / filename)) @app.get("/generate_av") diff --git a/mandelbrot.py b/mandelbrot.py new file mode 100644 index 0000000..a47a8e9 --- /dev/null +++ b/mandelbrot.py @@ -0,0 +1,21 @@ +# Source: https://matplotlib.org/matplotblog/posts/animated-fractals/ +def mandelbrot(x, y, threshold): + """Calculates whether the number c = x + i*y belongs to the + Mandelbrot set. In order to belong, the sequence z[i + 1] = z[i]**2 + c + must not diverge after 'threshold' number of steps. The sequence diverges + if the absolute value of z[i+1] is greater than 4. + + :param float x: the x component of the initial complex number + :param float y: the y component of the initial complex number + :param int threshold: the number of iterations to considered it converged + """ + # initial conditions + c = complex(x, y) + z = complex(0, 0) + + for i in range(threshold): + z = z ** 2 + c + if abs(z) > 4.: # it diverged + return i + + return threshold - 1 # it didn't diverge diff --git a/requirements.txt b/requirements.txt index ac4dbcf..9afd4bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,5 @@ starlette~=0.26.1 AccelBrainBeat~=1.0.5 httpx~=0.23.3 pydub~=0.25.1 +numpy~=1.24.2 +matplotlib~=3.7.1 \ No newline at end of file