acheney
1 year ago
5 changed files with 1844 additions and 0 deletions
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2022 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,291 @@
@@ -0,0 +1,291 @@
|
||||
// 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 -- |
||||
|
||||
// use opengl (could improve performance and make everything look smoother) (0 = false, 1 = true) |
||||
demo_opengl = 1 |
||||
|
||||
// demo width in pixels |
||||
demo_xsize = 1920 |
||||
|
||||
// demo height in pixels |
||||
demo_ysize = 1080 |
||||
|
||||
// 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 |
||||
|
||||
// -- FFT/VISUALIZER CONFIG -- |
||||
|
||||
// fft size (must be a power of 2) |
||||
fft_size = 2048 |
||||
|
||||
// visualizer minimum midi pitch |
||||
vis_min_pitch = 21 |
||||
|
||||
// visualizer maximum midi pitch |
||||
vis_max_pitch = 135 |
||||
|
||||
// visualizer floor in decibels (everything below this is cut out) |
||||
vis_floor = 80 |
||||
|
||||
// visualizer smoothing factor |
||||
vis_smooth = 3 |
||||
|
||||
// visualizer horizontal scale |
||||
vis_scale = 8 |
||||
|
||||
// visualizer enter time delay in ms |
||||
vis_delay = 2000 |
||||
|
||||
// visualizer enter time in ms |
||||
vis_time = 1000 |
||||
|
||||
// -- EXIT TIME CONFIG -- |
||||
|
||||
// time for everything to fade out in ms (this divided by 3 is the amount of time for each element to fade out) |
||||
all_exit = 3000 |
||||
|
||||
// -- LOGO CONFIG -- |
||||
|
||||
// logo size scale |
||||
logo_scale = 0.25 |
||||
|
||||
// logo enter time delay in ms |
||||
logo_delay = 1000 |
||||
|
||||
// logo enter time in ms |
||||
logo_time = 1000 |
||||
|
||||
// -- ARTIST CONFIG -- |
||||
|
||||
// artist size scale |
||||
artist_scale = 0.25 |
||||
|
||||
// artist enter time in ms |
||||
artist_time = 1000 |
||||
|
||||
// artist exit time in ms |
||||
artist_exit = 1000 |
||||
|
||||
// -- COVER CONFIG -- |
||||
|
||||
// cover gaussian blur radius |
||||
cover_blur_rad = 20 |
||||
|
||||
// cover brightness scale |
||||
cover_brightness = 0.5 |
||||
|
||||
// - END CONFIG - |
||||
|
||||
demo_load_wav( "media/song.wav" ) |
||||
cover = load( "media/cover.png" ) |
||||
logo = load("media/logo.png") |
||||
artist = load("media/artist.png") |
||||
|
||||
demo_length = 0 |
||||
|
||||
fft_im = new( fft_size, 1, FLOAT ) |
||||
fft_re = new( fft_size, 1, FLOAT ) |
||||
fft_im2 = new( fft_size, 1, FLOAT ) |
||||
fft_re2 = new( fft_size, 1, FLOAT ) |
||||
fft_db = new( fft_size, 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) |
||||
|
||||
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() |
||||
demo_deinit() |
||||
|
||||
fn view( $t1, $t2, $len ) |
||||
{ |
||||
clear( #000000 ) |
||||
|
||||
cover_render($t1) |
||||
vis_render($t1) |
||||
artist_render($t1) |
||||
logo_render($t1) |
||||
|
||||
transp(255) |
||||
} |
||||
|
||||
fn vis_render($t1) |
||||
{ |
||||
rel_time = max($t1, vis_delay) - vis_delay |
||||
exit_point = demo_length - ((2 * all_exit) / 3) |
||||
rel_time_end = max($t1, exit_point) - exit_point |
||||
|
||||
speed_enter = 255 / vis_time |
||||
speed_exit = 255 / (all_exit / 3) |
||||
trans = min(255 - (rel_time_end * speed_exit), min(rel_time * speed_enter, 255)) |
||||
transp(trans) |
||||
|
||||
t_scale(vis_scale, 1, 1) |
||||
width = (vis_max_pitch - vis_min_pitch) |
||||
|
||||
t_translate(-(width / 2), ((demo_ysize / 3) - vis_floor) , 0) |
||||
i = 0 while i < fft_size { |
||||
|
||||
v = demo_sound_pcm_stream[ (demo_sound_pcm_stream_ptr + i) * demo_sound_channels ] / 256 |
||||
win = sin( M_PI * i / fft_size ) |
||||
fft_re[i] = v * win |
||||
fft_im[i] = 0 |
||||
i = i + 1 |
||||
} |
||||
|
||||
fft(0, fft_im, fft_re) |
||||
|
||||
i = 0 while i < (fft_size / 2) { |
||||
v = fft_re[ i ] * fft_re[ i ] + fft_im[ i ] * fft_im[ i ] |
||||
v = sqrt( v ) |
||||
|
||||
v = 20 * log10( v ) |
||||
|
||||
old_v = fft_db[ i ] |
||||
|
||||
fft_db[ i ] = old_v + (v - old_v) * (1 / vis_smooth) |
||||
|
||||
i = i + 1 |
||||
} |
||||
|
||||
bin_width = demo_sound_rate / fft_size |
||||
|
||||
p = vis_min_pitch |
||||
while p < vis_max_pitch { |
||||
f = pow( 2, p / 12) * 8.175799 |
||||
bin = f / bin_width |
||||
idx = floor( bin ) |
||||
frac = bin - idx |
||||
dB = ( 1 - frac ) * fft_db[ idx ] + frac * fft_db[ idx +1 ] |
||||
|
||||
if (dB >= -vis_floor) { |
||||
line( (p - vis_min_pitch), vis_floor, (p - vis_min_pitch), -dB, WHITE) |
||||
} |
||||
|
||||
p = p + (1 / vis_scale) |
||||
} |
||||
|
||||
t_reset() |
||||
|
||||
|
||||
} |
||||
|
||||
fn cover_render($t1) { |
||||
|
||||
exit_point = demo_length - all_exit |
||||
rel_time = max($t1, exit_point) - exit_point |
||||
speed = 255 / (all_exit / 3) |
||||
trans = min(255 - (rel_time * speed), 255) |
||||
transp(trans) |
||||
|
||||
base = 255 * cover_brightness |
||||
flash = base * (abs(demo_sound_pcm_stream[demo_sound_pcm_stream_ptr * demo_sound_channels]) + 1) |
||||
pixi(cover, 0, 0, get_color(flash, flash, flash), ((demo_xsize / cover_xsize)), ((demo_xsize / cover_xsize))) |
||||
|
||||
} |
||||
|
||||
fn logo_render($t1) { |
||||
|
||||
exit_point = demo_length - (all_exit / 3) |
||||
rel_time = max($t1, exit_point) - exit_point |
||||
speed = 255 / (all_exit / 3) |
||||
trans = min(255 - (rel_time * speed), 255) |
||||
transp(trans) |
||||
|
||||
rel_time = max($t1, logo_delay) - logo_delay |
||||
speed = demo_ysize / logo_time |
||||
perc = min(((rel_time * speed) / demo_ysize), 1) |
||||
perc_curved = (atan(((2 * perc) - 1)*10)/(M_PI/2)/2 + 0.5) + 0.032765 |
||||
pixi(logo, 0, ((1 - perc_curved) * demo_ysize), WHITE, logo_scale, logo_scale) |
||||
} |
||||
|
||||
fn artist_render($t1) { |
||||
|
||||
transp(255) |
||||
|
||||
rel_time = max($t1, artist_time) - artist_time |
||||
speed = demo_ysize / artist_exit |
||||
perc = min(((rel_time * speed) / demo_ysize), 1) |
||||
perc_curved = (atan(((2 * perc) - 1)*10)/(M_PI/2)/2 + 0.5) - 0.032765 |
||||
pixi(artist, 0, perc_curved * -demo_ysize, WHITE, logo_scale, logo_scale) |
||||
} |
||||
|
||||
fn custom_init() |
||||
{ |
||||
//gaussian blur |
||||
|
||||
sigma = max((cover_blur_rad / 2), 1) |
||||
|
||||
kernel_width = (2 * cover_blur_rad) + 1 |
||||
|
||||
kernel = new(kernel_width, kernel_width, FLOAT) |
||||
|
||||
sum = 0 |
||||
|
||||
for (x = -cover_blur_rad ; x <= cover_blur_rad ; x + 1) { |
||||
for (y = -cover_blur_rad ; y <= cover_blur_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 + cover_blur_rad), (y + cover_blur_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( cover, cover_src, kernel, FIXED_POINT_MUL, 0, CONV_FILTER_COLOR ) |
||||
|
||||
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) |
||||
} |
||||
} |
||||
|
||||
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 %uk -preset slow", $ff.kbits ) |
||||
} |
||||
else |
||||
{ |
||||
sprintf( $vcodec, "-vcodec libx264 -b:v 50000k -preset medium" ) |
||||
} |
||||
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 ) |
||||
} |
Loading…
Reference in new issue