acheney
1 year ago
7 changed files with 2435 additions and 0 deletions
@ -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. |
@ -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 |
@ -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 ) |
@ -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 ) |
||||
} |
@ -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 ) |
||||
} |
@ -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…
Reference in new issue