Browse Source

Upload files to ''

master
acheney 1 year ago
parent
commit
1f3658d050
  1. 21
      LICENSE
  2. 20
      README.md
  3. 746
      boot.pixi
  4. 1037
      demo.pixi
  5. 109
      ffmpeg_video_export.pixi
  6. 391
      mjpeg.pixi
  7. 111
      riff.pixi

21
LICENSE

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Aden Cheney
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

20
README.md

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
# abstract
this is the second version of my pixilang visualizer, with a revamped look and config options! i kept the old one up because this one probably isn't as computationally efficient as the first one (it does look good if you bake it to a video though!)
it uses a few media files, including two logos and a cover (all `.png` files) and the `.wav` file to be visualized. additonally, it also takes text for the song attribution, which is specified in the config. it takes these and produces a video the length of the audio file, with a spectrum, flashing background, cover and logo art, song info, and transition effects at the beginning and end of the song
just replace the files (and change the config section if necessary) and be on your merry way! if you're on a *nix os with ffmpeg, you can uncomment the `demo_video_export` to produce a video file to upload wherever
see it in action [here](https://www.youtube.com/watch?v=KaGbA-CDJ1s)
# how to use
1. clone this repository and unarchive it
2. replace the media files, the ones in this repo are placeholders. the `artist.png` is the logo shown first, the `logo.png` is shown second. in one use case, if you're a label, the `logo.png` is your logo and the `artist.png` is your artist's logo. the `song.wav` is the audio to be visualized, and the `cover.png` is the cover art for the audio
3. if necessary, edit the `boot.pixi` and change the config variables (everything between `-- CONFIG BEGIN --` and `-- END CONFIG --`) to your liking. everything has comments explaining what they do. i'll probably add more variables to customize in the future
4. grab pixilang from [here](https://warmplace.ru/soft/pixilang/) and run the binary for your operating system. it's free (at least for desktop operating systems)
5. in pixilang, navigate to where you extracted the repo and run the `boot.pixi` file
6. enjoy!
# credits
the main visualizer was written by me, with help from nightradio, silent broadcast, and others. the other pixilang libraries were written by nightradio and are under the mit license. the placeholder assets were made by me

746
boot.pixi

@ -0,0 +1,746 @@ @@ -0,0 +1,746 @@
// pixilang-based music visualizer
// by acheney (with help from nightradio, silent broadcast, and others)
// under mit license
include "demo.pixi"
// - CONFIG BEGIN -
// -- DEMO LIB CONFIG --
// demo width in pixels
demo_xsize = 1920
// demo height in pixels
demo_ysize = 1080
// demo use opengl
demo_opengl = 1
// demo video export filename, comment it out if you don't want to export
//demo_video_export = "video.mkv"
// demo video export fps (frames per second)
demo_video_export_fps = 60
// demo video export quality (0 - 100)
demo_video_export_q = 100
// -- TEXT CONFIG --
// text (song title, song artist, song release title)
text_title = "song title"
text_artist = "song artist"
text_release = "song release title"
// text distance scaling (0 - 1)
text_scale = 0.25
// text color
text_color = #FFFFFF
// -- SPECTRUM/VISUALIZER CONFIG --
// goertzel size
vis_size = 2048
// visualizer color
vis_color = #FFFFFF
// linear visualizer (only enabled when 1, logarithmic when 0)
vis_linear = 1
// visualizer linear interpolation (only enabled when 1, bars when 0)
vis_int = 0
// visualizer number of bars
vis_bars = 100
// visualizer bar width factor ( 0 - 1 )
vis_bar_width = 0.5
// linear visualizer minimum frequency (hz)
vis_min_freq = 20
// linear visualizer maximum frequency (hz)
vis_max_freq = 3000
// logarithmic visualizer minimum midi pitch
vis_min_pitch = 15
// logarithmic visualizer maximum midi pitch
vis_max_pitch = 102
// visualizer smoothing factor ( >= 1 )
vis_smooth = 1.5
// visualizer vertical scale
vis_scale = 50
// -- TIME CONFIG --
// time for fade to black
all_exit = 6000
// time for logo + artist + blur transitions
all_enter = 6000
// -- LOGO CONFIG --
// logo size scale ( % of image size )
logo_scale = 0.25
// logo size scale ( persistent bottom-right corner ) ( % of demo_xsize )
logo_scale_persistent = 0.075
// -- ARTIST CONFIG --
// artist size scale ( % of image size )
artist_scale = 0.25
// -- COVER CONFIG --
// cover gaussian blur radius
cover_blur_rad = 20
// cover brightness scale ( 0 - 1 )
cover_brightness = 0.5
//cover scale ( persistent top-right corner ) ( % of demo_xsize )
cover_scale = 0.075
// cover flash speed ( 0 - 1 )
cover_flash_speed = 0.75
// cover shake amplitude ( affects both amp and the zoom of the cover )
cover_shake_amp = 3
// cover shake speed ( the lower the value, the faster it is )
cover_shake_speed = 1
// -- BEGINNING FLASH CONFIG --
// speed of the flash at the beginning in seconds
beginning_flash_speed = 0.05
// fade out of the flash at the beginning in seconds
beginning_flash_fade = 2
// exponential curve of the flash at the beginning ( >=0 )
beginning_flash_curve = 5
// - END CONFIG -
demo_load_wav( "media/song.wav" )
cover = load( "media/cover.png" )
logo = load("media/logo.png")
artist = load("media/artist.png")
set_flags( cover, CFLAG_INTERP )
demo_length = 0
goertzel_values = new( vis_bars, 1, FLOAT )
cover_xsize = get_xsize(cover)
cover_ysize = get_ysize(cover)
cover_size = cover_xsize * cover_ysize
cover_src = clone(cover)
logo_xsize = get_xsize(logo)
logo_ysize = get_ysize(logo)
corner_x = -demo_xsize div 2
corner_y = -demo_ysize div 2
logo_xsize = get_xsize(logo)
logo_ysize = get_ysize(logo)
artist_xsize = get_xsize(artist)
artist_ysize = get_ysize(artist)
demo_init()
custom_init()
demo_add_scene( 0, 0, 0, view )
demo_add_scene( 0, 0, demo_length, DEMO_STOP )
demo_play()
custom_deinit()
demo_deinit()
fn view( $t1, $t2, $len )
{
clear( #000000 )
bg_render( $t1 )
vis_render( $t1 )
cover_render( $t1 )
logo_render( $t1 )
text_render( $t1 )
screen_blur( $t1 )
logos_render( $t1 )
beginning_flash_render( $t1 )
black( $t1 )
transp( 255 )
}
fn vis_render( $t1 )
{
transp( 255 )
if ( vis_linear ) {
if ( vis_int ) {
freq_range = vis_max_freq - vis_min_freq
freq_unit = freq_range / vis_bars
bar_space = demo_xsize / vis_bars
i = 0 while i < vis_bars {
i_freq = ( freq_unit * i ) + vis_min_freq
k = ( 0.5 + ( ( vis_size * i_freq ) / demo_sound_rate ) )
real_w = 2 * cos( 2 * M_PI * k / vis_size )
imag_w = sin( 2 * M_PI * k / vis_size )
d1 = 0
d2 = 0
j = 0 while j < vis_size {
j = j + 1
v = demo_sound_pcm_stream[ ( demo_sound_pcm_stream_ptr + j ) * demo_sound_channels ] / 256
win = sin( M_PI * j / vis_size )
x = v * win
y = ( x * vis_size ) + real_w * d1 - d2
d2 = d1
d1 = y
}
result_r = 0.5 * real_w * d1 - d2
result_i = imag_w * d1
amp = result_r * result_r + result_i * result_i
amp = sqrt( amp )
old_amp = goertzel_values[ i ]
goertzel_values[ i ] = old_amp + ( amp - old_amp ) * ( 1 / vis_smooth )
amp_scaled = ( goertzel_values[ i ] / 500 ) * vis_scale
goertzel_values[ i ] = amp_scaled
i = i + 1
}
i = 0 while i < demo_xsize {
bin = ( i / demo_xsize ) * vis_bars
idx = floor( bin )
frac = bin - idx
amp = ( 1 - frac ) * goertzel_values[ idx ] + frac * goertzel_values[ idx + 1 ]
i_adjusted = i - ( demo_xsize / 2 )
line( i_adjusted, 0, i_adjusted, amp, vis_color )
line( i_adjusted, 0, i_adjusted, -amp, vis_color )
i = i + 1
}
} else {
freq_range = vis_max_freq - vis_min_freq
freq_unit = freq_range / vis_bars
bar_space = demo_xsize / vis_bars
i = 0 while i < vis_bars {
i_freq = ( freq_unit * i ) + vis_min_freq
k = ( 0.5 + ( ( vis_size * i_freq ) / demo_sound_rate ) )
real_w = 2 * cos( 2 * M_PI * k / vis_size )
imag_w = sin( 2 * M_PI * k / vis_size )
d1 = 0
d2 = 0
j = 0 while j < vis_size {
j = j + 1
v = demo_sound_pcm_stream[ ( demo_sound_pcm_stream_ptr + j ) * demo_sound_channels ] / 256
win = sin( M_PI * j / vis_size )
x = v * win
y = ( x * vis_size ) + real_w * d1 - d2
d2 = d1
d1 = y
}
result_r = 0.5 * real_w * d1 - d2
result_i = imag_w * d1
amp = result_r * result_r + result_i * result_i
amp = sqrt( amp )
old_amp = goertzel_values[ i ]
goertzel_values[ i ] = old_amp + ( amp - old_amp ) * ( 1 / vis_smooth )
amp_scaled = ( goertzel_values[ i ] / 500 ) * vis_scale
fbox( ( -demo_xsize / 2 ) + ( bar_space * i ), -amp_scaled, bar_space * vis_bar_width, amp_scaled, vis_color )
fbox( ( -demo_xsize / 2 ) + ( bar_space * i ), 0, bar_space * vis_bar_width, amp_scaled, vis_color )
i = i + 1
}
}
} else {
if (vis_int) {
pitch_range = vis_max_pitch - vis_min_pitch
pitch_unit = pitch_range / vis_bars
bar_space = demo_xsize / vis_bars
i = 0 while i < vis_bars {
i_pitch = ( pitch_unit * i ) + vis_min_pitch
i_freq = pow( 2, i_pitch / 12) * 8.175799
k = ( 0.5 + ( ( vis_size * i_freq ) / demo_sound_rate ) )
real_w = 2 * cos( 2 * M_PI * k / vis_size )
imag_w = sin( 2 * M_PI * k / vis_size )
d1 = 0
d2 = 0
j = 0 while j < vis_size {
j = j + 1
v = demo_sound_pcm_stream[ ( demo_sound_pcm_stream_ptr + j ) * demo_sound_channels ] / 256
win = sin( M_PI * j / vis_size )
x = v * win
y = ( x * vis_size ) + real_w * d1 - d2
d2 = d1
d1 = y
}
result_r = 0.5 * real_w * d1 - d2
result_i = imag_w * d1
amp = result_r * result_r + result_i * result_i
amp = sqrt( amp )
old_amp = goertzel_values[ i ]
goertzel_values[ i ] = old_amp + ( amp - old_amp ) * ( 1 / vis_smooth )
amp_scaled = ( goertzel_values[ i ] / 500 ) * vis_scale
goertzel_values[ i ] = amp_scaled
i = i + 1
}
i = 0 while i < demo_xsize {
bin = ( i / demo_xsize ) * vis_bars
idx = floor( bin )
frac = bin - idx
amp = ( 1 - frac ) * goertzel_values[ idx ] + frac * goertzel_values[ idx + 1 ]
i_adjusted = i - ( demo_xsize / 2 )
line( i_adjusted, 0, i_adjusted, amp, vis_color )
line( i_adjusted, 0, i_adjusted, -amp, vis_color )
i = i + 1
}
} else {
pitch_range = vis_max_pitch - vis_min_pitch
pitch_unit = pitch_range / vis_bars
bar_space = demo_xsize / vis_bars
i = 0 while i < vis_bars {
i_pitch = ( pitch_unit * i ) + vis_min_pitch
i_freq = pow( 2, i_pitch / 12) * 8.175799
k = ( 0.5 + ( ( vis_size * i_freq ) / demo_sound_rate ) )
real_w = 2 * cos( 2 * M_PI * k / vis_size )
imag_w = sin( 2 * M_PI * k / vis_size )
d1 = 0
d2 = 0
j = 0 while j < vis_size {
j = j + 1
v = demo_sound_pcm_stream[ ( demo_sound_pcm_stream_ptr + j ) * demo_sound_channels ] / 256
win = sin( M_PI * j / vis_size )
x = v * win
y = ( x * vis_size ) + real_w * d1 - d2
d2 = d1
d1 = y
}
result_r = 0.5 * real_w * d1 - d2
result_i = imag_w * d1
amp = result_r * result_r + result_i * result_i
amp = sqrt( amp )
old_amp = goertzel_values[ i ]
goertzel_values[ i ] = old_amp + ( amp - old_amp ) * ( 1 / vis_smooth )
amp_scaled = ( goertzel_values[ i ] / 500 ) * vis_scale
fbox( ( -demo_xsize / 2 ) + ( bar_space * i ), -amp_scaled, bar_space * vis_bar_width, amp_scaled, vis_color )
fbox( ( -demo_xsize / 2 ) + ( bar_space * i ), 0, bar_space * vis_bar_width, amp_scaled, vis_color )
i = i + 1
}
}
}
transp( 255 )
}
fn bg_render($t1) {
transp( 255 )
base = 255 * cover_brightness
sample = ( abs( demo_sound_pcm_stream[ demo_sound_pcm_stream_ptr * demo_sound_channels ] ) + 0.5 )
old_flash = flash
flash = base * sample
flash = old_flash * ( 1 - cover_flash_speed ) + flash * cover_flash_speed
shake_x_func = sin( 0.6 * ( $t1 / ( 1000 * cover_shake_speed ) ) ) + sin( 2.65 * ( $t1 / ( 1000 * cover_shake_speed ) ) ) + sin( 3.7 * ( $t1 / ( 1000 * cover_shake_speed ) ) ) * cover_shake_amp
shake_y_func = sin( 0.4 * ( $t1 / ( 1000 * cover_shake_speed ) ) ) + sin( 1.45 * ( $t1 / ( 1000 * cover_shake_speed ) ) ) + sin( 4.5 * ( $t1 / ( 1000 * cover_shake_speed ) ) ) * cover_shake_amp
pixi( cover, shake_x_func , shake_y_func , get_color( flash, flash, flash ), ( ( demo_xsize / cover_xsize ) ) * ( 1 + ( cover_shake_amp / 30 ) ) , ( ( demo_xsize / cover_xsize ) ) * ( 1 + ( cover_shake_amp / 30 ) ) )
transp( 255 )
}
fn logos_render($t1) {
transp( 255 )
y_pos = smooth_staircase( ( ( $t1 / ( all_enter / 3 ) ) - 1 ), 1, 15, 1, -0.5 ) * ( all_enter / 3 )
pixi(artist, 0, -y_pos, WHITE, logo_scale, logo_scale)
pixi(logo, 0, -y_pos + ( all_enter / 3 ), WHITE, logo_scale, logo_scale)
transp( 255 )
}
fn logo_render($t1) {
transp( 255 )
rel_time = 1 - clip( max( $t1 - ( all_enter + 2000 ) , 0 ) / 1000 )
enter_func = ( pow( 50, rel_time ) - 1 ) / ( 50 - 1 )
enter_func_adjusted = ( 1 - enter_func ) * ( demo_ysize / 2 )
if (logo_xsize > logo_ysize) {
logo_adjust_factor = demo_xsize / logo_ysize
x_pos = corner_x - ( ( logo_ysize * logo_adjust_factor * logo_scale_persistent ) / 2 ) - demo_xsize / 50
y_pos = corner_y - ( ( logo_ysize * logo_adjust_factor *logo_scale_persistent ) / 2 ) + demo_xsize / 50 + ( ( demo_ysize / 2 ) - enter_func_adjusted )
x_scale = logo_adjust_factor * logo_scale_persistent
y_scale = logo_adjust_factor * logo_scale_persistent
pixi( logo, x_pos , y_pos, WHITE, x_scale, y_scale, ( ( logo_xsize - logo_ysize ) / 2 ), 0, logo_ysize, logo_ysize )
} else {
logo_adjust_factor = demo_xsize / logo_xsize
x_pos = -corner_x - ( ( logo_xsize * logo_adjust_factor * logo_scale_persistent ) / 2 ) - demo_xsize / 50
y_pos = -corner_y - ( ( logo_xsize * logo_adjust_factor * logo_scale_persistent ) / 2 ) - demo_xsize / 50 + ( ( demo_ysize / 2 ) - enter_func_adjusted )
x_scale = logo_adjust_factor * logo_scale_persistent
y_scale = logo_adjust_factor * logo_scale_persistent
pixi( logo, x_pos , y_pos, WHITE, x_scale, y_scale, ( ( logo_ysize - logo_xsize ) / 2 ), 0, logo_xsize, logo_xsize )
}
transp( 255 )
}
fn cover_render( $t1 ) {
transp( 255 )
rel_time = 1 - clip( max( $t1 - ( all_enter + 1000 ) , 0 ) / 1000 )
enter_func = ( pow( 50, rel_time ) - 1 ) / ( 50 - 1 )
enter_func_adjusted = ( 1 - enter_func ) * ( demo_ysize / 2 )
if (cover_xsize > cover_ysize) {
cover_adjust_factor = demo_xsize / cover_ysize
x_pos = -corner_x - ( ( cover_ysize * cover_adjust_factor * cover_scale ) / 2 ) - demo_xsize / 50
y_pos = corner_y + ( ( cover_ysize * cover_adjust_factor * cover_scale ) / 2 ) + demo_xsize / 50 - ( ( demo_ysize / 2 ) - enter_func_adjusted )
x_scale = cover_adjust_factor * cover_scale
y_scale = cover_adjust_factor * cover_scale
pixi( cover_src, x_pos , y_pos, WHITE, x_scale, y_scale, ( ( cover_xsize - cover_ysize ) / 2 ), 0, cover_ysize, cover_ysize )
} else {
cover_adjust_factor = demo_xsize / cover_xsize
x_pos = -corner_x - ( ( cover_xsize * cover_adjust_factor * cover_scale ) / 2 ) - demo_xsize / 50
y_pos = corner_y + ( ( cover_xsize * cover_adjust_factor * cover_scale ) / 2 ) + demo_xsize / 50 - ( ( demo_ysize / 2 ) - enter_func_adjusted )
x_scale = cover_adjust_factor * cover_scale
y_scale = cover_adjust_factor * cover_scale
pixi( cover_src, x_pos , y_pos, WHITE, x_scale, y_scale, ( ( cover_ysize - cover_xsize ) / 2 ), 0, cover_xsize, cover_xsize )
}
transp( 255 )
}
fn black( $t1 ) {
transp( 255 )
exit_point = demo_length - all_exit
rel_time = max($t1, exit_point) - exit_point
speed = 255 / (all_exit)
trans = min(255 - (rel_time * speed), 255)
transp(255 - trans)
fbox(corner_x, corner_y, demo_xsize, demo_ysize, BLACK)
transp( 255 )
}
fn text_render($t1) {
transp( 255 )
rel_time = 1 - clip( max( $t1 - ( all_enter + 1000 ) , 0 ) / 1000 )
enter_func = ( pow( 50, rel_time ) - 1 ) / ( 50 - 1 )
enter_func_adjusted = ( 1 - enter_func ) * ( demo_ysize / 2 )
if (cover_xsize > cover_ysize) {
cover_adjust_factor = demo_xsize / cover_ysize
x_pos = -corner_x - ( ( cover_ysize * cover_adjust_factor * cover_scale ) ) - demo_xsize / 25
y_pos = corner_y + ( ( cover_ysize * cover_adjust_factor * cover_scale ) / 2 ) + demo_xsize / 50 - ( ( demo_ysize / 2 ) - enter_func_adjusted )
x_scale = cover_adjust_factor * cover_scale
y_scale = cover_adjust_factor * cover_scale
print( text_title, x_pos , y_pos - ( ( ( cover_ysize * cover_adjust_factor * cover_scale ) / 2 ) * text_scale ) , text_color , TOP | RIGHT )
print( text_artist, x_pos , y_pos , text_color , TOP | RIGHT )
print( text_release, x_pos , y_pos + ( ( ( cover_ysize * cover_adjust_factor * cover_scale ) / 2 ) * text_scale ) , text_color , TOP | RIGHT )
} else {
cover_adjust_factor = demo_xsize / cover_xsize
x_pos = -corner_x - ( ( cover_xsize * cover_adjust_factor * cover_scale ) ) - demo_xsize / 25
y_pos = corner_y + ( ( cover_xsize * cover_adjust_factor * cover_scale ) / 2 ) + demo_xsize / 50 - ( ( demo_ysize / 2 ) - enter_func_adjusted )
x_scale = cover_adjust_factor * cover_scale
y_scale = cover_adjust_factor * cover_scale
print( text_title, x_pos , y_pos - ( ( ( cover_ysize * cover_adjust_factor * cover_scale ) / 2 ) * text_scale ) , text_color , TOP | RIGHT )
print( text_artist, x_pos , y_pos , text_color , TOP | RIGHT )
print( text_release, x_pos , y_pos + ( ( ( cover_ysize * cover_adjust_factor * cover_scale ) / 2 ) * text_scale ) , text_color , TOP | RIGHT )
}
transp( 255 )
}
fn screen_blur( $t1 ) {
if (demo_opengl) {
$prev_screen = get_screen()
if temp_img <= 0 { temp_img = new( demo_xsize, demo_ysize, PIXEL ) } else { resize( temp_img, demo_xsize, demo_ysize ) }
copy( temp_img, $prev_screen )
set_screen( temp_img )
effector( EFF_VBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_HBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_VBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_HBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_VBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_HBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
set_screen( $prev_screen )
update_gl_data( temp_img )
pixi( temp_img )
} else {
effector( EFF_VBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_HBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_VBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_HBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_VBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
effector( EFF_HBLUR, ( 1 - ( clip( ( $t1 - all_enter ) / 1000 ) ) ) * 20 , WHITE, corner_x, corner_y, demo_xsize, demo_ysize )
}
}
fn beginning_flash_render( $t1 ) {
transp_time = max( 0 , $t1 - ( 1000 * beginning_flash_speed ) )
transp_value = 255 - ( 255 * ( transp_time / ( 1000 * beginning_flash_fade ) ) )
transp_value_curved = pow( transp_value / 255 , beginning_flash_curve ) * 255
transp( transp_value_curved )
c = 255 * ( $t1 / ( 1000 * beginning_flash_speed ) )
fbox(corner_x, corner_y, demo_xsize, demo_ysize, get_color( c , c , c ))
transp( 255 )
}
fn custom_init()
{
gauss( cover_src, cover, cover_blur_rad )
demo_length = (demo_sound_len / demo_sound_rate) * 1000
}
fn max($a, $b) {
if ($a >= $b) {
ret($a)
} else {
ret($b)
}
}
fn min($a, $b) {
if ($a < $b) {
ret($a)
} else {
ret($b)
}
}
fn clip( $a ) {
if ($a < 0) { ret( 0 ) }
if ($a >= 0) { ret( $a ) }
if ($a >= 1) { ret( 1 ) }
}
fn gauss( $src, $dest, $rad ) {
sigma = max(($rad / 2), 1)
kernel_width = (2 * $rad) + 1
kernel = new(kernel_width, kernel_width, FLOAT)
sum = 0
for (x = -$rad ; x <= $rad ; x + 1) {
for (y = -$rad ; y <= $rad ; y + 1) {
exp_num = -(x * x + y * y)
exp_dem = 2 * sigma * sigma
expression = pow(M_E, exp_num / exp_dem)
ker_val = (expression / (2 * M_PI * sigma * sigma))
kernel[(x + $rad), (y + $rad)] = ker_val
sum = sum + ker_val
}
}
FIXED_POINT_MUL = 32768
for (x = 0 ; x < kernel_width ; x + 1) {
for (y = 0 ; y < kernel_width ; y + 1) {
kernel[x, y] = kernel[x, y] / sum * FIXED_POINT_MUL
}
}
convert_type( kernel, INT )
conv_filter( $dest, $src, kernel, FIXED_POINT_MUL, 0, CONV_FILTER_COLOR )
}
fn smooth_staircase( $x, $h, $a, $w, $o ) {
part_1 = ( $a * ( $x - $o ) ) / ( $w )
part_2 = ( $a * floor( ( $x - $o ) / $w ) )
part_3 = ( $a / 2 )
part_4 = tanh( part_1 - part_2 - part_3 )
part_5 = ( 2 * tanh( $a / 2 ) )
part_6 = ( part_4 / part_5 )
whole = ( $h * ( part_6 + ( 1 / 2 ) + floor( ( $x - $o ) / $w ) ) )
ret( whole )
}
show_memory_debug_messages( 1 )

1037
demo.pixi

File diff suppressed because it is too large Load Diff

109
ffmpeg_video_export.pixi

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
//
// ffmpeg/avconv-based video export functions
// (experimental)
//
fn ffmpeg_video_export_thread( $thread_id, $ff )
{
if $ff < 0 { ret( 0 ) }
$ts = ""
$audio = ""
$audio2 = ""
if $ff.audio_file_name > 0
{
sprintf( $audio, "-i \"%s\"", $ff.audio_file_name )
$audio2 = "-acodec pcm_s16le"
}
$vcodec = ""
if $ff.kbits == 0
{
sprintf( $vcodec, "-vcodec libx264 -b:v 2000k -preset fast" )
}
else
{
sprintf( $vcodec, "-vcodec libx264 -b:v %uk -preset slow", $ff.kbits )
}
sprintf(
$ts,
"%s %s -y -pix_fmt bgr32 -s %dx%d -f rawvideo -r %d -i \"%s\" %s %s \"%s\"",
g_avconv,
$audio,
$ff.xsize,
$ff.ysize,
$ff.fps,
$ff.pipe_name,
$vcodec,
$audio2,
$ff.name )
printf( "FFmpeg (output) command: %s\n", $ts )
system( $ts )
}
fn ffmpeg_video_export_open( $filename, $xsize, $ysize, $fps, $kbits, $max_frames, $audio_file_name )
{
g_avconv = -1
while 1
{
if system( "ffmpeg -version" ) == 0 { g_avconv = "ffmpeg" break }
if system( "avconv -version" ) == 0 { g_avconv = "avconv" break }
break
}
if g_avconv == -1
{
logf( "No AV converters detected. Please install ffmpeg or avconv!\n" )
logf( "Please also note that this library is designed for *nix systems (Linux, macOS, BSD, etc.) only.\n" )
ret( -1 )
}
$ff = new( 1, 1, INT )
$ff.xsize = $xsize
$ff.ysize = $ysize
$ff.fps = $fps
$ff.kbits = $kbits
$ff.name = $filename
$ff.pipe_name = new( 1, 1, INT8 )
sprintf( $ff.pipe_name, "/tmp/avconv_vpipe_%d", $ff )
$ff.frame = 0
$ff.max_frames = $max_frames
if $0 >= 7
{
$ff.audio_file_name = $audio_file_name
}
$ts = ""
sprintf( $ts, "mkfifo %s", $ff.pipe_name )
system( $ts )
$ff.th = thread_create( ffmpeg_video_export_thread, $ff )
ret( $ff )
}
fn ffmpeg_video_export_close( $ff )
{
if $ff < 0 { ret( 0 ) }
if $ff.pipe_f
{
fclose( $ff.pipe_f )
}
thread_destroy( $ff.th, INT_MAX )
remove_file( $ff.pipe_name )
remove( $ff.pipe_name )
remove( $ff )
}
fn ffmpeg_video_export_write( $ff, $buf )
{
if $ff < 0 { ret( 0 ) }
if $ff.pipe_f == 0 { $ff.pipe_f = fopen( $ff.pipe_name, "wb" ) }
if $ff.pipe_f
{
fwrite( $buf, get_size( $buf ) * get_esize( $buf ), $ff.pipe_f )
$ff.frame + 1
if $ff.max_frames && $ff.frame >= $ff.max_frames
{
fputs( "FFmpeg export stop\n" )
ret( 1 )
}
}
ret( 0 )
}

391
mjpeg.pixi

@ -0,0 +1,391 @@ @@ -0,0 +1,391 @@
//
// AVI MJPEG encoder
//
include "riff.pixi"
AVIH_FLAG_HASINDEX = 0x00000010 //Index at end of file; NOT RECOGNIZED BY MPLAYER & VLC :(
AVIH_FLAG_MUSTUSEINDEX = 0x00000020
AVIH_FLAG_ISINTERLEAVED = 0x00000100
AVIH_FLAG_TRUSTCKTYPE = 0x00000800 //Use CKType to find key frames
AVIH_FLAG_WASCAPTUREFILE = 0x00010000
AVIH_FLAG_COPYRIGHTED = 0x00020000
MJPEG_ENCODER_FLAG_HASSOUND = ( 1 << 0 )
MJPEG_ENCODER_FLAG_USEINDEX = ( 1 << 1 )
fn mjpeg_fn_error( $fn_name, $pars )
{
logf( "%s() MJPEG error: wrong number of parameters (%d)\n", $fn_name, $pars )
}
fn mjpeg_encoder_open(
$fps,
$width,
$height,
$quality,
$audio_channels,
$audio_frames_per_second,
$audio_sample_type,
$flags,
$output_stream )
{
if $0 != 9 { mjpeg_fn_error( "mjpeg_encoder_open", $0 ) ret( -1 ) }
$mj = new( 1, 1, INT )
$mj.fps = $fps
$mj.frames = 0
$mj.width = $width
$mj.height = $height
$mj.quality = $quality
$mj.audio_ch = $audio_channels
$mj.audio_freq = $audio_frames_per_second
$mj.audio_type = $audio_sample_type
$mj.flags = $flags
$mj.out = $output_stream
$mj.idx = new( 4, 1, INT32 )
$mj.idx_ptr = 0
$mj.jpeg = -1
$mj.silent_chunk = -1
$mj.audio_bits = 0
if $audio_sample_type == INT8 { $mj.audio_bits = 8 }
if $audio_sample_type == INT16 { $mj.audio_bits = 16 }
if $audio_sample_type == FLOAT32 { $mj.audio_bits = 32 }
if $mj.audio_bits == 0
{
logf( "MJPEG encoder error: wrong audio sample type. Possible values: INT8, INT16, FLOAT32\n" )
ret( -1 )
}
//AVI:
$mj.c_avi = riff_chunk_open( -1, "RIFF", $mj.out )
riff_chunk_write_data( $mj.c_avi, "AVI ", 0, 0 )
//Header:
$c_hdrl = riff_chunk_open( $mj.c_avi, "LIST", $mj.out )
riff_chunk_write_data( $c_hdrl, "hdrl", 0, 0 )
$c = riff_chunk_open( $c_hdrl, "avih", $mj.out )
$avih = new( 14, 1, INT32 )
clean( $avih )
$avih[ 0 ] = 1000000 div $mj.fps //microSecPerFrame
$avih[ 1 ] = 10000000 //maxBytesPerSec
$avih[ 3 ] = AVIH_FLAG_ISINTERLEAVED | AVIH_FLAG_TRUSTCKTYPE | AVIH_FLAG_WASCAPTUREFILE //flags
if $flags & MJPEG_ENCODER_FLAG_USEINDEX
{
$avih[ 3 ] | AVIH_FLAG_HASINDEX
}
$avih[ 4 ] = 0 //totalFrames
$mj.fix1_offset = ftell( $mj.out ) + 4 * 4
if $flags & MJPEG_ENCODER_FLAG_HASSOUND { $avih[ 6 ] = 2 } else { $avih[ 6 ] = 1 }
$avih[ 7 ] = 1024 * 1024 //suggestedBufferSize
$avih[ 8 ] = $mj.width
$avih[ 9 ] = $mj.height
riff_chunk_write_data( $c, $avih, 0, 0 )
riff_chunk_close( $c )
remove( $avih )
//Video stream info:
$c_strl = riff_chunk_open( $c_hdrl, "LIST", $mj.out )
riff_chunk_write_data( $c_strl, "strl", 0, 0 )
$c = riff_chunk_open( $c_strl, "strh", $mj.out )
$strh = new( 14, 1, INT32 )
clean( $strh )
$strh[ 0 ] = 'vids' //type
$strh[ 1 ] = 'MJPG' //handler
$strh[ 5 ] = 1 //scale
$strh[ 6 ] = $fps //rate
$strh[ 8 ] = 0 //length
$mj.fix2_offset = ftell( $mj.out ) + 8 * 4
$strh[ 9 ] = 1024 * 1024 //suggestedBufferSize
$strh[ 10 ] = -1 //quality
$strh[ 13 ] = $width | ( $height << 16 )
riff_chunk_write_data( $c, $strh, 0, 0 )
riff_chunk_close( $c )
remove( $strh )
$c = riff_chunk_open( $c_strl, "strf", $mj.out )
$bmph = new( 10, 1, INT32 )
clean( $bmph )
$bmph[ 0 ] = get_size( $bmph ) * get_esize( $bmph ) //size
$bmph[ 1 ] = $width
$bmph[ 2 ] = $height
$bmph[ 3 ] = 1 | ( 24 << 16 ) //planes and bitCount
$bmph[ 4 ] = 'MJPG' //compression
$bmph[ 5 ] = $width * $height * 3 //imgSize
riff_chunk_write_data( $c, $bmph, 0, 0 )
riff_chunk_close( $c )
remove( $bmph )
riff_chunk_close( $c_strl )
if $flags & MJPEG_ENCODER_FLAG_HASSOUND
{
//Audio stream info (uncompressed interleaved):
$c_strl = riff_chunk_open( $c_hdrl, "LIST", $mj.out )
riff_chunk_write_data( $c_strl, "strl", 0, 0 )
$c = riff_chunk_open( $c_strl, "strh", $mj.out )
$strh = new( 14, 1, INT32 )
clean( $strh )
$strh[ 0 ] = 'auds' //type
$strh[ 1 ] = 0 //handler
$strh[ 5 ] = 1 //scale
$strh[ 6 ] = $audio_frames_per_second //rate
$strh[ 8 ] = 0 //length
$mj.fix3_offset = ftell( $mj.out ) + 8 * 4
$strh[ 9 ] = 0 //suggestedBufferSize
$strh[ 10 ] = -1 //quality
$strh[ 11 ] = $audio_channels * ( $mj.audio_bits / 8 ) //sampleSize
riff_chunk_write_data( $c, $strh, 0, 0 )
riff_chunk_close( $c )
remove( $strh )
$c = riff_chunk_open( $c_strl, "strf", $mj.out )
$fmt = new( 4, 1, INT32 )
clean( $fmt )
$audio_format = 1; if $mj.audio_bits == 32 { $audio_format = 3 }
$fmt[ 0 ] = $audio_format | ( $audio_channels << 16 )
$fmt[ 1 ] = $audio_frames_per_second
$fmt[ 2 ] = $audio_frames_per_second * $audio_channels * ( $mj.audio_bits / 8 ) //bytes per second
$fmt[ 3 ] = $audio_channels * ( $mj.audio_bits / 8 ) | ( $mj.audio_bits << 16 ) //block align + bits
riff_chunk_write_data( $c, $fmt, 0, 0 )
riff_chunk_close( $c )
remove( $fmt )
riff_chunk_close( $c_strl )
}
riff_chunk_close( $c_hdrl )
//Video and Audio data:
$mj.c_movi = riff_chunk_open( $mj.c_avi, "LIST", $mj.out )
riff_chunk_write_data( $mj.c_movi, "movi", 0, 0 )
ret( $mj )
}
/*fn mjpeg_encoder_jfif_to_avi1( $jpeg )
{
if $0 != 1 { mjpeg_fn_error( "mjpeg_encoder_jfif_to_avi1", $0 ) ret( 0 ) }
$p = -1
$i = 0 while $i < 256
{
if $jpeg[ $i ] == 'J'
{
if $jpeg[ $i + 1 ] == 'F'
{
if $jpeg[ $i + 2 ] == 'I'
{
if $jpeg[ $i + 3 ] == 'F'
{
$p = $i
break
}
}
}
}
$i + 1
}
if $p >= 0
{
$size = get_size( $jpeg )
$jpeg[ $p ] = 'A' $p + 1
$jpeg[ $p ] = 'V' $p + 1
$jpeg[ $p ] = 'I' $p + 1
$jpeg[ $p ] = '1' $p + 1
$jpeg[ $p ] = 0 $p + 1
$jpeg[ $p ] = 0 $p + 1
$jpeg[ $p ] = $size & 255 $p + 1
$jpeg[ $p ] = ( $size >> 8 ) & 255 $p + 1
$jpeg[ $p ] = ( $size >> 16 ) & 255 $p + 1
$jpeg[ $p ] = ( $size >> 24 ) & 255 $p + 1
$jpeg[ $p ] = $size & 255 $p + 1
$jpeg[ $p ] = ( $size >> 8 ) & 255 $p + 1
$jpeg[ $p ] = ( $size >> 16 ) & 255 $p + 1
$jpeg[ $p ] = ( $size >> 24 ) & 255 $p + 1
}
ret( 0 )
}*/
fn mjpeg_encoder_write_jpeg( $mj, $jpeg, $offset_bytes, $size_bytes )
{
if $0 != 4 { mjpeg_fn_error( "mjpeg_encoder_write_jpeg", $0 ) ret( 0 ) }
if $mj <= 0 { ret( 0 ) }
if $mj.flags & MJPEG_ENCODER_FLAG_USEINDEX
{
mjpeg_encoder_add_index( $mj, '00dc', 0x10, ftell( $mj.out ) - ( $mj.c_movi.size_ptr + 4 ), $size_bytes )
}
$c = riff_chunk_open( $mj.c_movi, "00dc", $mj.out )
riff_chunk_write_data( $c, $jpeg, $offset_bytes, $size_bytes )
riff_chunk_close( $c )
ret( $size_bytes )
}
fn mjpeg_encoder_write_image( $mj, $img )
{
if $0 != 2 { mjpeg_fn_error( "mjpeg_encoder_write_image", $0 ) ret( 0 ) }
if $mj <= 0 { ret( 0 ) }
if $mj.jpeg <= 0
{
$mj.jpeg = new( 1, 1, INT8 )
}
$jpeg = $mj.jpeg
if $img != -999
{
$f = fopen_mem( $jpeg )
fsave( $img, $f, FORMAT_JPEG, $mj.quality )
fclose( $f )
}
if $mj.flags & MJPEG_ENCODER_FLAG_USEINDEX
{
mjpeg_encoder_add_index( $mj, '00dc', 0x10, ftell( $mj.out ) - ( $mj.c_movi.size_ptr + 4 ), get_size( $jpeg ) )
}
$c = riff_chunk_open( $mj.c_movi, "00dc", $mj.out )
riff_chunk_write_data( $c, $jpeg, 0, 0 )
riff_chunk_close( $c )
ret( get_size( $jpeg ) )
}
fn mjpeg_encoder_get_audio_size( $mj ) //Number of audio frames per chunk
{
if $0 != 1 { mjpeg_fn_error( "mjpeg_encoder_get_audio_size", $0 ) ret( 0 ) }
if $mj <= 0 { ret( 0 ) }
$len1 = ( ( $mj.frames - 1 ) * $mj.audio_freq ) div $mj.fps
$len2 = ( $mj.frames * $mj.audio_freq ) div $mj.fps
$len = $len2 - $len1
ret( $len )
}
fn mjpeg_encoder_write_audio( $mj, $audio, $offset_bytes, $size_bytes )
{
if $0 != 4 { mjpeg_fn_error( "mjpeg_encoder_write_audio", $0 ) ret( 0 ) }
if $mj <= 0 { ret( 0 ) }
$bytes = mjpeg_encoder_get_audio_size( $mj ) * $mj.audio_ch * ( $mj.audio_bits div 8 )
if $mj.c_01wb <= 0
{
if $mj.flags & MJPEG_ENCODER_FLAG_USEINDEX
{
mjpeg_encoder_add_index( $mj, '01wb', 0, ftell( $mj.out ) - ( $mj.c_movi.size_ptr + 4 ), $bytes )
}
$mj.c_01wb = riff_chunk_open( $mj.c_movi, "01wb", $mj.out )
}
if $audio <= 0
{
//Silent chunk:
if $mj.silent_chunk <= 0
{
$mj.silent_chunk = new( $bytes, 1, INT8 )
clean( $mj.silent_chunk )
}
$audio = $mj.silent_chunk
}
riff_chunk_write_data( $mj.c_01wb, $audio, $offset_bytes, $size_bytes )
ret( $size_bytes )
}
fn mjpeg_encoder_next_frame( $mj )
{
if $0 != 1 { mjpeg_fn_error( "mjpeg_encoder_next_frame", $0 ) ret( -1 ) }
if $mj <= 0 { ret( -1 ) }
if $mj.c_01wb > 0
{
//Close audio chunk:
riff_chunk_close( $mj.c_01wb )
$mj.c_01wb = -1
}
$mj.frames + 1
ret( 0 )
}
fn mjpeg_encoder_add_index( $mj, $id, $flags, $offset, $size )
{
if $0 != 5 { mjpeg_fn_error( "mjpeg_encoder_add_index", $0 ) ret( -1 ) }
if $mj <= 0 { ret( -1 ) }
$idx = $mj.idx
$idx_ptr = $mj.idx_ptr
$idx[ $idx_ptr * 4 + 0 ] = $id
$idx[ $idx_ptr * 4 + 1 ] = $flags
$idx[ $idx_ptr * 4 + 2 ] = $offset
$idx[ $idx_ptr * 4 + 3 ] = $size
$idx_ptr + 1
if $idx_ptr >= get_size( $idx ) / 4
{
resize( $idx, ( $idx_ptr + 256 ) * 4, 1, INT32 )
}
$mj.idx_ptr = $idx_ptr
ret( 0 )
}
fn mjpeg_encoder_close( $mj )
{
if $0 != 1 { mjpeg_fn_error( "mjpeg_encoder_close", $0 ) ret( -1 ) }
if $mj <= 0 { ret( -1 ) }
if $mj.frames <= 0
{
logf( "MJPEG encoder error: no frames\n" )
}
riff_chunk_close( $mj.c_movi )
//Index:
if $mj.flags & MJPEG_ENCODER_FLAG_USEINDEX
{
$idx = $mj.idx
$idx_ptr = $mj.idx_ptr
$c = riff_chunk_open( $mj.c_avi, "idx1", $mj.out )
riff_chunk_write_data( $c, $idx, 0, $idx_ptr * 4 * 4 )
riff_chunk_close( $c )
}
//Fixup:
fseek( $mj.out, $mj.fix1_offset, SEEK_SET )
riff_write_int32( $mj.frames, $mj.out ) //totalFrames
fseek( $mj.out, $mj.fix2_offset, SEEK_SET )
riff_write_int32( $mj.frames, $mj.out ) //video stream length
if $mj.flags & MJPEG_ENCODER_FLAG_HASSOUND
{
fseek( $mj.out, $mj.fix3_offset, SEEK_SET )
riff_write_int32( ( $mj.frames * $mj.audio_freq ) div $mj.fps, $mj.out ) //audio stream length (number of frames)
}
riff_chunk_close( $mj.c_avi )
remove( $mj.silent_chunk )
remove( $mj.jpeg )
remove( $mj.idx )
remove( $mj )
ret( 0 )
}

111
riff.pixi

@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
/*
Functions for working with Resource Interchange File Format (RIFF)
How to write:
c = riff_chunk_open( -1, "NAME", stream )
riff_chunk_write_data( c, data_container, 0, 0 )
riff_chunk_close( c )
How to read: not implemented yet.
*/
fn riff_write_int32( $val, $f )
{
$w = 0
while 1
{
if fputc( $val & 255, $f ) == -1 { $w = 0 break }
if fputc( ( $val >> 8 ) & 255, $f ) == -1 { $w = 0 break }
if fputc( ( $val >> 16 ) & 255, $f ) == -1 { $w = 0 break }
if fputc( ( $val >> 24 ) & 255, $f ) == -1 { $w = 0 break }
$w = 4
break
}
ret( $w )
}
fn riff_chunk_open( $parent, $id, $f )
{
$err = 0
$c = new( 1, 1, INT )
$c.parent = $parent
if $parent > 0
{
$parent.size + 8
$parent2 = $parent.parent
while 1
{
if $parent2 == -1 { break }
$parent2.size + 8
$parent2 = $parent2.parent
}
}
$c.id = $id
$c.size = 0
$c.size_ptr = 0
$c.f = $f
while 1
{
if fwrite( $id, 4, $f ) != 4 { $err = 1 break } //Save chunk ID
$c.size_ptr = ftell( $f )
if riff_write_int32( 0, $f ) != 4 { $err = 1 break } //Save chunk size (zero by default)
break
}
if $err
{
remove( $c )
$c = -1
}
ret( $c )
}
fn riff_chunk_close( $c )
{
if $c <= 0 { logf( "Close: no chunk\n" ) ret }
if $c.size
{
$f = $c.f
$prev_pos = ftell( $f )
fseek( $f, $c.size_ptr, SEEK_SET )
riff_write_int32( $c.size, $f )
fseek( $f, $prev_pos, SEEK_SET )
if $c.size & 1
{
fputc( 0, $f );
$parent = $c.parent
while 1
{
if $parent == -1 { break }
$parent.size + 1
$parent = $parent.parent
}
}
}
remove( $c )
}
fn riff_chunk_write_data( $c, $data, $offset_bytes, $size_bytes )
{
$w = 0
if $c <= 0 { logf( "Write: no chunk\n" ) ret }
if $size_bytes <= 0
{
$size_bytes = get_size( $data ) * get_esize( $data )
}
$f = $c.f
$w = fwrite( $data, $size_bytes, $f, $offset_bytes )
if $w == $size_bytes
{
$c.size + $size_bytes
$parent = $c.parent
while 1
{
if $parent == -1 { break }
$parent.size + $size_bytes
$parent = $parent.parent
}
}
ret( $w )
}
Loading…
Cancel
Save