这个 JSL 制作了一段视频以配合一些音乐。通常是相反的方式:选择音乐以匹配视频。通过首先选择音乐,并稍微限制视频的外观,我得到了一组可以与音频组合的图像(在本例中使用 Blender)以获得完美的同步。再一次,JSL 没有被清理,但是有一些评论散落在其中。
不要吃 - YouTube

wav = "44.1KHz 16bit stereo.wav"; // this is the music file
//Open( wav ); // we'll get back to the music after a bit. for now, start it playing.
// here's *some* of the parameters that were lifted out of the JSL as it was
// developed, moved up front to be easier to change
resolution = 120; // 20 makes a fast render but a less smooth appearance. 120 is slow and pretty
// this is the dimension of the mesh that creates the shape of the lumps. doesn't seem to affect speed.
dim = 30; // believe 30 is a limitation on win opengl. needs to be even number.
// the 1920*1080 video size is still inline, but this made it possible to scale it to twice the size and
// let Blender scale it back down, probably smoothing out some jaggies...anti-aliasing...
picscale = .5;//2;//
// initial rotation of the scene. Later these numbers will be changed, a little, as the images are generated.
yrot = 188; // +/- 20
xrot = 68; // +/- 20
zrot = -310; // this can rotate continuously
// make some extra images at the beginning before the audio; this audio file has no leading silence
preroll = 10;
// this was taken from the teapot example that ships with JMP, and modified a lot. Some names stuck.
// in the teapot, a patch was a 4x4 matrix of numbers. Here it is 30x30 (dim, above)
// this could be *way* simpler if you don't need it to be pretty...this is all about smoothness.
patch = Function( {dent}, // -1<=dent<=1
xpoints = J( dim, dim, 0 );
ypoints = J( dim, dim, 0 );
zpoints = J( dim, dim, 0 );
// build the smooth 3D shape
// in the 30x30 square grid there are 15 concentric (square) rings to place in the desired sheet
// for each point in the 30x30 grid, the ring number is determined by the distance to the
// edge of the row or col closest to the edge
bottom = 0; // starting here...trial and error to get a pleasing shape
top = (dim / 2) - 1; // can't explain it because it is a week later...
center = (dim + 1) / 2; // NOTE: the 30x30 x,y,z coordinates are
scale = 5; // control points...they DO NOT LIE on the surface
For( row = 1, row <= dim, row++,
For( col = 1, col <= dim, col++,
ring = Interpolate( Min( row - 1, col - 1, dim - row, dim - col ), bottom, 0.001, top, Pi() );
ypoints[row, col] = scale * Sin( ring ) * (row - center) / dim;
xpoints[row, col] = scale * Sin( ring ) * (col - center) / dim;
zpoints[row, col] = Interpolate( ring, 0, .9, // low values of ring are the folded-under corners of the
.7, 1, // sheet. .9 actually punches back up slightly from the base at 1.
1.8, -.5, // mid values of ring from .7 to 1.8 map further negative
// ring from 1.8 to PI runs from -.5 to either -2 (big bump for negative dent) or
// +1.8 (big dent for positive dent) with 0 dent adjusted to -.3 to look flat
Pi(), Interpolate( -dent, -1, -2, 0, -.3, 1, 1.8 ) // maxdented=1.8 -.3=centered bumped=-2
);
)
);
// build the combined points matrix. shape() converts the 30x30 matrix into 900x1
// and the 3 900x1 matrixes are joined into a 900x3
points = Shape( ypoints, dim * dim, 1 ) || Shape( xpoints, dim * dim, 1 ) || Shape( zpoints, dim * dim, 1 );
// the MAP2 method adds the control points. The WWW examples rarely explain the MAP2
// parameters in detail; I think I've got this mostly right, especially for a square case (xDim==yDim)
Teapot << Map2( MAP2_VERTEX_3, 0, 1, N Cols( points ), Sqrt( N Rows( points ) ),
0, 1, N Cols( points ) * Sqrt( N Rows( points ) ), Sqrt( N Rows( points ) ), points );
// the EVALMESH2 method creates polygons with normal vectors using the resolution to
// determine the number of polygons
Teapot << EvalMesh2( FILL, 0, resolution, 0, resolution );
);
// pre-build a set of objects. Call the above function, a bunch of times, with a range of values from -1 to 1
// the objects are 3D display lists that can be reused. No color is assigned yet.
nobj = 240;// creates 2N+1 discrete levels. turns out to be not important exactly how many.
obj = Associative Array(); // to hold the display list objects
For( dent = -nobj, dent <= nobj, dent += 1,
teapot = Scene Display List();
patch( dent / nobj );
obj[dent] = teapot;
);
// make the main 3D displaylist box. To make the video not have a white border line I had to
// add 1. Later you'll see where the border is removed
scene = Scene Box( picscale * 1920 + 1, picscale * 1080 + 1 );
New Window( "Teapot",
V List Box(
scene, // the sliders were used to figure out what rotations I
Slider Box( -360, 360, xrot, buildscene(), <<SETWIDTH> real + imag :*</img> imag ); // just the amplitude
time = (i - 1) * (cols / 44100); // unused?
maxa = Max( maxa, Max( ampl ) );
);
// do it again, this time make the pics using the maxs and maxa
i = 1;
zrot = zrot - preroll * .25;
{real, imag} = FFT( {x[i, 0]`} );
ampl = Sqrt( real :*</img> real + imag :*</img> imag ); // just the amplitude
amplFiltered = ampl * 0.00001;
// make preroll lead-in frames before the audio;
For( i = -preroll, i < 1, i++,
buildscene();
Wait( 0 );
pic = scene << getpicture;
pixels = pic << getpixels;
pixels = pixels[2 :: picscale * 1080 + 1, 3 :: picscale * 1920 + 2];
pic << setpixels( pixels );
pic << saveimage( "$temp\m" || Right( Char( i + preroll, 9 ), 9, "0" ) || ".png" );
amplFiltered = ampl * Interpolate( i, -preroll, 0.00001, -preroll / 5, 0.0001, 1, 1 );
);
// make the frames from the filtered FFT results. the filter allows fast rise but slow decay
For( i = 1, i <= rows, i++,
Print( i / rows ); // each for is a video frame and 1/30 second of audio
{real, imag} = FFT( {x[i, 0]`} ); // FFT the row
ampl = Sqrt( real :*</img> real + imag :*</img> imag ); // just the amplitude
time = (i - 1) * (cols / 44100); // unused
amplFiltered = If( i == 1,
ampl,
selector = Greater( ampl, amplFiltered );
selector :*</img> ampl + (1 - selector) :*</img> ((3 * amplFiltered + ampl) / 4);
);
buildscene();
Wait( 0 );
pic = scene << getpicture;
pixels = pic << getpixels;
// here's the clipping of the unwanted border pixels...
// the exact offsets may need to be rediscovered sometime if JMP or OS changes something
pixels = pixels[2 :: picscale * 1080 + 1, 3 :: picscale * 1920 + 2];
pic << setpixels( pixels );
// Blender works well with a directory of files x00001.png, x00002.png, ...
pic << saveimage( "$temp\m" || Right( Char( i + preroll, 9 ), 9, "0" ) || ".png" );
);
// dead frames at end ramp down the amplitude...
For( i, i < rows + 360, i++, // trailer
amplFiltered = amplFiltered :*</img> .95;
buildscene();
Wait( 0 );
pic = scene << getpicture;
pixels = pic << getpixels;
pixels = pixels[2 :: picscale * 1080 + 1, 3 :: picscale * 1920 + 2];
pic << setpixels( pixels );
pic << saveimage( "$temp\m" || Right( Char( i + preroll, 9 ), 9, "0" ) || ".png" );
);
,
Print( "bad file" )
);
Print( "done" );
</SETWIDTH>
19jun2019: 几年前社区变化后终于修复了JSL