Compare commits
	
		
			No commits in common. "master" and "embed-mp3" have entirely different histories.
		
	
	
		
	
		
					 34 changed files with 5115 additions and 1615 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,5 +1,3 @@ | ||||||
| zig-cache/ | zig-cache/ | ||||||
| zig-out/ |  | ||||||
| *.mp3 | *.mp3 | ||||||
| *.wav | *.wav | ||||||
| build_runner.zig |  | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								README.md
									
										
									
									
									
								
							|  | @ -20,9 +20,6 @@ glitch art "framework", ???????? language??? something? | ||||||
|     will likely be there by default) |     will likely be there by default) | ||||||
|  - the SWH plugins ( https://github.com/swh/lv2 ) |  - the SWH plugins ( https://github.com/swh/lv2 ) | ||||||
|  - the Invada Studio plugins ( https://launchpad.net/invada-studio/ ) |  - the Invada Studio plugins ( https://launchpad.net/invada-studio/ ) | ||||||
|  - abGate plugin |  | ||||||
|  - MDA plugins |  | ||||||
|  - Calf plugins |  | ||||||
| 
 | 
 | ||||||
| ```bash | ```bash | ||||||
| # build and install | # build and install | ||||||
|  | @ -51,17 +48,10 @@ $your_image_viewer blah_g1.bmp | ||||||
| using repl works via `scritcher repl scri_file.scri input_image.bmp` | using repl works via `scritcher repl scri_file.scri input_image.bmp` | ||||||
| 
 | 
 | ||||||
| you type commands as you'd write the specific scritcher commands | you type commands as you'd write the specific scritcher commands | ||||||
| (`doc/README.md`), with four repl-specific ones (semicolons do not apply): | (`doc/README.md`), with three repl-specific ones: | ||||||
|  - `push`, to push the last written command to the queue |  - `push`, to push the last written command to the queue | ||||||
|  - `save`, to write the queue to the given `scri_file.scri` file |  - `save`, to write the queue to the given `scri_file.scri` file | ||||||
|  - `list`, to print the current contents of the queue |  - `list`, to print the current contents of the queue | ||||||
|  - `quit`, to exit |  | ||||||
| 
 | 
 | ||||||
| After a non-REPL command, such as an effect, the program pointed by | this allows for quicker iteration of commands, as you can type a command, tweak | ||||||
| `SCRITCHER_RUNNER` will run as argument to the `runqs` command. By default, | its arguments, and when satisfied, `push` it, and work on the next one, etc. | ||||||
| the program run will be `ristretto` (as it is my preffered image viewer, |  | ||||||
| considering it was able to handle when some images went broke) |  | ||||||
| 
 |  | ||||||
| this allows for quicker iteration of commands, as you can type a command, see |  | ||||||
| the image changes as fast as possible, tweak its arguments, |  | ||||||
| and when satisfied, `push` it, and work on the next command, etc. |  | ||||||
|  |  | ||||||
							
								
								
									
										68
									
								
								build.zig
									
										
									
									
									
								
							
							
						
						
									
										68
									
								
								build.zig
									
										
									
									
									
								
							|  | @ -1,6 +1,7 @@ | ||||||
| const std = @import("std"); | const builds = @import("std").build; | ||||||
|  | const Builder = @import("std").build.Builder; | ||||||
| 
 | 
 | ||||||
| fn setupLinks(step: *std.Build.Step.Compile) void { | fn setupLinks(step: *builds.LibExeObjStep) void { | ||||||
|     step.linkSystemLibrary("c"); |     step.linkSystemLibrary("c"); | ||||||
| 
 | 
 | ||||||
|     step.linkSystemLibrary("lilv-0"); |     step.linkSystemLibrary("lilv-0"); | ||||||
|  | @ -10,64 +11,27 @@ fn setupLinks(step: *std.Build.Step.Compile) void { | ||||||
|     step.linkSystemLibrary("GraphicsMagickWand"); |     step.linkSystemLibrary("GraphicsMagickWand"); | ||||||
|     step.linkSystemLibrary("GraphicsMagick"); |     step.linkSystemLibrary("GraphicsMagick"); | ||||||
| 
 | 
 | ||||||
|     step.addIncludePath(.{ .path = "/usr/include/GraphicsMagick" }); |     step.addIncludeDir("/usr/include/lilv-0"); | ||||||
|     step.addIncludePath(.{ .path = "/usr/include" }); |     step.addIncludeDir("/usr/include/GraphicsMagick"); | ||||||
| 
 |  | ||||||
|     const possible_lilv_include_dirs = [_][]const u8{ |  | ||||||
|         "/usr/include/lilv-0/lilv", |  | ||||||
|         "/usr/include/lilv-0", |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     var found_any_lilv = false; |  | ||||||
| 
 |  | ||||||
|     for (possible_lilv_include_dirs) |possible_lilv_dir| { |  | ||||||
|         var possible_dir = std.fs.cwd().openDir(possible_lilv_dir, .{}) catch |err| { |  | ||||||
|             std.debug.print("possible lilv {s} fail: {s}\n", .{ possible_lilv_dir, @errorName(err) }); |  | ||||||
|             continue; |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         possible_dir.close(); |  | ||||||
|         found_any_lilv = true; |  | ||||||
| 
 |  | ||||||
|         std.debug.print("found lilv at '{s}'\n", .{possible_lilv_dir}); |  | ||||||
|         step.addIncludePath(.{ .path = possible_lilv_dir }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (!found_any_lilv) { |  | ||||||
|         std.debug.print("No LILV library was found :(\n", .{}); |  | ||||||
|         @panic("no lilv found"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn build(b: *std.Build) void { | pub fn build(b: *Builder) void { | ||||||
|     const target = b.standardTargetOptions(.{}); |     const mode = b.standardReleaseOptions(); | ||||||
|     const optimize = b.standardOptimizeOption(.{}); |     const exe = b.addExecutable("scritcher", "src/main.zig"); | ||||||
|  |     exe.setBuildMode(mode); | ||||||
|  |     exe.install(); | ||||||
| 
 | 
 | ||||||
|     const exe = b.addExecutable(.{ |  | ||||||
|         .name = "scritcher", |  | ||||||
|         .root_source_file = b.path("src/main.zig"), |  | ||||||
|         .target = target, |  | ||||||
|         .optimize = optimize, |  | ||||||
|     }); |  | ||||||
|     setupLinks(exe); |     setupLinks(exe); | ||||||
|     b.installArtifact(exe); |  | ||||||
| 
 | 
 | ||||||
|     const run_cmd = b.addRunArtifact(exe); |     const test_obj_step = b.addTest("src/main.zig"); | ||||||
|  |     setupLinks(test_obj_step); | ||||||
| 
 | 
 | ||||||
|     if (b.args) |args| { |     const run_cmd = exe.run(); | ||||||
|         run_cmd.addArgs(args); |     run_cmd.step.dependOn(b.getInstallStep()); | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     const run_step = b.step("run", "Run the app"); |     const run_step = b.step("run", "Run the app"); | ||||||
|     run_step.dependOn(&run_cmd.step); |     run_step.dependOn(&run_cmd.step); | ||||||
| 
 | 
 | ||||||
|     const test_step = b.addTest(.{ |     const test_step = b.step("test", "Run tests"); | ||||||
|         .root_source_file = b.path("src/main.zig"), |     test_step.dependOn(&test_obj_step.step); | ||||||
|         .target = target, |  | ||||||
|         .optimize = optimize, |  | ||||||
|     }); |  | ||||||
|     setupLinks(test_step); |  | ||||||
|     const run_unit_tests = b.addRunArtifact(test_step); |  | ||||||
|     const test_cmd = b.step("test", "run unit tests"); |  | ||||||
|     test_cmd.dependOn(&run_unit_tests.step); |  | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										206
									
								
								doc/README.md
									
										
									
									
									
								
							
							
						
						
									
										206
									
								
								doc/README.md
									
										
									
									
									
								
							|  | @ -23,8 +23,7 @@ where in the file you want the plugin to be ran. | ||||||
| so, if you did `plugin 3 1...`, it would split the file in 3, then get the | so, if you did `plugin 3 1...`, it would split the file in 3, then get the | ||||||
| part that is of index 1 (starting at 0). | part that is of index 1 (starting at 0). | ||||||
| 
 | 
 | ||||||
| **Keep in mind parts can start from either top or bottom of the image, | **Keep in mind parts start from the bottom of the file.** | ||||||
| it depends of the file format** |  | ||||||
| 
 | 
 | ||||||
| ## `load path_or_arg` | ## `load path_or_arg` | ||||||
| 
 | 
 | ||||||
|  | @ -167,11 +166,6 @@ Rotate the image by `deg` degrees, filling the resulting triangles with `bgfill` | ||||||
| 
 | 
 | ||||||
| `bgfill` is a hex string, e.g `#000000`. | `bgfill` is a hex string, e.g `#000000`. | ||||||
| 
 | 
 | ||||||
| ## `embed split index path_to_file` |  | ||||||
| 
 |  | ||||||
| embed an audio file in the given image. the file is opened with libsndfile, |  | ||||||
| which means some formats, like mp3, will not be opened. |  | ||||||
| 
 |  | ||||||
| ## `quicksave` | ## `quicksave` | ||||||
| 
 | 
 | ||||||
| Save the file on the same directory of the file specified by `load`, but | Save the file on the same directory of the file specified by `load`, but | ||||||
|  | @ -179,201 +173,3 @@ with a suffix on the filename (before extension). | ||||||
| 
 | 
 | ||||||
| Doing consecutive `quicksave`s will not overwrite any files, the suffixes will | Doing consecutive `quicksave`s will not overwrite any files, the suffixes will | ||||||
| just be different. | just be different. | ||||||
| 
 |  | ||||||
| ## `gate split index switch threshold attack hold decay gaterange`  |  | ||||||
| 
 |  | ||||||
| **TODO:** find good parameters |  | ||||||
| 
 |  | ||||||
|  - switch (bool): 0..1, default 0 |  | ||||||
|  - threshold (dB): -70..12, default -70 |  | ||||||
|  - attack (ms): 0.1..500, default 30 |  | ||||||
|  - hold (ms): 5..3000, default 500 |  | ||||||
|  - decay (ms): 5..4000, default 1000 |  | ||||||
|  - gaterange (dB): -90..-20, default -90 |  | ||||||
| 
 |  | ||||||
| ## `detune split index detune mix output latency` |  | ||||||
| 
 |  | ||||||
| > A low-quality stereo pitch shifter for the sort of chorus and detune effects found on multi-effects hardware. |  | ||||||
| 
 |  | ||||||
|  - detune (cents, left channel is lowered in pitch, right channel is raised): 0..1, default 0.2 |  | ||||||
|  - mix (wet/dry mix): 0..1, default 0.9 |  | ||||||
|  - output (level trim): 0..1, default 0.5 |  | ||||||
|  - latency (trade-off between latency and low-freq response): 0..1, default 0.5 |  | ||||||
| 
 |  | ||||||
| other presets: |  | ||||||
|  - stereo detune: 0.2 0.9 0.5 0.5 |  | ||||||
|  - out of tune: 0.8 0.7 0.5 0.5 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ## `overdrive split index drive muffle output` |  | ||||||
| 
 |  | ||||||
| > Possible uses include adding body to drum loops, fuzz guitar, and that 'standing outside a nightclub' sound. This plug does not simulate valve distortion, and any attempt to process organ sounds through it will be extremely unrewarding! |  | ||||||
| 
 |  | ||||||
|  - drive (amount of distortion): 0..1, default 0 |  | ||||||
|  - muffle (gentle low-pass filter): 0..1, default 0 |  | ||||||
|  - output (level trim): 0..1, default 0.5 |  | ||||||
| 
 |  | ||||||
| ## `degrade split index headroom quant rate post_filt non_lin output` |  | ||||||
| 
 |  | ||||||
| > Sample quality reduction |  | ||||||
| 
 |  | ||||||
| **NOTE:** finding the right parameters is HARD for this plugin. |  | ||||||
| 
 |  | ||||||
|  - headroom (peak clipping threshold): 0..1, default 0.8 |  | ||||||
|  - quant (bit depth, typically 8 or below for "telephone" quality): 0..1, default 0.5 |  | ||||||
|  - rate (sample rate): 0..1, default 0.65 |  | ||||||
|  - post_filt (low-pass filter to muffle the distortion): 0..1, default 0.9 |  | ||||||
|  - non_lin (additional harmonic distortion "thickening"): 0..1, default 0.58 |  | ||||||
|  - output: 0..1, default 0.5 |  | ||||||
| 
 |  | ||||||
| ## `repsycho split index tune fine decay thresh hold mix quality` |  | ||||||
| 
 |  | ||||||
| **NOTE:** HARD to find good parameters |  | ||||||
| 
 |  | ||||||
|  - tune (coarse tune, semitones): 0..1, default 1 |  | ||||||
|  - fine (fine tune, cents): 0..1, default 1 |  | ||||||
|  - decay (adjust envelope of each trunk, a fast decay can be useful while setting up): 0..1, default 0.5 |  | ||||||
|  - thresh (trigger level to divide the input into chunks): 0..1, default 0.6 |  | ||||||
|  - hold (minimum chunk length): 0..1, default 0.45 |  | ||||||
|  - mix (mix original signal with output): 0..1, default 1 |  | ||||||
|  - quality (quality, bool. the high=1 setting uses smoother pitch-shifting and allows stereo): 0..1, default 0 |  | ||||||
| 
 |  | ||||||
| ## `talkbox split index wet dry carrier quality` |  | ||||||
| 
 |  | ||||||
| > High resolution vocoder |  | ||||||
| 
 |  | ||||||
|  - wet: 0..1, default 0.5 |  | ||||||
|  - dry: 0..1, default 0 |  | ||||||
|  - carrier: 0..1, default 0 |  | ||||||
|  - quality: 0..1, default 1 |  | ||||||
| 
 |  | ||||||
| ## `dyncomp split index enable hold inputgain threshold ratio attack release gain_min gain_max rms` |  | ||||||
| 
 |  | ||||||
|  - enable (bool): 0..1, default 1 |  | ||||||
|  - hold (bool): 0..1, default 0 |  | ||||||
|  - inputgain (dB): -10..30, default 0 |  | ||||||
|  - threshold (dB): -50..-10, default -30 |  | ||||||
|  - ratio (???): 0..1, default 0 |  | ||||||
|  - attack (seconds): 0.001..0.1, default 0.01 |  | ||||||
|  - release (seconds): 0.03..3.0, default 0.3 |  | ||||||
|  - gain\_min (dB): -20..40 |  | ||||||
|  - gain\_max (dB): -20..40 |  | ||||||
|  - rms (signal level, dB): -80..10 |  | ||||||
|   |  | ||||||
| ## `thruzero split index rate mix feedback depth_mod` |  | ||||||
| 
 |  | ||||||
| > Tape flanger and ADT |  | ||||||
| 
 |  | ||||||
| > This plug simulates tape-flanging, where two copies of a signal cancel out completely as the tapes pass each other. It can also be used for other "modulated delay" effects such as phasing and simple chorusing. |  | ||||||
| 
 |  | ||||||
|  - rate (modulation rate, set to minimum for static comb filtering): 0..1, default 0.3 |  | ||||||
|  - mix (wet/dry mix, set to 50% for complete cancelling): 0..1, default 0.47 |  | ||||||
|  - feedback (add positive or negative feedback for harsher or "ringing" sound): 0..1, default 0.3 |  | ||||||
|  - depth_mod (modulation depth, set to less than 100% to limit build up of low frequencies with feedback): 0..1, default 1 |  | ||||||
| 
 |  | ||||||
| ## `foverdrive split index drive` |  | ||||||
| 
 |  | ||||||
| Fast Overdrive from SWH plugins. |  | ||||||
| 
 |  | ||||||
|  - drive: 1..3, default 1 |  | ||||||
| 
 |  | ||||||
| ## `gverb split index roomsize revtime damping drylevel earlylevel taillevel` |  | ||||||
| 
 |  | ||||||
| GVerb algorithm from SWH plugins. |  | ||||||
| 
 |  | ||||||
|  - roomsize (meters): 1..300, default 75.75 |  | ||||||
|  - revtime (reverb time, seconds): 0.1..30, default 7.575 |  | ||||||
|  - damping: 0..1, default 0.5 |  | ||||||
|  - inputbandwidth: 0..1, default 0.75 |  | ||||||
|  - drylevel (dB): -70..0, default 0 |  | ||||||
|  - earlylevel (dB): -70..0, default 0 |  | ||||||
|  - taillevel (dB): -70..0, default -17.5 |  | ||||||
| 
 |  | ||||||
| ## `invert split index` |  | ||||||
| 
 |  | ||||||
| > A utility plugin that inverts the signal, also (wrongly) known as a 180 degree phase shift. |  | ||||||
| 
 |  | ||||||
| ## `tapedelay split index speed da_db t1d t1a_db...` |  | ||||||
| 
 |  | ||||||
| **TODO:** gives 0 output |  | ||||||
| 
 |  | ||||||
| > Correctly models the tape motion and some of the smear effect, there is no simulation fo the head saturation yet, as I don't have a good model of it. When I get one I will add it. |  | ||||||
| 
 |  | ||||||
| > The way the tape accelerates and decelerates gives a nicer delay effect for many purposes. |  | ||||||
| 
 |  | ||||||
|  - speed (inches/sec, 1=normal): 0..10, default 1 |  | ||||||
|  - da\_db (dry level, dB): -90..0, default -90 |  | ||||||
|  - t1d (tap 1 distance, inches): 0..4, default 0 |  | ||||||
|  - t1a\_db (tap 1 level, dB): -90..0, default 0 |  | ||||||
|  - t2d (tap 2 distance, inches): 0..4, default 1 |  | ||||||
|  - t2a\_db (tap 2 level, dB): -90..0, default -90 |  | ||||||
|  - t3d (tap 3 distance, inches): 0..4, default 2 |  | ||||||
|  - t3a\_db (tap 3 level, dB): -90..0, default -90 |  | ||||||
|  - t4d (tap 4 distance, inches): 0..4, default 3 |  | ||||||
|  - t4a\_db (tap 4 level, dB): -90..0, default -90 |  | ||||||
| 
 |  | ||||||
| ## `moddelay split index base` |  | ||||||
| 
 |  | ||||||
| > A delay whose tap can be modulated at audio rate. |  | ||||||
| 
 |  | ||||||
|  - base (base delay, seconds): 0..1, default 1 |  | ||||||
| 
 |  | ||||||
| ## `multichorus split index min_delay mod_depth mod_rate stereo voices vphase amount dry freq freq2 q overlap level_in level_out lfo` |  | ||||||
| 
 |  | ||||||
| Calf Multi Chorus |  | ||||||
| 
 |  | ||||||
|  - `min_delay` (ms): 0.1..10, default 5 |  | ||||||
|  - `mod_depth` (ms): 0.1..10, default 6 |  | ||||||
|  - `mod_rate` (hz): 0.1..20, default 0.1 |  | ||||||
|  - `stereo` (degrees): 0..360, default 180 |  | ||||||
|  - `voices`: 1..8, default 4 |  | ||||||
|  - `vphase` (inter-voice phase, degrees): 0..360, default 64 |  | ||||||
|  - `amount`: 0..4, default 0.5 |  | ||||||
|  - `dry`: 0..4, default 0.5 |  | ||||||
|  - `freq` (center frq 1, hz): 10..20000, default 100 |  | ||||||
|  - `freq2` (center frq 2, hz): 10..20000, default 5000 |  | ||||||
|  - `q` (???): 0.125..8, default 0.125 |  | ||||||
|  - `overlap`: 0..1, default 0.75 |  | ||||||
|  - `level_in` (Input Gain): 0.0156250..64, default 1 |  | ||||||
|  - `level_out` (Output Gain): 0.0156250..64, default 1 |  | ||||||
|  - `lfo` (toggle): 0..1, default 1 |  | ||||||
| 
 |  | ||||||
| ## `saturator split index bypass level_in level_out mix drive blend lp_pre_freq hp_pre_fre lp_post_freq hp_post_freq p_freq p_level p_q pre post` |  | ||||||
| 
 |  | ||||||
|  - `bypass` (toggle): 0..1, default 0 |  | ||||||
|  - `level_in` (Input Gain): 0.0156250..64, default 1 |  | ||||||
|  - `level_out` (Output Gain): 0.0156250..64, default 1 |  | ||||||
|  - `mix`: 0..1, default 1 |  | ||||||
|  - `drive` (saturation, coef): 0.1..10, default 5 |  | ||||||
|  - `blend` (coef): -10..10, default 10 |  | ||||||
|  - `lp_pre_freq` (lowpass, hz): 10..20000, default 20000 |  | ||||||
|  - `hp_pre_freq` (highpass, hz): 10..20000, default 10 |  | ||||||
|  - `lp_post_freq` (lowpass, hz): 10..20000, default 20000 |  | ||||||
|  - `hp_post_freq` (highpass, hz): 10..20000, default 10 |  | ||||||
|  - `p_freq` (Tone, hz): 80..8000, default 2000 |  | ||||||
|  - `p_level` (Amount): 0.0625..16, default 1 |  | ||||||
|  - `p_q` (???, coef): 0.1..10, default 1 |  | ||||||
|  - `pre` (Activate Pre, toggle): 0..1, default 0 |  | ||||||
|  - `post` (Activate Post, toggle): 0..1, default 0 |  | ||||||
| 
 |  | ||||||
| ## `vintagedelay split index ...` |  | ||||||
| 
 |  | ||||||
|  - `level_in` (Input Gain): 0.0156250..64, default 1 |  | ||||||
|  - `level_out` (Output Gain): 0.0156250..64, default 1 |  | ||||||
|  - `subdiv` (int): 1..16, default 4 |  | ||||||
|  - `time_l` (int): 1..16, default 3 |  | ||||||
|  - `time_r` (int): 1..16, default 5 |  | ||||||
|  - `feedback`: 0..1, default 0.5 |  | ||||||
|  - `amount` (Wet): 0..4, default 0.25 |  | ||||||
|  - `mix_mode` (enum): Stereo=0, Ping-Pong=1, L then R=2, R then L=3, default 1 |  | ||||||
|  - `medium` (enum): Plain=0, Tape=1, Old Tape=2, default 1 |  | ||||||
|  - `dry` (dry): 0..4, default 1 |  | ||||||
|  - `width` (stereo width, strict): -1..1, default 1 |  | ||||||
|  - `fragmentation` (enum): Repeating=0, Pattern=1, default 0 |  | ||||||
|  - `pbeats` (Pattern Beats, int): 1..8, default 4 |  | ||||||
|  - `pfrag` (Pattern Fragmentation, int): 1..8, default 4 |  | ||||||
|  - `timing` (enum): BPM=0, ms=1, Hz=2, Sync=3, default 0 |  | ||||||
|  - `bpm`: 30..300, default 120 |  | ||||||
|  - `ms` (int): 10..2000, default 500 |  | ||||||
|  - `hz`: 0.01..100, default 2 |  | ||||||
|  - `bpm_host` (strict): 1..300, default 120 |  | ||||||
|  |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| load :0; |  | ||||||
| degrade 8 1 0.8 0.5 0.65 0.9 0.58 0.5; |  | ||||||
| degrade 8 2 0.1 1 0.65 0.5 0.5 0.4; |  | ||||||
| degrade 8 3 0.1 1 0.65 0.9 0.58 0.5; |  | ||||||
| degrade 8 4 0 1 1 0 0 1; |  | ||||||
| degrade 8 5 0 1 1 0 0 0; |  | ||||||
| degrade 8 6 0 0 0 0 0 0; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| detune 3 1 0.2 0.9 0.5 0.5; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| dyncomp 3 1 1 0 0 -30 0 0.01 0.3 0 0 0; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| foverdrive 3 1 100; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| gate 3 1 0 -70 30 500 1000 -90; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| gverb 3 1 75.75 7.575 0.5 0.75 0 0 -17.5; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| invert 3 1; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +1,3 @@ | ||||||
| load :0; | load :0; | ||||||
| mbeq 3 1 0 0 0 0.3 0 0 7 0 0 0 0 0 0.1 0 0; | mbeq 3 1 1 4 0.2 2.1; | ||||||
| quicksave; | quicksave; | ||||||
|  |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| moddelay 3 1 1; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| multichorus 3 1 5 6 0.1 180 4 64 0.5 0.5 100 5000 0.125 0.75 1 1 1; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| load :0; |  | ||||||
| overdrive 5 1 0.5 0 0.4; |  | ||||||
| overdrive 5 2 0.6 0.2 0.3; |  | ||||||
| overdrive 5 3 0.7 0.3 0.2; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| load :0; |  | ||||||
| repsycho 8 1 1 1 0.5 0.6 0.45 1 0; |  | ||||||
| repsycho 8 2 1 1 1 0.6 0.45 1 0; |  | ||||||
| repsycho 8 3 1 1 0.5 0.8 0.45 1 0; |  | ||||||
| repsycho 8 4 1 1 0.5 0.6 0.1 1 0; |  | ||||||
| repsycho 8 5 1 1 0.5 0.6 0.45 0.5 0; |  | ||||||
| repsycho 8 6 1 1 0.5 0.6 0.45 1 1; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| rflanger 3 1 2.5 1; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| saturator 3 1 0 1 1 1 5 10 20000 10 20000 10 2000 1 1 0 0; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| talkbox 3 1 0.5 0 0 1; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| tapedelay 3 1 1 -90 0 0 1 -90 2 -90 3 -90; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| thruzero 3 1 0.3 0.47 0.3 1; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| load :0; |  | ||||||
| vintagedelay 3 1 1 1 4 3 5 0.5 0.25 1 1 1 1 0 4 4 0 120 500 2 120; |  | ||||||
| quicksave; |  | ||||||
|  | @ -1,10 +1,11 @@ | ||||||
| const std = @import("std"); | const std = @import("std"); | ||||||
| const log = std.log.scoped(.scritcher_bmp); | 
 | ||||||
| pub const BMPValidError = error{InvalidMagic}; | pub const BMPValidError = error{InvalidMagic}; | ||||||
| 
 | 
 | ||||||
| const VALID_MAGICS = [_][]const u8{ | const VALID_MAGICS = [_][]const u8{ | ||||||
|     "BM", |     "BM", | ||||||
|     "BA", |     "BA", | ||||||
|  | 
 | ||||||
|     "CI", |     "CI", | ||||||
|     "CP", |     "CP", | ||||||
|     "IC", |     "IC", | ||||||
|  | @ -19,7 +20,7 @@ pub fn magicValid(magic: []const u8) !void { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!valid) { |     if (!valid) { | ||||||
|         log.debug("\tINVALID HEADER: '{s}'", .{magic}); |         std.debug.warn("\tINVALID HEADER: '{}'\n", magic); | ||||||
|         return BMPValidError.InvalidMagic; |         return BMPValidError.InvalidMagic; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ const lv2 = @import("lv2_helpers.zig"); | ||||||
| const plugins = @import("plugin.zig"); | const plugins = @import("plugin.zig"); | ||||||
| const image = @import("image.zig"); | const image = @import("image.zig"); | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher_custom); |  | ||||||
| const c = lv2.c; | const c = lv2.c; | ||||||
| 
 | 
 | ||||||
| const RunBuffers = plugins.RunBuffers; | const RunBuffers = plugins.RunBuffers; | ||||||
|  | @ -12,20 +11,23 @@ const RunBuffers = plugins.RunBuffers; | ||||||
| pub const RandomNoise = struct { | pub const RandomNoise = struct { | ||||||
|     r: std.rand.DefaultPrng, |     r: std.rand.DefaultPrng, | ||||||
|     rand_buf: ?[]f32 = null, |     rand_buf: ?[]f32 = null, | ||||||
|     allocator: ?std.mem.Allocator = null, |     allocator: ?*std.mem.Allocator = null, | ||||||
|     cnt: usize = 0, |     cnt: usize = 0, | ||||||
| 
 | 
 | ||||||
|     pub fn init( |     pub fn init( | ||||||
|         allocator: std.mem.Allocator, |         allocator: *std.mem.Allocator, | ||||||
|         params: anytype, |         params: *plugins.ParamMap, | ||||||
|     ) ?RandomNoise { |     ) ?RandomNoise { | ||||||
|         var r = std.rand.DefaultPrng.init(params.seed); |         const seed = @floatToInt(u64, params.get("seed").?.value); | ||||||
|  |         const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value); | ||||||
| 
 | 
 | ||||||
|         if (params.fill_bytes > 0) { |         var r = std.rand.DefaultPrng.init(seed); | ||||||
|             var rand_buf = allocator.alloc(f32, params.fill_bytes) catch return null; |  | ||||||
| 
 | 
 | ||||||
|             for (rand_buf, 0..) |_, idx| { |         if (fillbytes > 0) { | ||||||
|                 rand_buf[idx] = r.random().float(f32); |             var rand_buf = allocator.alloc(f32, fillbytes) catch return null; | ||||||
|  | 
 | ||||||
|  |             for (rand_buf) |_, idx| { | ||||||
|  |                 rand_buf[idx] = r.random.float(f32); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return RandomNoise{ |             return RandomNoise{ | ||||||
|  | @ -52,7 +54,7 @@ pub const RandomNoise = struct { | ||||||
|             bufs.out[0] = rand_buf[self.cnt]; |             bufs.out[0] = rand_buf[self.cnt]; | ||||||
|             self.cnt += 1; |             self.cnt += 1; | ||||||
|         } else { |         } else { | ||||||
|             bufs.out[0] = self.r.random().float(f32); |             bufs.out[0] = self.r.random.float(f32); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  | @ -60,20 +62,23 @@ pub const RandomNoise = struct { | ||||||
| pub const WildNoise = struct { | pub const WildNoise = struct { | ||||||
|     r: std.rand.DefaultPrng, |     r: std.rand.DefaultPrng, | ||||||
|     rand_buf: ?[]f32 = null, |     rand_buf: ?[]f32 = null, | ||||||
|     allocator: ?std.mem.Allocator = null, |     allocator: ?*std.mem.Allocator = null, | ||||||
|     cnt: usize = 0, |     cnt: usize = 0, | ||||||
| 
 | 
 | ||||||
|     pub fn init( |     pub fn init( | ||||||
|         allocator: std.mem.Allocator, |         allocator: *std.mem.Allocator, | ||||||
|         params: anytype, |         params: *plugins.ParamMap, | ||||||
|     ) ?WildNoise { |     ) ?WildNoise { | ||||||
|         var r = std.rand.DefaultPrng.init(params.seed); |         const seed = @floatToInt(u64, params.get("seed").?.value); | ||||||
|  |         const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value); | ||||||
| 
 | 
 | ||||||
|         if (params.fill_bytes > 0) { |         var r = std.rand.DefaultPrng.init(seed); | ||||||
|             var rand_buf = allocator.alloc(f32, params.fill_bytes) catch return null; |  | ||||||
| 
 | 
 | ||||||
|             for (rand_buf, 0..) |_, idx| { |         if (fillbytes > 0) { | ||||||
|                 rand_buf[idx] = @as(f32, @floatFromInt(r.random().int(u1))); |             var rand_buf = allocator.alloc(f32, fillbytes) catch return null; | ||||||
|  | 
 | ||||||
|  |             for (rand_buf) |_, idx| { | ||||||
|  |                 rand_buf[idx] = @intToFloat(f32, r.random.int(u1)); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return WildNoise{ |             return WildNoise{ | ||||||
|  | @ -99,7 +104,7 @@ pub const WildNoise = struct { | ||||||
|             bufs.out[0] = rand_buf[self.cnt]; |             bufs.out[0] = rand_buf[self.cnt]; | ||||||
|             self.cnt += 1; |             self.cnt += 1; | ||||||
|         } else { |         } else { | ||||||
|             bufs.out[0] = @as(f32, @floatFromInt(self.r.random().int(u1))); |             bufs.out[0] = @intToFloat(f32, self.r.random.int(u1)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  | @ -112,18 +117,16 @@ pub const Write = struct { | ||||||
|     data: f32, |     data: f32, | ||||||
| 
 | 
 | ||||||
|     pub fn init( |     pub fn init( | ||||||
|         allocator: std.mem.Allocator, |         allocator: *std.mem.Allocator, | ||||||
|         params: anytype, |         params: *plugins.ParamMap, | ||||||
|     ) Write { |     ) Write { | ||||||
|         _ = allocator; |         const data = params.get("data").?; | ||||||
|         return Write{ |         return Write{ | ||||||
|             .data = params.data, |             .data = data.value, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn deinit(self: *Write) void { |     pub fn deinit(self: *Write) void {} | ||||||
|         _ = self; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     pub fn run(self: *Write, bufs: *RunBuffers) void { |     pub fn run(self: *Write, bufs: *RunBuffers) void { | ||||||
|         bufs.out[0] = self.data; |         bufs.out[0] = self.data; | ||||||
|  | @ -131,27 +134,27 @@ pub const Write = struct { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub const Embed = struct { | pub const Embed = struct { | ||||||
|     allocator: std.mem.Allocator, |     allocator: *std.mem.Allocator, | ||||||
|     filepath: []const u8, |     filepath: []const u8, | ||||||
| 
 | 
 | ||||||
|     sndfile: *c.SNDFILE = undefined, |     sndfile: *c.SNDFILE = undefined, | ||||||
|     buf: []f32 = undefined, |     buf: []f32 = undefined, | ||||||
| 
 | 
 | ||||||
|     pub fn init(allocator: std.mem.Allocator, params: anytype) @This() { |     pub fn init(allocator: *std.mem.Allocator, filepath: []const u8) @This() { | ||||||
|         return Embed{ |         return Embed{ | ||||||
|             .allocator = allocator, |             .allocator = allocator, | ||||||
|             .filepath = params.path, |             .filepath = filepath, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn setup(self: *@This()) !void { |     pub fn setup(self: *@This()) !void { | ||||||
|         var in_fmt = c.SF_INFO{ |         var in_fmt = c.SF_INFO{ | ||||||
|             .frames = @as(c_int, 0), |             .frames = c_int(0), | ||||||
|             .samplerate = @as(c_int, 0), |             .samplerate = c_int(0), | ||||||
|             .channels = @as(c_int, 0), |             .channels = c_int(0), | ||||||
|             .format = @as(c_int, 0), |             .format = c_int(0), | ||||||
|             .sections = @as(c_int, 0), |             .sections = c_int(0), | ||||||
|             .seekable = @as(c_int, 0), |             .seekable = c_int(0), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         self.sndfile = try image.sopen( |         self.sndfile = try image.sopen( | ||||||
|  | @ -163,12 +166,10 @@ pub const Embed = struct { | ||||||
| 
 | 
 | ||||||
|         image.sseek(self.sndfile, 0); |         image.sseek(self.sndfile, 0); | ||||||
| 
 | 
 | ||||||
|         self.buf = try self.allocator.alloc(f32, @as(usize, @intCast(in_fmt.channels))); |         self.buf = try self.allocator.alloc(f32, @intCast(usize, in_fmt.channels)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn deinit(self: *@This()) void { |     pub fn deinit(self: *@This()) void {} | ||||||
|         _ = self; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     pub fn run(self: *@This(), bufs: *RunBuffers) void { |     pub fn run(self: *@This(), bufs: *RunBuffers) void { | ||||||
|         const read_bytes = c.sf_readf_float(self.sndfile, self.buf.ptr, 1); |         const read_bytes = c.sf_readf_float(self.sndfile, self.buf.ptr, 1); | ||||||
|  | @ -180,10 +181,11 @@ pub const Embed = struct { | ||||||
| 
 | 
 | ||||||
|         if (read_bytes < 0) { |         if (read_bytes < 0) { | ||||||
|             const st: i32 = c.sf_error(self.sndfile); |             const st: i32 = c.sf_error(self.sndfile); | ||||||
|             log.debug("Failed to read {s} ({s})", .{ |             std.debug.warn( | ||||||
|  |                 "Failed to read {} ({})\n", | ||||||
|                 self.filepath, |                 self.filepath, | ||||||
|                 c.sf_error_number(st), |                 c.sf_error_number(st), | ||||||
|             }); |             ); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										231
									
								
								src/image.zig
									
										
									
									
									
								
							
							
						
						
									
										231
									
								
								src/image.zig
									
										
									
									
									
								
							|  | @ -3,9 +3,11 @@ const lv2 = @import("lv2_helpers.zig"); | ||||||
| const c = lv2.c; | const c = lv2.c; | ||||||
| const bmp = @import("bmp_valid.zig"); | const bmp = @import("bmp_valid.zig"); | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher_image); |  | ||||||
| const plugins = @import("plugin.zig"); | const plugins = @import("plugin.zig"); | ||||||
| 
 | 
 | ||||||
|  | /// Approximate size of the BMP header, in bytes. | ||||||
|  | pub const BMPHeaderSize: usize = 82000; | ||||||
|  | 
 | ||||||
| /// Buffer size for main image copying. | /// Buffer size for main image copying. | ||||||
| pub const BufferSize: usize = 300000; | pub const BufferSize: usize = 300000; | ||||||
| 
 | 
 | ||||||
|  | @ -21,34 +23,35 @@ pub const ImageError = error{ | ||||||
| 
 | 
 | ||||||
| /// Low level integration function with libsndfile. | /// Low level integration function with libsndfile. | ||||||
| pub fn sopen( | pub fn sopen( | ||||||
|     allocator: std.mem.Allocator, |     allocator: *std.mem.Allocator, | ||||||
|     path: []const u8, |     path: []const u8, | ||||||
|     mode: i32, |     mode: i32, | ||||||
|     fmt: *c.SF_INFO, |     fmt: *c.SF_INFO, | ||||||
| ) !*c.SNDFILE { | ) !*c.SNDFILE { | ||||||
|     const cstr_path = try allocator.dupeZ(u8, path); |     var cstr_path = try std.cstr.addNullByte(allocator, path); | ||||||
|     defer allocator.free(cstr_path); |     defer allocator.free(cstr_path); | ||||||
| 
 | 
 | ||||||
|     const file = c.sf_open(cstr_path.ptr, mode, fmt); |     var file = c.sf_open(cstr_path.ptr, mode, fmt); | ||||||
|     const st: i32 = c.sf_error(file); |     const st: i32 = c.sf_error(file); | ||||||
| 
 | 
 | ||||||
|     if (st != 0) { |     if (st != 0) { | ||||||
|         log.debug("Failed to open {s} ({s})", .{ |         std.debug.warn( | ||||||
|  |             "Failed to open {} ({})\n", | ||||||
|             path, |             path, | ||||||
|             c.sf_error_number(st), |             c.sf_error_number(st), | ||||||
|         }); |         ); | ||||||
| 
 | 
 | ||||||
|         return ImageError.OpenFail; |         return ImageError.OpenFail; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const frames_on_end = c.sf_seek(file, 0, c.SEEK_END); |     const frames_on_end = c.sf_seek(file, 0, c.SEEK_END); | ||||||
|     _ = c.sf_seek(file, 0, c.SEEK_SET); |     _ = c.sf_seek(file, 0, c.SEEK_SET); | ||||||
|     try std.testing.expectEqual(fmt.frames, frames_on_end); |     std.testing.expectEqual(fmt.frames, frames_on_end); | ||||||
| 
 | 
 | ||||||
|     const frames_on_end_by_end = c.sf_seek(file, frames_on_end, c.SEEK_SET); |     const frames_on_end_by_end = c.sf_seek(file, frames_on_end, c.SEEK_SET); | ||||||
|     try std.testing.expectEqual(frames_on_end, frames_on_end_by_end); |     std.testing.expectEqual(frames_on_end, frames_on_end_by_end); | ||||||
| 
 | 
 | ||||||
|     log.debug("frames on end: {}, frame on end (2): {}", .{ frames_on_end, frames_on_end_by_end }); |     std.debug.warn("frames on end: {}, frame on end (2): {}\n", frames_on_end, frames_on_end_by_end); | ||||||
| 
 | 
 | ||||||
|     return file.?; |     return file.?; | ||||||
| } | } | ||||||
|  | @ -57,54 +60,49 @@ pub fn swrite(file: *c.SNDFILE, buf: [*]f32, frames: i64) !void { | ||||||
|     const count = c.sf_writef_float(file, buf, frames); |     const count = c.sf_writef_float(file, buf, frames); | ||||||
| 
 | 
 | ||||||
|     if (count != frames) { |     if (count != frames) { | ||||||
|         log.debug("Wanted to write {}, got {}", .{ frames, count }); |         std.debug.warn("Wanted to write {}, got {}\n", frames, count); | ||||||
|         return ImageError.WriteFail; |         return ImageError.WriteFail; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn sseek(file: *c.SNDFILE, offset: usize) void { | pub fn sseek(file: *c.SNDFILE, offset: usize) void { | ||||||
|     const offset_i64 = @as(i64, @intCast(offset)); |     const offset_i64 = @intCast(i64, offset); | ||||||
|     const frames = c.sf_seek(file, offset_i64, c.SEEK_SET); |     const frames = c.sf_seek(file, offset_i64, c.SEEK_SET); | ||||||
|     const frames_current = c.sf_seek(file, 0, c.SEEK_CUR); |     const frames_current = c.sf_seek(file, 0, c.SEEK_CUR); | ||||||
|     std.debug.assert(frames == frames_current); |     std.testing.expectEqual(frames, frames_current); | ||||||
| 
 | 
 | ||||||
|     if (frames != offset_i64) { |     if (frames != offset_i64) { | ||||||
|         log.debug("failed to seek to {} (seeked {} frames, offset_i64={})", .{ offset, frames, offset_i64 }); |         std.debug.warn("failed to seek to {} (seeked {} frames, offset_i64={})\n", offset, frames, offset_i64); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Caller owns the returned memory. | /// Caller owns the returned memory. | ||||||
| pub fn temporaryName(allocator: std.mem.Allocator) ![]u8 { | pub fn temporaryName(allocator: *std.mem.Allocator) ![]u8 { | ||||||
|     const template_start = "/temp/temp_"; |     const template_start = "/temp/temp_"; | ||||||
|     const template = "/tmp/temp_XXXXXXXXXXXXXXXXXXXXX"; |     const template = "/tmp/temp_XXXXXXXXXXX"; | ||||||
|     var nam = try allocator.alloc(u8, template.len); |     var nam = try allocator.alloc(u8, template.len); | ||||||
|     std.mem.copyForwards(u8, nam, template); |     std.mem.copy(u8, nam, template); | ||||||
| 
 | 
 | ||||||
|     const seed = @as(u64, @truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))); |     var r = std.rand.DefaultPrng.init(std.time.timestamp()); | ||||||
|     var r = std.rand.DefaultPrng.init(seed); |  | ||||||
| 
 | 
 | ||||||
|     var fill = nam[template_start.len..nam.len]; |     var fill = nam[template_start.len..nam.len]; | ||||||
| 
 | 
 | ||||||
|     var i: usize = 0; |     var i: usize = 0; | ||||||
|     while (i < 100) : (i += 1) { |     while (i < 100) : (i += 1) { | ||||||
|  | 
 | ||||||
|         // generate a random uppercase letter, that is, 65 + random number. |         // generate a random uppercase letter, that is, 65 + random number. | ||||||
|         for (fill, 0..) |_, f_idx| { |         for (fill) |_, f_idx| { | ||||||
|             const idx = @as(u8, @intCast(r.random().uintLessThan(u5, 24))); |             var idx = @intCast(u8, r.random.uintLessThan(u5, 24)); | ||||||
|             const letter = @as(u8, 65) + idx; |             var letter = u8(65) + idx; | ||||||
|             fill[f_idx] = letter; |             fill[f_idx] = letter; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // if we fail to access it, we assume it doesn't exist and return it. |         // if we fail to access it, we assume it doesn't exist and return it. | ||||||
|         var tmp_file: std.fs.File = std.fs.cwd().openFile( |         std.fs.File.access(nam) catch |err| { | ||||||
|             nam, |             if (err == error.FileNotFound) { | ||||||
|             .{ .mode = .read_only }, |                 return nam; | ||||||
|         ) catch |err| { |             } | ||||||
|             if (err == error.FileNotFound) return nam else continue; |  | ||||||
|         }; |         }; | ||||||
| 
 |  | ||||||
|         // if we actually found someone, close the handle so that we don't |  | ||||||
|         // get EMFILE later on. |  | ||||||
|         tmp_file.close(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return error.TempGenFail; |     return error.TempGenFail; | ||||||
|  | @ -112,17 +110,17 @@ pub fn temporaryName(allocator: std.mem.Allocator) ![]u8 { | ||||||
| 
 | 
 | ||||||
| pub fn mkSfInfo() c.SF_INFO { | pub fn mkSfInfo() c.SF_INFO { | ||||||
|     return c.SF_INFO{ |     return c.SF_INFO{ | ||||||
|         .frames = @as(c_int, 0), |         .frames = c_int(0), | ||||||
|         .samplerate = @as(c_int, 44100), |         .samplerate = c_int(44100), | ||||||
|         .channels = @as(c_int, 1), |         .channels = c_int(1), | ||||||
|         .format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW | c.SF_ENDIAN_BIG, |         .format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW | c.SF_ENDIAN_BIG, | ||||||
|         .sections = @as(c_int, 0), |         .sections = c_int(0), | ||||||
|         .seekable = @as(c_int, 0), |         .seekable = c_int(0), | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub const Image = struct { | pub const Image = struct { | ||||||
|     allocator: std.mem.Allocator, |     allocator: *std.mem.Allocator, | ||||||
| 
 | 
 | ||||||
|     /// Pointer to the underlying libsndfile's SNDFILE struct. |     /// Pointer to the underlying libsndfile's SNDFILE struct. | ||||||
|     sndfile: *c.SNDFILE, |     sndfile: *c.SNDFILE, | ||||||
|  | @ -137,21 +135,21 @@ pub const Image = struct { | ||||||
|     curpath: []const u8, |     curpath: []const u8, | ||||||
| 
 | 
 | ||||||
|     /// Open a BMP image for later. |     /// Open a BMP image for later. | ||||||
|     pub fn open(allocator: std.mem.Allocator, path: []const u8) !*Image { |     pub fn open(allocator: *std.mem.Allocator, path: []const u8) !*Image { | ||||||
|         var in_fmt = mkSfInfo(); |         var in_fmt = mkSfInfo(); | ||||||
|         const sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt); |         var sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt); | ||||||
| 
 | 
 | ||||||
|         const image = try allocator.create(Image); |         var image = try allocator.create(Image); | ||||||
| 
 | 
 | ||||||
|         std.debug.assert(in_fmt.frames > @as(i64, 0)); |         std.debug.assert(in_fmt.frames > i64(0)); | ||||||
|         std.debug.assert(in_fmt.seekable == @as(i32, 1)); |         std.debug.assert(in_fmt.seekable == i32(1)); | ||||||
| 
 | 
 | ||||||
|         image.* = Image{ |         image.* = Image{ | ||||||
|             .allocator = allocator, |             .allocator = allocator, | ||||||
|             .sndfile = sndfile, |             .sndfile = sndfile, | ||||||
|             .path = path, |             .path = path, | ||||||
|             .curpath = path, |             .curpath = path, | ||||||
|             .frames = @as(usize, @intCast(in_fmt.frames)), |             .frames = @intCast(usize, in_fmt.frames), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         return image; |         return image; | ||||||
|  | @ -160,50 +158,49 @@ pub const Image = struct { | ||||||
|     pub fn clone(self: *Image) !*Image { |     pub fn clone(self: *Image) !*Image { | ||||||
|         var in_fmt = mkSfInfo(); |         var in_fmt = mkSfInfo(); | ||||||
|         // clone sndfile |         // clone sndfile | ||||||
|         const sndfile = try sopen(self.allocator, self.curpath, c.SFM_READ, &in_fmt); |         var sndfile = try sopen(self.allocator, self.curpath, c.SFM_READ, &in_fmt); | ||||||
|         std.debug.assert(self.frames == @as(usize, @intCast(in_fmt.frames))); |         std.testing.expectEqual(self.frames, @intCast(usize, in_fmt.frames)); | ||||||
| 
 | 
 | ||||||
|         const image = try self.allocator.create(Image); |         var image = try self.allocator.create(Image); | ||||||
| 
 | 
 | ||||||
|         std.debug.assert(in_fmt.frames > @as(i64, 0)); |         std.debug.assert(in_fmt.frames > i64(0)); | ||||||
|         std.debug.assert(in_fmt.seekable == @as(i32, 1)); |         std.debug.assert(in_fmt.seekable == i32(1)); | ||||||
| 
 | 
 | ||||||
|         image.* = Image{ |         image.* = Image{ | ||||||
|             .allocator = self.allocator, |             .allocator = self.allocator, | ||||||
|             .sndfile = sndfile, |             .sndfile = sndfile, | ||||||
|             .path = self.path, |             .path = self.path, | ||||||
|             .curpath = self.curpath, |             .curpath = self.curpath, | ||||||
|             .frames = @as(usize, @intCast(in_fmt.frames)), |             .frames = @intCast(usize, in_fmt.frames), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         return image; |         return image; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn close(self: *Image) void { |     pub fn close(self: *Image) void { | ||||||
|         const st: i32 = c.sf_close(self.sndfile); |         //self.allocator.free(self.path); | ||||||
|  |         //self.allocator.free(self.curpath); | ||||||
|  |         var st: i32 = c.sf_close(self.sndfile); | ||||||
|  | 
 | ||||||
|         if (st != 0) { |         if (st != 0) { | ||||||
|             log.debug("Failed to close {s} ({s})", .{ |             std.debug.warn( | ||||||
|  |                 "Failed to close {} ({})\n", | ||||||
|                 self.path, |                 self.path, | ||||||
|                 c.sf_error_number(st), |                 c.sf_error_number(st), | ||||||
|             }); |             ); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         self.allocator.free(self.path); |  | ||||||
|         self.allocator.free(self.curpath); |  | ||||||
| 
 |  | ||||||
|         var allocator = self.allocator; |  | ||||||
|         self.* = undefined; |  | ||||||
|         allocator.destroy(self); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn read(self: *Image, file_chans: c_int, buf: []f32) bool { |     pub fn read(self: *Image, file_chans: c_int, buf: []f32) bool { | ||||||
|         const n_read: c.sf_count_t = c.sf_readf_float(self.sndfile, buf.ptr, 1); |         var file = file_opt.?; | ||||||
|         const buf_chans = @as(c_int, @intCast(buf.len)); | 
 | ||||||
|  |         const n_read: c.sf_count_t = c.sf_readf_float(file, buf.ptr, 1); | ||||||
|  |         const buf_chans = @intCast(c_int, buf.len); | ||||||
| 
 | 
 | ||||||
|         var i = file_chans - 1; |         var i = file_chans - 1; | ||||||
|         while (i < buf_chans) : (i += 1) { |         while (i < buf_chans) : (i += 1) { | ||||||
|             //buf[@intCast(usize, i)] = buf[i % file_chans]; |             //buf[@intCast(usize, i)] = buf[i % file_chans]; | ||||||
|             buf[@as(usize, @intCast(i))] = buf[@as(usize, @intCast(@mod(i, file_chans)))]; |             buf[@intCast(usize, i)] = buf[@intCast(usize, @mod(i, file_chans))]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return n_read == 1; |         return n_read == 1; | ||||||
|  | @ -227,7 +224,7 @@ pub const Image = struct { | ||||||
|         sseek(out_file, start); |         sseek(out_file, start); | ||||||
| 
 | 
 | ||||||
|         while (i <= end) : (i += buf.len) { |         while (i <= end) : (i += buf.len) { | ||||||
|             log.debug("\t\ti={d}, buf.len={d}, end={d}", .{ i, buf.len, end }); |             std.debug.warn("\t\ti={}, buf.len={}, end={}\n", i, buf.len, end); | ||||||
|             sseek(self.sndfile, i); |             sseek(self.sndfile, i); | ||||||
|             sseek(out_file, i); |             sseek(out_file, i); | ||||||
| 
 | 
 | ||||||
|  | @ -237,13 +234,13 @@ pub const Image = struct { | ||||||
|             var view: []f32 = buf[0..buf.len]; |             var view: []f32 = buf[0..buf.len]; | ||||||
| 
 | 
 | ||||||
|             if (bytes_until_end < buf.len) { |             if (bytes_until_end < buf.len) { | ||||||
|                 read_bytes = c.sf_readf_float(self.sndfile, buf.ptr, @as(i64, @intCast(bytes_until_end))); |                 read_bytes = c.sf_readf_float(self.sndfile, buf.ptr, @intCast(i64, bytes_until_end)); | ||||||
|                 view = buf[0..bytes_until_end]; |                 view = buf[0..bytes_until_end]; | ||||||
|             } else { |             } else { | ||||||
|                 read_bytes = c.sf_readf_float(self.sndfile, buf.ptr, @as(i64, @intCast(buf.len))); |                 read_bytes = c.sf_readf_float(self.sndfile, buf.ptr, @intCast(i64, buf.len)); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             try swrite(out_file, view.ptr, @as(i64, @intCast(view.len))); |             try swrite(out_file, view.ptr, @intCast(i64, view.len)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         sseek(self.sndfile, end); |         sseek(self.sndfile, end); | ||||||
|  | @ -252,8 +249,8 @@ pub const Image = struct { | ||||||
| 
 | 
 | ||||||
|     fn getSeekPos(self: *Image, position: plugins.Position) plugins.SeekPos { |     fn getSeekPos(self: *Image, position: plugins.Position) plugins.SeekPos { | ||||||
|         const file_end = self.frames; |         const file_end = self.frames; | ||||||
|         const seek_pos = position.seekPos(file_end); |         var seek_pos = position.seekPos(file_end); | ||||||
|         log.debug("\tstart {d} end {d}", .{ seek_pos.start, seek_pos.end }); |         std.debug.warn("\tstart {} end {}\n", seek_pos.start, seek_pos.end); | ||||||
|         return seek_pos; |         return seek_pos; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -261,20 +258,21 @@ pub const Image = struct { | ||||||
|         var in_fmt = mkSfInfo(); |         var in_fmt = mkSfInfo(); | ||||||
| 
 | 
 | ||||||
|         self.sndfile = try sopen(self.allocator, path, c.SFM_READ, &in_fmt); |         self.sndfile = try sopen(self.allocator, path, c.SFM_READ, &in_fmt); | ||||||
|         // std.testing.expectEqual(self.frames, @intCast(usize, in_fmt.frames)); |         std.testing.expectEqual(self.frames, @intCast(usize, in_fmt.frames)); | ||||||
| 
 | 
 | ||||||
|         self.curpath = path; |         self.curpath = path; | ||||||
|         self.frames = @as(usize, @intCast(in_fmt.frames)); |         self.frames = @intCast(usize, in_fmt.frames); | ||||||
| 
 | 
 | ||||||
|         log.debug("\timage: reopened on '{s}' (frames={d}, fmt.frames={d})", .{ |         std.debug.warn( | ||||||
|  |             "\timage: reopened on '{}' (frames={}, fmt.frames={})\n", | ||||||
|             self.curpath, |             self.curpath, | ||||||
|             self.frames, |             self.frames, | ||||||
|             in_fmt.frames, |             in_fmt.frames, | ||||||
|         }); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn checkValid(self: *Image) !void { |     pub fn checkValid(self: *Image) !void { | ||||||
|         var file = try std.fs.cwd().openFile(self.path, .{ .mode = .read_only }); |         var file = try std.fs.File.openRead(self.path); | ||||||
|         defer file.close(); |         defer file.close(); | ||||||
| 
 | 
 | ||||||
|         // main bmp header: |         // main bmp header: | ||||||
|  | @ -286,7 +284,6 @@ pub const Image = struct { | ||||||
|         var magic = [2]u8{ 0, 0 }; |         var magic = [2]u8{ 0, 0 }; | ||||||
|         _ = try file.read(&magic); |         _ = try file.read(&magic); | ||||||
| 
 | 
 | ||||||
|         if (std.mem.endsWith(u8, self.path, ".bmp")) |  | ||||||
|         try bmp.magicValid(&magic); |         try bmp.magicValid(&magic); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -306,48 +303,50 @@ pub const Image = struct { | ||||||
|         defer ctx.deinit(); |         defer ctx.deinit(); | ||||||
| 
 | 
 | ||||||
|         var ports = try lv2.setupPorts(&ctx); |         var ports = try lv2.setupPorts(&ctx); | ||||||
|         defer ctx.allocator.free(ports); |  | ||||||
| 
 | 
 | ||||||
|         if (ctx.n_audio_in > 2) { |         if (ctx.n_audio_in > 2) { | ||||||
|             log.debug("plugin <{s}> has more than two inputs.", .{plugin_uri}); |             std.debug.warn("plugin <{}> has more than two inputs.\n", plugin_uri); | ||||||
|             return ImageError.InvalidPlugin; |             return ImageError.InvalidPlugin; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (ctx.n_audio_out > 2) { |         if (ctx.n_audio_out > 2) { | ||||||
|             log.debug("plugin <{s}> has more than two outputs.", .{plugin_uri}); |             std.debug.warn("plugin <{}> has more than two outputs.\n", plugin_uri); | ||||||
|             return ImageError.InvalidPlugin; |             return ImageError.InvalidPlugin; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // now, for each param for the plugin, we find its port, and set |         // now, for each param for the plugin, we find its port, and set | ||||||
|         // the value for the port there. |         // the value for the port there. | ||||||
|         for (params.items) |param| { |         var it = params.iterator(); | ||||||
|             const sym_cstr = try self.allocator.dupeZ(u8, param.sym); | 
 | ||||||
|  |         while (it.next()) |param| { | ||||||
|  |             var sym_cstr = try std.cstr.addNullByte(self.allocator, param.sym); | ||||||
|             defer self.allocator.free(sym_cstr); |             defer self.allocator.free(sym_cstr); | ||||||
| 
 | 
 | ||||||
|             const sym = c.lilv_new_string(ctx.world, sym_cstr.ptr); |             var sym = c.lilv_new_string(ctx.world, sym_cstr.ptr); | ||||||
|             const port = c.lilv_plugin_get_port_by_symbol(ctx.plugin, sym) orelse { |             const port = c.lilv_plugin_get_port_by_symbol(ctx.plugin, sym) orelse blk: { | ||||||
|                 log.debug("assert fail: symbol {s} not found on port", .{param.sym}); |                 std.debug.warn("assert fail: symbol {} not found on port\n", param.sym); | ||||||
|                 return ImageError.InvalidSymbol; |                 return ImageError.InvalidSymbol; | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             c.lilv_node_free(sym); |             c.lilv_node_free(sym); | ||||||
| 
 | 
 | ||||||
|             const idx = c.lilv_port_get_index(ctx.plugin, port); |             var idx = c.lilv_port_get_index(ctx.plugin, port); | ||||||
|             log.debug("\tset sym={s}, idx={d} to val={}", .{ |             std.debug.warn( | ||||||
|  |                 "\tset sym={}, idx={} to val={}\n", | ||||||
|                 param.sym, |                 param.sym, | ||||||
|                 idx, |                 idx, | ||||||
|                 param.value, |                 param.value, | ||||||
|             }); |             ); | ||||||
|             (&ports[idx]).value = param.value; |             (&ports[idx]).value = param.value; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // now we need to generate a temporary file and put the output of |         // now we need to generate a temporary file and put the output of | ||||||
|         // running the plugin on that file |         // running the plugin on that file | ||||||
|         const tmpnam = try temporaryName(self.allocator); |         var tmpnam = try temporaryName(self.allocator); | ||||||
|         log.debug("\trunning plugin from '{s}' to '{s}'", .{ self.curpath, tmpnam }); |         std.debug.warn("\trunning plugin from '{}' to '{}'\n", self.curpath, tmpnam); | ||||||
| 
 | 
 | ||||||
|         var out_fmt = mkSfInfo(); |         var out_fmt = mkSfInfo(); | ||||||
|         const out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt); |         var out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt); | ||||||
| 
 | 
 | ||||||
|         var rctx = try plugins.RunContext.init(self.allocator, ctx.plugin); |         var rctx = try plugins.RunContext.init(self.allocator, ctx.plugin); | ||||||
|         defer rctx.deinit(); |         defer rctx.deinit(); | ||||||
|  | @ -370,34 +369,27 @@ pub const Image = struct { | ||||||
|         // pre-plugin copy, merged with bmp header copy |         // pre-plugin copy, merged with bmp header copy | ||||||
|         try self.copyBytes( |         try self.copyBytes( | ||||||
|             out_file, |             out_file, | ||||||
|             @as(usize, 0), |             usize(0), | ||||||
|             seek_pos.start, |             seek_pos.start, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         sseek(self.sndfile, seek_pos.start); |         sseek(self.sndfile, seek_pos.start); | ||||||
| 
 | 
 | ||||||
|         var i: usize = seek_pos.start; |         var i: usize = seek_pos.start; | ||||||
|         log.debug("\tseek pos start: {d} end: {d}", .{ seek_pos.start, seek_pos.end }); |         std.debug.warn("\tseek pos start: {} end: {}\n", seek_pos.start, seek_pos.end); | ||||||
| 
 | 
 | ||||||
|         var inbuf = &rctx.buffers.in; |         var inbuf = rctx.buffers.in; | ||||||
|         const outbuf = &rctx.buffers.out; |         var outbuf = rctx.buffers.out; | ||||||
| 
 | 
 | ||||||
|         while (i <= seek_pos.end) : (i += 1) { |         while (i <= seek_pos.end) : (i += 1) { | ||||||
|             inbuf[0] = 0; |             const read_bytes = c.sf_readf_float(self.sndfile, inbuf.ptr, 1); | ||||||
|             inbuf[1] = 0; |  | ||||||
| 
 |  | ||||||
|             const read_bytes = c.sf_readf_float(self.sndfile, inbuf, 1); |  | ||||||
|             if (read_bytes == 0) { |             if (read_bytes == 0) { | ||||||
|                 log.debug("WARN! reached EOF at idx={d}", .{i}); |                 std.debug.warn("WARN! reached EOF at idx={}\n", i); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // trick plugins into having correct stereo signal from |  | ||||||
|             // my mono input |  | ||||||
|             inbuf[1] = inbuf[0]; |  | ||||||
| 
 |  | ||||||
|             lv2.lilv_instance_run(rctx.instance, 1); |             lv2.lilv_instance_run(rctx.instance, 1); | ||||||
|             try swrite(out_file, outbuf, 1); |             try swrite(out_file, outbuf.ptr, 1); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         sseek(self.sndfile, seek_pos.end); |         sseek(self.sndfile, seek_pos.end); | ||||||
|  | @ -416,22 +408,23 @@ pub const Image = struct { | ||||||
|         try self.reopen(tmpnam); |         try self.reopen(tmpnam); | ||||||
|         try self.checkValid(); |         try self.checkValid(); | ||||||
| 
 | 
 | ||||||
|         const time_taken = timer.read(); |         var time_taken = timer.read(); | ||||||
|         log.debug("\ttook {d:.2}ms running plugin", .{time_taken / std.time.us_per_ms}); |         std.debug.warn("\ttook {d:.2}ms running plugin\n", time_taken / std.time.millisecond); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn saveTo(self: *Image, out_path: []const u8) !void { |     pub fn saveTo(self: *Image, out_path: []const u8) !void { | ||||||
|         log.debug("\timg: copy from '{s}' to '{s}'", .{ self.curpath, out_path }); |         std.debug.warn("\timg: copy from '{}' to '{}'\n", self.curpath, out_path); | ||||||
|         try std.fs.copyFileAbsolute(self.curpath, out_path, .{}); |         try std.fs.copyFile(self.curpath, out_path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn runCustomPlugin( |     pub fn runCustomPlugin( | ||||||
|         self: *Image, |         self: *Image, | ||||||
|         comptime Plugin: type, |         comptime Plugin: type, | ||||||
|         position: plugins.Position, |         position: plugins.Position, | ||||||
|         extra: anytype, |         comptime ExtraType: type, | ||||||
|  |         extra: ExtraType, | ||||||
|     ) !void { |     ) !void { | ||||||
|         const plugin_opt: ?Plugin = Plugin.init(self.allocator, extra); |         var plugin_opt: ?Plugin = Plugin.init(self.allocator, extra); | ||||||
|         if (plugin_opt == null) { |         if (plugin_opt == null) { | ||||||
|             return ImageError.PluginLoadFail; |             return ImageError.PluginLoadFail; | ||||||
|         } |         } | ||||||
|  | @ -448,13 +441,15 @@ pub const Image = struct { | ||||||
| 
 | 
 | ||||||
|         // the code here is a copypaste of runPlugin() without the specific |         // the code here is a copypaste of runPlugin() without the specific | ||||||
|         // lilv things. |         // lilv things. | ||||||
|         const tmpnam = try temporaryName(self.allocator); |         var tmpnam = try temporaryName(self.allocator); | ||||||
|         log.debug("\trunning CUSTOM plugin from '{s}' to '{s}'", .{ self.curpath, tmpnam }); |         std.debug.warn("\trunning CUSTOM plugin from '{}' to '{}'\n", self.curpath, tmpnam); | ||||||
| 
 | 
 | ||||||
|         var out_fmt = mkSfInfo(); |         var out_fmt = mkSfInfo(); | ||||||
|         const out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt); |         var out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt); | ||||||
|  | 
 | ||||||
|  |         var bufs = try plugins.RunBuffers.init(self.allocator); | ||||||
|  |         defer bufs.deinit(); | ||||||
| 
 | 
 | ||||||
|         var bufs = plugins.RunBuffers{}; |  | ||||||
|         const seek_pos = self.getSeekPos(position); |         const seek_pos = self.getSeekPos(position); | ||||||
| 
 | 
 | ||||||
|         // make sure we start from 0 |         // make sure we start from 0 | ||||||
|  | @ -469,27 +464,27 @@ pub const Image = struct { | ||||||
|         // pre-plugin copy, merged with bmp header copy |         // pre-plugin copy, merged with bmp header copy | ||||||
|         try self.copyBytes( |         try self.copyBytes( | ||||||
|             out_file, |             out_file, | ||||||
|             @as(usize, 0), |             usize(0), | ||||||
|             seek_pos.start, |             seek_pos.start, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         sseek(self.sndfile, seek_pos.start); |         sseek(self.sndfile, seek_pos.start); | ||||||
| 
 | 
 | ||||||
|         var i: usize = seek_pos.start; |         var i: usize = seek_pos.start; | ||||||
|         log.debug("\tseek pos start: {d} end: {d}", .{ seek_pos.start, seek_pos.end }); |         std.debug.warn("\tseek pos start: {} end: {}\n", seek_pos.start, seek_pos.end); | ||||||
| 
 | 
 | ||||||
|         const inbuf = &bufs.in; |         var inbuf = bufs.in; | ||||||
|         const outbuf = &bufs.out; |         var outbuf = bufs.out; | ||||||
| 
 | 
 | ||||||
|         while (i <= seek_pos.end) : (i += 1) { |         while (i <= seek_pos.end) : (i += 1) { | ||||||
|             const read_bytes = c.sf_readf_float(self.sndfile, inbuf, 1); |             const read_bytes = c.sf_readf_float(self.sndfile, bufs.in.ptr, 1); | ||||||
|             if (read_bytes == 0) { |             if (read_bytes == 0) { | ||||||
|                 log.debug("WARN! reached EOF at idx={d}", .{i}); |                 std.debug.warn("WARN! reached EOF at idx={}\n", i); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             plugin.run(&bufs); |             plugin.run(&bufs); | ||||||
|             try swrite(out_file, outbuf, 1); |             try swrite(out_file, bufs.out.ptr, 1); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         sseek(self.sndfile, seek_pos.end); |         sseek(self.sndfile, seek_pos.end); | ||||||
|  |  | ||||||
							
								
								
									
										976
									
								
								src/lang.zig
									
										
									
									
									
								
							
							
						
						
									
										976
									
								
								src/lang.zig
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,12 +1,10 @@ | ||||||
| const std = @import("std"); | const std = @import("std"); | ||||||
| const plugin = @import("plugin.zig"); | const plugin = @import("plugin.zig"); | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher_lv2); |  | ||||||
| 
 |  | ||||||
| pub const c = @cImport({ | pub const c = @cImport({ | ||||||
|     @cInclude("sndfile.h"); |     @cInclude("sndfile.h"); | ||||||
|     @cInclude("lilv/lilv.h"); |     @cInclude("lilv/lilv.h"); | ||||||
|     @cInclude("lv2.h"); |     @cInclude("lv2/core/lv2.h"); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| pub fn Lv2Core(comptime ns: []const u8) []const u8 { | pub fn Lv2Core(comptime ns: []const u8) []const u8 { | ||||||
|  | @ -23,7 +21,7 @@ const LV2_CORE__connectionOptional = Lv2Core("#connectionOptional"); | ||||||
| pub fn lilv_instance_connect_port( | pub fn lilv_instance_connect_port( | ||||||
|     instance: [*c]c.LilvInstance, |     instance: [*c]c.LilvInstance, | ||||||
|     port_index: u32, |     port_index: u32, | ||||||
|     data_location: ?*anyopaque, |     data_location: ?*c_void, | ||||||
| ) void { | ) void { | ||||||
|     instance.?.*.lv2_descriptor.?.*.connect_port.?(instance.?.*.lv2_handle, port_index, data_location); |     instance.?.*.lv2_descriptor.?.*.connect_port.?(instance.?.*.lv2_handle, port_index, data_location); | ||||||
| } | } | ||||||
|  | @ -61,44 +59,42 @@ pub const Port = struct { | ||||||
| /// Setup ports for a given plugin. Gives an array to pointers of Port structs. | /// Setup ports for a given plugin. Gives an array to pointers of Port structs. | ||||||
| /// This setup is required so we link the plugin to the ports later on, and | /// This setup is required so we link the plugin to the ports later on, and | ||||||
| /// also link our buffers, and control values. | /// also link our buffers, and control values. | ||||||
| /// |  | ||||||
| /// Caller owns returned memory. |  | ||||||
| pub fn setupPorts(ctx: *plugin.Context) ![]Port { | pub fn setupPorts(ctx: *plugin.Context) ![]Port { | ||||||
|     const world = ctx.world; |     var world = ctx.world; | ||||||
|     const n_ports: u32 = c.lilv_plugin_get_num_ports(ctx.plugin); |     const n_ports: u32 = c.lilv_plugin_get_num_ports(ctx.plugin); | ||||||
| 
 | 
 | ||||||
|     var ports = try ctx.allocator.alloc(Port, n_ports); |     var ports = try ctx.allocator.alloc(Port, n_ports); | ||||||
| 
 | 
 | ||||||
|     for (ports, 0..) |_, idx| { |     for (ports) |_, idx| { | ||||||
|         const port: *Port = &ports[idx]; |         var port: *Port = &ports[idx]; | ||||||
|         port.* = Port{ |         port.* = Port{ | ||||||
|             .lilv_port = null, |             .lilv_port = null, | ||||||
|             .ptype = .Control, |             .ptype = .Control, | ||||||
|             .index = @as(f32, 0), |             .index = f32(0), | ||||||
|             .value = @as(f32, 0), |             .value = f32(0), | ||||||
|             .is_input = false, |             .is_input = false, | ||||||
|             .optional = false, |             .optional = false, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const values: []f32 = try ctx.allocator.alloc(f32, n_ports); |     var values: []f32 = try ctx.allocator.alloc(f32, n_ports); | ||||||
|     defer ctx.allocator.free(values); |     defer ctx.allocator.free(values); | ||||||
| 
 | 
 | ||||||
|     c.lilv_plugin_get_port_ranges_float(ctx.plugin, null, null, values.ptr); |     c.lilv_plugin_get_port_ranges_float(ctx.plugin, null, null, values.ptr); | ||||||
|     const lv2_InputPort = c.lilv_new_uri(world, LV2_CORE__InputPort.ptr).?; |     var lv2_InputPort = c.lilv_new_uri(world, LV2_CORE__InputPort.ptr); | ||||||
|     //defer std.heap.c_allocator.destroy(lv2_InputPort); |     defer std.heap.c_allocator.destroy(lv2_InputPort); | ||||||
| 
 | 
 | ||||||
|     const lv2_OutputPort = c.lilv_new_uri(world, LV2_CORE__OutputPort.ptr).?; |     var lv2_OutputPort = c.lilv_new_uri(world, LV2_CORE__OutputPort.ptr); | ||||||
|     //defer std.heap.c_allocator.destroy(lv2_OutputPort); |     defer std.heap.c_allocator.destroy(lv2_OutputPort); | ||||||
| 
 | 
 | ||||||
|     const lv2_AudioPort = c.lilv_new_uri(world, LV2_CORE__AudioPort.ptr).?; |     var lv2_AudioPort = c.lilv_new_uri(world, LV2_CORE__AudioPort.ptr); | ||||||
|     //defer std.heap.c_allocator.destroy(lv2_AudioPort); |     defer std.heap.c_allocator.destroy(lv2_AudioPort); | ||||||
| 
 | 
 | ||||||
|     const lv2_ControlPort = c.lilv_new_uri(world, LV2_CORE__ControlPort.ptr).?; |     var lv2_ControlPort = c.lilv_new_uri(world, LV2_CORE__ControlPort.ptr); | ||||||
|     //defer std.heap.c_allocator.destroy(lv2_ControlPort); |     defer std.heap.c_allocator.destroy(lv2_ControlPort); | ||||||
| 
 | 
 | ||||||
|     const lv2_connection_string = c.lilv_new_uri(world, LV2_CORE__connectionOptional.ptr).?; |     var lv2_connectionOptional = c.lilv_new_uri(world, LV2_CORE__connectionOptional.ptr); | ||||||
|     //defer std.heap.c_allocator.destroy(lv2_connection_string); |     defer std.heap.c_allocator.destroy(lv2_connectionOptional); | ||||||
| 
 | 
 | ||||||
|     var i: u32 = 0; |     var i: u32 = 0; | ||||||
|     while (i < n_ports) : (i += 1) { |     while (i < n_ports) : (i += 1) { | ||||||
|  | @ -110,17 +106,17 @@ pub fn setupPorts(ctx: *plugin.Context) ![]Port { | ||||||
|         port.index = i; |         port.index = i; | ||||||
| 
 | 
 | ||||||
|         if (std.math.isNan(values[i])) { |         if (std.math.isNan(values[i])) { | ||||||
|             port.value = @as(f32, 0); |             port.value = f32(0); | ||||||
|         } else { |         } else { | ||||||
|             port.value = values[i]; |             port.value = values[i]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         port.optional = c.lilv_port_has_property(ctx.plugin, lport, lv2_connection_string); |         port.optional = c.lilv_port_has_property(ctx.plugin, lport, lv2_connectionOptional); | ||||||
| 
 | 
 | ||||||
|         if (c.lilv_port_is_a(ctx.plugin, lport, lv2_InputPort)) { |         if (c.lilv_port_is_a(ctx.plugin, lport, lv2_InputPort)) { | ||||||
|             port.is_input = true; |             port.is_input = true; | ||||||
|         } else if (!c.lilv_port_is_a(ctx.plugin, lport, lv2_OutputPort) and !port.optional) { |         } else if (!c.lilv_port_is_a(ctx.plugin, lport, lv2_OutputPort) and !port.optional) { | ||||||
|             log.debug("Port {d} is neither input or output", .{i}); |             std.debug.warn("Port {} is neither input or output\n", i); | ||||||
|             return error.UnassignedIOPort; |             return error.UnassignedIOPort; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -136,7 +132,7 @@ pub fn setupPorts(ctx: *plugin.Context) ![]Port { | ||||||
|                 ctx.n_audio_out += 1; |                 ctx.n_audio_out += 1; | ||||||
|             } |             } | ||||||
|         } else if (!port.optional) { |         } else if (!port.optional) { | ||||||
|             log.debug("Port {d} has unsupported type", .{i}); |             std.debug.warn("Port {} has unsupported type\n", i); | ||||||
|             return error.UnsupportedPortType; |             return error.UnsupportedPortType; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -2,12 +2,9 @@ | ||||||
| const std = @import("std"); | const std = @import("std"); | ||||||
| const images = @import("image.zig"); | const images = @import("image.zig"); | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher_magick); |  | ||||||
| const Image = images.Image; | const Image = images.Image; | ||||||
| 
 | 
 | ||||||
| const mc = @cImport({ | const mc = @import("magick_wand.zig"); | ||||||
|     @cInclude("wand/magick_wand.h"); |  | ||||||
| }); |  | ||||||
| 
 | 
 | ||||||
| pub const MagickContext = struct { | pub const MagickContext = struct { | ||||||
|     wand: *mc.MagickWand, |     wand: *mc.MagickWand, | ||||||
|  | @ -15,7 +12,7 @@ pub const MagickContext = struct { | ||||||
|     pub fn init() !MagickContext { |     pub fn init() !MagickContext { | ||||||
|         mc.InitializeMagick(null); |         mc.InitializeMagick(null); | ||||||
| 
 | 
 | ||||||
|         const wand = mc.NewMagickWand(); |         var wand = mc.NewMagickWand(); | ||||||
|         if (wand == null) return error.WandCreateFail; |         if (wand == null) return error.WandCreateFail; | ||||||
| 
 | 
 | ||||||
|         return MagickContext{ |         return MagickContext{ | ||||||
|  | @ -29,7 +26,6 @@ pub const MagickContext = struct { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn doErr(self: *MagickContext) !void { |     pub fn doErr(self: *MagickContext) !void { | ||||||
|         _ = self; |  | ||||||
|         return error.WandError; |         return error.WandError; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  | @ -38,10 +34,10 @@ fn magickLoad(image: *Image) !MagickContext { | ||||||
|     var mctx = try MagickContext.init(); |     var mctx = try MagickContext.init(); | ||||||
|     errdefer mctx.deinit(); |     errdefer mctx.deinit(); | ||||||
| 
 | 
 | ||||||
|     const curpath = try image.allocator.dupeZ(u8, image.curpath); |     var curpath = try std.cstr.addNullByte(image.allocator, image.curpath); | ||||||
|     defer image.allocator.free(curpath); |     defer image.allocator.free(curpath); | ||||||
| 
 | 
 | ||||||
|     log.debug("loading '{s}'", .{curpath}); |     std.debug.warn("loading '{}'\n", curpath); | ||||||
| 
 | 
 | ||||||
|     if (mc.MagickReadImage(mctx.wand, curpath.ptr) != 1) |     if (mc.MagickReadImage(mctx.wand, curpath.ptr) != 1) | ||||||
|         return error.MagickReadFail; |         return error.MagickReadFail; | ||||||
|  | @ -52,16 +48,16 @@ fn magickLoad(image: *Image) !MagickContext { | ||||||
| fn magickSave(image: *Image, wand: *mc.MagickWand) !void { | fn magickSave(image: *Image, wand: *mc.MagickWand) !void { | ||||||
|     const allocator = image.allocator; |     const allocator = image.allocator; | ||||||
| 
 | 
 | ||||||
|     const tmpnam = try images.temporaryName(allocator); |     var tmpnam = try images.temporaryName(allocator); | ||||||
|     const c_tmpnam = try allocator.dupeZ(u8, tmpnam); |     var c_tmpnam = try std.cstr.addNullByte(allocator, tmpnam); | ||||||
|     defer allocator.free(c_tmpnam); |     defer allocator.free(c_tmpnam); | ||||||
| 
 | 
 | ||||||
|     log.debug("\tmagick: saving to '{s}'..", .{c_tmpnam}); |     std.debug.warn("\tmagick: saving to '{}'..", c_tmpnam); | ||||||
| 
 | 
 | ||||||
|     if (mc.MagickWriteImage(wand, c_tmpnam.ptr) != 1) |     if (mc.MagickWriteImage(wand, c_tmpnam.ptr) != 1) | ||||||
|         return error.MagickWriteFail; |         return error.MagickWriteFail; | ||||||
| 
 | 
 | ||||||
|     log.debug("OK", .{}); |     std.debug.warn("OK\n"); | ||||||
| 
 | 
 | ||||||
|     try image.reopen(tmpnam); |     try image.reopen(tmpnam); | ||||||
| } | } | ||||||
|  | @ -72,7 +68,7 @@ pub fn runRotate(image: *Image, deg: f32, bgfill: []const u8) !void { | ||||||
|     var mctx = try magickLoad(image); |     var mctx = try magickLoad(image); | ||||||
|     defer mctx.deinit(); |     defer mctx.deinit(); | ||||||
| 
 | 
 | ||||||
|     const bg = mc.NewPixelWand(); |     var bg = mc.NewPixelWand(); | ||||||
|     defer mc.DestroyPixelWand(bg); |     defer mc.DestroyPixelWand(bg); | ||||||
| 
 | 
 | ||||||
|     if (mc.PixelSetColor(bg, bgfill.ptr) != 1) |     if (mc.PixelSetColor(bg, bgfill.ptr) != 1) | ||||||
|  |  | ||||||
							
								
								
									
										4124
									
								
								src/magick_wand.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4124
									
								
								src/magick_wand.zig
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										198
									
								
								src/main.zig
									
										
									
									
									
								
							
							
						
						
									
										198
									
								
								src/main.zig
									
										
									
									
									
								
							|  | @ -3,8 +3,6 @@ const langs = @import("lang.zig"); | ||||||
| const runners = @import("runner.zig"); | const runners = @import("runner.zig"); | ||||||
| const printer = @import("printer.zig"); | const printer = @import("printer.zig"); | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher); |  | ||||||
| 
 |  | ||||||
| test "scritcher" { | test "scritcher" { | ||||||
|     _ = @import("lang.zig"); |     _ = @import("lang.zig"); | ||||||
|     _ = @import("runner.zig"); |     _ = @import("runner.zig"); | ||||||
|  | @ -18,32 +16,18 @@ const readline = @cImport({ | ||||||
|     @cInclude("readline/history.h"); |     @cInclude("readline/history.h"); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| fn wrapInCmdList(allocator: std.mem.Allocator, cmd: *langs.Command) !langs.CommandList { | fn wrapInCmdList(allocator: *std.mem.Allocator, cmd: langs.Command) !langs.CommandList { | ||||||
|     var cmds = langs.CommandList.init(allocator); |     var cmds = langs.CommandList.init(allocator); | ||||||
|     try cmds.append(cmd); |     try cmds.append(cmd); | ||||||
|     return cmds; |     return cmds; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn copyCommandToHeap(allocator: std.mem.Allocator, command: langs.Command, comptime tag: langs.Command.Tag) !*langs.Command { | pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void { | ||||||
|     const CommandStruct = langs.Command.tagToType(tag); |     var stdout_file = try std.io.getStdOut(); | ||||||
|     const casted = command.cast(CommandStruct).?; |     const stdout = &stdout_file.outStream().stream; | ||||||
|     var heap_cmd = try allocator.create(CommandStruct); |     const scri_path = try (args_it.next(allocator) orelse @panic("expected scri path")); | ||||||
| 
 | 
 | ||||||
|     heap_cmd.* = casted.*; |     var file_read_opt: ?std.fs.File = std.fs.File.openRead(scri_path) catch |err| blk: { | ||||||
|     log.debug("casted: {}", .{casted}); |  | ||||||
|     log.debug("heap_cmd: {}", .{heap_cmd}); |  | ||||||
| 
 |  | ||||||
|     return &heap_cmd.base; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn doRepl(allocator: std.mem.Allocator, args_it: anytype) !void { |  | ||||||
|     var stdout_file = std.io.getStdOut(); |  | ||||||
|     const stdout = &stdout_file.writer(); |  | ||||||
|     const scri_path = (args_it.next() orelse @panic("expected scri path")); |  | ||||||
|     errdefer allocator.free(scri_path); |  | ||||||
|     defer allocator.free(scri_path); |  | ||||||
| 
 |  | ||||||
|     var file_read_opt: ?std.fs.File = std.fs.cwd().openFile(scri_path, .{}) catch |err| blk: { |  | ||||||
|         if (err == error.FileNotFound) break :blk null; |         if (err == error.FileNotFound) break :blk null; | ||||||
|         return err; |         return err; | ||||||
|     }; |     }; | ||||||
|  | @ -56,47 +40,42 @@ pub fn doRepl(allocator: std.mem.Allocator, args_it: anytype) !void { | ||||||
|     defer lang.deinit(); |     defer lang.deinit(); | ||||||
| 
 | 
 | ||||||
|     if (total_bytes > 0) { |     if (total_bytes > 0) { | ||||||
|         const scri_existing = try allocator.alloc(u8, total_bytes); |         // this MUST BE long lived (a reference to it is kept inside | ||||||
|  |         // existing_cmds, and then passed along to cmds), | ||||||
|  |         // we can't defer them here | ||||||
|  |         var scri_existing = try allocator.alloc(u8, total_bytes); | ||||||
|         _ = try file_read_opt.?.read(scri_existing); |         _ = try file_read_opt.?.read(scri_existing); | ||||||
|         defer allocator.free(scri_existing); |  | ||||||
| 
 | 
 | ||||||
|         // we can't defer this directly because we copy the |         // we can defer this because we copy the Command structs back to cmds | ||||||
|         // Command pointers to the cmds list. running deinit() directly |  | ||||||
|         // would cause those pointers to be freed. |  | ||||||
|         var existing_cmds = try lang.parse(scri_existing); |         var existing_cmds = try lang.parse(scri_existing); | ||||||
|         defer existing_cmds.list.deinit(); |         defer existing_cmds.deinit(); | ||||||
| 
 | 
 | ||||||
|         // copy the existing command list into the repl's command list |         // copy the existing command list into the repl's command list | ||||||
|         for (existing_cmds.list.items) |existing_cmd| { |         var it = existing_cmds.iterator(); | ||||||
|  |         while (it.next()) |existing_cmd| { | ||||||
|             try cmds.append(existing_cmd); |             try cmds.append(existing_cmd); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         // if there isn't any commands on the file, we load our default |         // if there isn't any commands on the file, we load our default | ||||||
|         // 'load :0' command |         // 'load :0' command | ||||||
|  |         var loadargs = langs.ArgList.init(allocator); | ||||||
|  |         try loadargs.append(":0"); | ||||||
| 
 | 
 | ||||||
|         // TODO: deliberate memleak here. we only allocate this |         try cmds.append(langs.Command{ | ||||||
|         // command once, for the start of the file, so. |             .command = .Load, | ||||||
|         var load_cmd = try allocator.create(langs.Command.Load); |             .args = loadargs, | ||||||
|         load_cmd.path = ":0"; |         }); | ||||||
|         load_cmd.base.tag = langs.Command.Tag.load; |  | ||||||
| 
 |  | ||||||
|         // taking address is fine, because load_cmd lives in the lifetime |  | ||||||
|         // of the allocator. |  | ||||||
|         try cmds.append(&load_cmd.base); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (file_read_opt) |file_read| { |     if (file_read_opt) |file_read| { | ||||||
|         file_read.close(); |         file_read.close(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var file = try std.fs.cwd().createFile(scri_path, .{ |     var file = try std.fs.File.openWrite(scri_path); | ||||||
|         .read = false, |  | ||||||
|         .truncate = true, |  | ||||||
|     }); |  | ||||||
|     defer file.close(); |     defer file.close(); | ||||||
| 
 | 
 | ||||||
|     var out = file.writer(); |     var out = file.outStream(); | ||||||
|     const stream = &out; |     var stream = &out.stream; | ||||||
| 
 | 
 | ||||||
|     // since we opened the file for writing, it becomes empty, so, to ensure |     // since we opened the file for writing, it becomes empty, so, to ensure | ||||||
|     // we don't fuck up later on, we print cmds before starting the repl |     // we don't fuck up later on, we print cmds before starting the repl | ||||||
|  | @ -120,72 +99,31 @@ pub fn doRepl(allocator: std.mem.Allocator, args_it: anytype) !void { | ||||||
|     // run the load command |     // run the load command | ||||||
|     try runner.runCommands(cmds, true); |     try runner.runCommands(cmds, true); | ||||||
| 
 | 
 | ||||||
|     const wanted_runner: []const u8 = std.posix.getenv("SCRITCHER_RUNNER") orelse "ristretto"; |     var runqs_args = langs.ArgList.init(allocator); | ||||||
|  |     defer runqs_args.deinit(); | ||||||
| 
 | 
 | ||||||
|     var runqs_cmd = langs.Command.RunQS{ |     // TODO change the runqs command to something given in an env var | ||||||
|         .base = langs.Command{ .tag = langs.Command.Tag.runqs }, |     try runqs_args.append("ristretto"); | ||||||
|         .program = wanted_runner, |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     while (true) { |     while (true) { | ||||||
|         lang.reset(); |         lang.reset(); | ||||||
| 
 | 
 | ||||||
|         var rd_line = readline.readline("> "); |         var rd_line = readline.readline(c"> "); | ||||||
|         if (rd_line == null) { |         if (rd_line == null) { | ||||||
|             log.debug("leaving from eof", .{}); |             std.debug.warn("leaving from eof\n"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         readline.add_history(rd_line); |         readline.add_history(rd_line); | ||||||
|         //defer std.heap.c_allocator.destroy(rd_line); |         //defer std.heap.c_allocator.destroy(rd_line); | ||||||
| 
 | 
 | ||||||
|         const line = rd_line[0..std.mem.len(rd_line)]; |         var line = rd_line[0..std.mem.len(u8, rd_line)]; | ||||||
| 
 | 
 | ||||||
|         if (std.mem.eql(u8, line, "push")) { |         if (std.mem.eql(u8, line, "push")) { | ||||||
|             const heap_cmd = switch (current.tag) { |             try cmds.append(current); | ||||||
|                 .noop => try copyCommandToHeap(allocator, current, .noop), |  | ||||||
|                 .load => try copyCommandToHeap(allocator, current, .load), |  | ||||||
|                 .quicksave => try copyCommandToHeap(allocator, current, .quicksave), |  | ||||||
|                 .runqs => try copyCommandToHeap(allocator, current, .runqs), |  | ||||||
|                 .amp => try copyCommandToHeap(allocator, current, .amp), |  | ||||||
|                 .rflanger => try copyCommandToHeap(allocator, current, .rflanger), |  | ||||||
|                 .eq => try copyCommandToHeap(allocator, current, .eq), |  | ||||||
|                 .phaser => try copyCommandToHeap(allocator, current, .phaser), |  | ||||||
|                 .mbeq => try copyCommandToHeap(allocator, current, .mbeq), |  | ||||||
|                 .chorus => try copyCommandToHeap(allocator, current, .chorus), |  | ||||||
|                 .pitchscaler => try copyCommandToHeap(allocator, current, .pitchscaler), |  | ||||||
|                 .reverb => try copyCommandToHeap(allocator, current, .reverb), |  | ||||||
|                 .highpass => try copyCommandToHeap(allocator, current, .highpass), |  | ||||||
|                 .delay => try copyCommandToHeap(allocator, current, .delay), |  | ||||||
|                 .vinyl => try copyCommandToHeap(allocator, current, .vinyl), |  | ||||||
|                 .revdelay => try copyCommandToHeap(allocator, current, .revdelay), |  | ||||||
|                 .gate => try copyCommandToHeap(allocator, current, .gate), |  | ||||||
|                 .detune => try copyCommandToHeap(allocator, current, .detune), |  | ||||||
|                 .overdrive => try copyCommandToHeap(allocator, current, .overdrive), |  | ||||||
|                 .degrade => try copyCommandToHeap(allocator, current, .degrade), |  | ||||||
|                 .repsycho => try copyCommandToHeap(allocator, current, .repsycho), |  | ||||||
|                 .talkbox => try copyCommandToHeap(allocator, current, .talkbox), |  | ||||||
|                 .dyncomp => try copyCommandToHeap(allocator, current, .dyncomp), |  | ||||||
|                 .thruzero => try copyCommandToHeap(allocator, current, .thruzero), |  | ||||||
|                 .foverdrive => try copyCommandToHeap(allocator, current, .foverdrive), |  | ||||||
|                 .gverb => try copyCommandToHeap(allocator, current, .gverb), |  | ||||||
|                 .invert => try copyCommandToHeap(allocator, current, .invert), |  | ||||||
|                 .tapedelay => try copyCommandToHeap(allocator, current, .tapedelay), |  | ||||||
|                 .moddelay => try copyCommandToHeap(allocator, current, .moddelay), |  | ||||||
|                 .multichorus => try copyCommandToHeap(allocator, current, .multichorus), |  | ||||||
|                 .saturator => try copyCommandToHeap(allocator, current, .saturator), |  | ||||||
|                 .vintagedelay => try copyCommandToHeap(allocator, current, .vintagedelay), |  | ||||||
|                 .noise => try copyCommandToHeap(allocator, current, .noise), |  | ||||||
|                 .wildnoise => try copyCommandToHeap(allocator, current, .wildnoise), |  | ||||||
|                 .write => try copyCommandToHeap(allocator, current, .write), |  | ||||||
|                 .embed => try copyCommandToHeap(allocator, current, .embed), |  | ||||||
|                 .rotate => try copyCommandToHeap(allocator, current, .rotate), |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             try cmds.append(heap_cmd); |  | ||||||
| 
 | 
 | ||||||
|             // run the current added command to main cmds list |             // run the current added command to main cmds list | ||||||
|             // with the main parent runner |             // with the main parent runner | ||||||
|             var cmds_wrapped = try wrapInCmdList(allocator, heap_cmd); |             var cmds_wrapped = try wrapInCmdList(allocator, current); | ||||||
|             defer cmds_wrapped.deinit(); |             defer cmds_wrapped.deinit(); | ||||||
|             try runner.runCommands(cmds_wrapped, true); |             try runner.runCommands(cmds_wrapped, true); | ||||||
| 
 | 
 | ||||||
|  | @ -200,20 +138,17 @@ pub fn doRepl(allocator: std.mem.Allocator, args_it: anytype) !void { | ||||||
|             try printer.printList(cmds, stream); |             try printer.printList(cmds, stream); | ||||||
|             continue; |             continue; | ||||||
|         } else if (std.mem.eql(u8, line, "quit") or std.mem.eql(u8, line, "q")) { |         } else if (std.mem.eql(u8, line, "quit") or std.mem.eql(u8, line, "q")) { | ||||||
|             log.debug("leaving", .{}); |             std.debug.warn("leaving\n"); | ||||||
|             break; |             break; | ||||||
|         } else if (std.mem.startsWith(u8, line, "#")) { |         } else if (std.mem.startsWith(u8, line, "#")) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var cmds_parsed = lang.parse(line) catch |err| { |         var cmds_parsed = lang.parse(line) catch |err| { | ||||||
|             log.debug("repl: error while parsing: {}", .{err}); |             std.debug.warn("repl: error while parsing: {}\n", err); | ||||||
|             continue; |             continue; | ||||||
|         }; |         }; | ||||||
| 
 |         current = cmds_parsed.at(0); | ||||||
|         // no command? ignore! |  | ||||||
|         if (cmds_parsed.list.items.len == 0) continue; |  | ||||||
|         current = cmds_parsed.list.items[0].*; |  | ||||||
| 
 | 
 | ||||||
|         // by cloning the parent runner, we can iteratively write |         // by cloning the parent runner, we can iteratively write | ||||||
|         // whatever command we want and only commit the good results |         // whatever command we want and only commit the good results | ||||||
|  | @ -221,38 +156,43 @@ pub fn doRepl(allocator: std.mem.Allocator, args_it: anytype) !void { | ||||||
|         var runner_clone = try runner.clone(); |         var runner_clone = try runner.clone(); | ||||||
|         defer runner_clone.deinit(); |         defer runner_clone.deinit(); | ||||||
| 
 | 
 | ||||||
|         // taking address is fine, because runqs_cmd lives in the lifetime |         try cmds_parsed.append(langs.Command{ | ||||||
|         // of this function. |             .command = .RunQS, | ||||||
|         try cmds_parsed.append(&runqs_cmd.base); |             .args = runqs_args, | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         try runner_clone.runCommands(cmds_parsed, true); |         try runner_clone.runCommands(cmds_parsed, true); | ||||||
|         _ = try stdout.write("\n"); |         try stdout.write("\n"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn doHelp() void { | pub fn main() !void { | ||||||
|     log.debug("scritcher!", .{}); |     const allocator = std.heap.direct_allocator; | ||||||
|     log.debug("usage: scritcher [run|help|repl]", .{}); |  | ||||||
|     log.debug("\tscritcher run path_to_script.scri path_to_input_file.bmp", .{}); |  | ||||||
|     log.debug("\tscritcher repl path_to_script.scri path_to_input_file.bmp", .{}); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| fn doRun(allocator: std.mem.Allocator, args_it: anytype) !void { |  | ||||||
|     var lang = langs.Lang.init(allocator); |     var lang = langs.Lang.init(allocator); | ||||||
|     defer lang.deinit(); |     defer lang.deinit(); | ||||||
| 
 | 
 | ||||||
|     var runner = runners.Runner.init(allocator, false); |     var runner = runners.Runner.init(allocator, false); | ||||||
|     defer runner.deinit(); |     defer runner.deinit(); | ||||||
| 
 | 
 | ||||||
|     const scri_path = (args_it.next() orelse @panic("run: expected scri path")); |     var args_it = std.process.args(); | ||||||
| 
 | 
 | ||||||
|     var file = try std.fs.cwd().openFile(scri_path, .{}); |     // TODO print help | ||||||
|  | 
 | ||||||
|  |     _ = try (args_it.next(allocator) orelse @panic("expected exe name")); | ||||||
|  |     const scri_path = try (args_it.next(allocator) orelse @panic("expected scri path")); | ||||||
|  | 
 | ||||||
|  |     if (std.mem.eql(u8, scri_path, "repl")) { | ||||||
|  |         return try doRepl(allocator, &args_it); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var file = try std.fs.File.openRead(scri_path); | ||||||
|     defer file.close(); |     defer file.close(); | ||||||
| 
 | 
 | ||||||
|     // sadly, we read it all into memory. such is life |     // sadly, we read it all into memory. such is life | ||||||
|     const total_bytes = try file.getEndPos(); |     const total_bytes = try file.getEndPos(); | ||||||
| 
 | 
 | ||||||
|     const data = try allocator.alloc(u8, total_bytes); |     var data = try allocator.alloc(u8, total_bytes); | ||||||
|     defer allocator.free(data); |     defer allocator.free(data); | ||||||
|     _ = try file.read(data); |     _ = try file.read(data); | ||||||
| 
 | 
 | ||||||
|  | @ -261,33 +201,3 @@ fn doRun(allocator: std.mem.Allocator, args_it: anytype) !void { | ||||||
| 
 | 
 | ||||||
|     try runner.runCommands(cmds, true); |     try runner.runCommands(cmds, true); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| pub fn main() !void { |  | ||||||
|     var allocator_instance = std.heap.GeneralPurposeAllocator(.{}){}; |  | ||||||
|     defer { |  | ||||||
|         _ = allocator_instance.deinit(); |  | ||||||
|     } |  | ||||||
|     const allocator = allocator_instance.allocator(); |  | ||||||
| 
 |  | ||||||
|     var args_it = try std.process.argsWithAllocator(allocator); |  | ||||||
|     defer args_it.deinit(); |  | ||||||
| 
 |  | ||||||
|     _ = args_it.skip(); |  | ||||||
|     const cli_command_opt = args_it.next(); |  | ||||||
|     if (cli_command_opt == null) { |  | ||||||
|         return doHelp(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const cli_command = cli_command_opt.?; |  | ||||||
| 
 |  | ||||||
|     if (std.mem.eql(u8, cli_command, "help")) { |  | ||||||
|         return doHelp(); |  | ||||||
|     } else if (std.mem.eql(u8, cli_command, "repl")) { |  | ||||||
|         return try doRepl(allocator, &args_it); |  | ||||||
|     } else if (std.mem.eql(u8, cli_command, "run")) { |  | ||||||
|         return try doRun(allocator, &args_it); |  | ||||||
|     } else { |  | ||||||
|         log.debug("unknown command: '{s}'", .{cli_command}); |  | ||||||
|         return error.UnknownCommand; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ const std = @import("std"); | ||||||
| const lv2 = @import("lv2_helpers.zig"); | const lv2 = @import("lv2_helpers.zig"); | ||||||
| const c = lv2.c; | const c = lv2.c; | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher_plugin); |  | ||||||
| const ImageError = @import("image.zig").ImageError; | const ImageError = @import("image.zig").ImageError; | ||||||
| 
 | 
 | ||||||
| /// Control port | /// Control port | ||||||
|  | @ -36,7 +35,7 @@ pub const Position = struct { | ||||||
| 
 | 
 | ||||||
|     pub fn seekPos(self: Position, total_size: usize) SeekPos { |     pub fn seekPos(self: Position, total_size: usize) SeekPos { | ||||||
|         std.debug.assert(self.index <= self.split); |         std.debug.assert(self.index <= self.split); | ||||||
|         const tot = total_size / self.split; |         var tot = total_size / self.split; | ||||||
| 
 | 
 | ||||||
|         return SeekPos{ |         return SeekPos{ | ||||||
|             .start = self.index * tot, |             .start = self.index * tot, | ||||||
|  | @ -47,7 +46,7 @@ pub const Position = struct { | ||||||
| 
 | 
 | ||||||
| /// Represents the starting context for a single plugin run. | /// Represents the starting context for a single plugin run. | ||||||
| pub const Context = struct { | pub const Context = struct { | ||||||
|     allocator: std.mem.Allocator, |     allocator: *std.mem.Allocator, | ||||||
|     world: *c.LilvWorld, |     world: *c.LilvWorld, | ||||||
|     plugin: *const c.LilvPlugin, |     plugin: *const c.LilvPlugin, | ||||||
| 
 | 
 | ||||||
|  | @ -62,11 +61,29 @@ pub const Context = struct { | ||||||
| 
 | 
 | ||||||
| /// Specific run context for non-plugins. | /// Specific run context for non-plugins. | ||||||
| pub const RunBuffers = struct { | pub const RunBuffers = struct { | ||||||
|     // we use [2]f32 to account for stereo plugins, however |     allocator: *std.mem.Allocator, | ||||||
|     // we only use in_buf[0] and out_buf[0], and don't use the | 
 | ||||||
|  |     in: []f32, | ||||||
|  |     out: []f32, | ||||||
|  | 
 | ||||||
|  |     pub fn init(allocator: *std.mem.Allocator) !RunBuffers { | ||||||
|  |         // we allocate []f32 with size 2 to account for stereo plugins, however | ||||||
|  |         // we only use &in_buf[0] and &out_buf[0], and don't use the | ||||||
|         // (supposedly) right side of neither input or output. |         // (supposedly) right side of neither input or output. | ||||||
|     in: [2]f32 = [_]f32{0} ** 2, |         var in_buf = try allocator.alloc(f32, 2); | ||||||
|     out: [2]f32 = [_]f32{0} ** 2, |         std.mem.secureZero(f32, in_buf); | ||||||
|  | 
 | ||||||
|  |         return RunBuffers{ | ||||||
|  |             .allocator = allocator, | ||||||
|  |             .in = in_buf, | ||||||
|  |             .out = try allocator.alloc(f32, 2), | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn deinit(self: *RunBuffers) void { | ||||||
|  |         self.allocator.free(self.in); | ||||||
|  |         self.allocator.free(self.out); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Represents the specific run context of plugin instantation. | /// Represents the specific run context of plugin instantation. | ||||||
|  | @ -75,12 +92,10 @@ pub const RunContext = struct { | ||||||
|     instance: *c.LilvInstance, |     instance: *c.LilvInstance, | ||||||
| 
 | 
 | ||||||
|     pub fn init( |     pub fn init( | ||||||
|         allocator: std.mem.Allocator, |         allocator: *std.mem.Allocator, | ||||||
|         plugin: *const c.LilvPlugin, |         plugin: *const c.LilvPlugin, | ||||||
|     ) !RunContext { |     ) !RunContext { | ||||||
|         _ = allocator; // TODO batch RunBuffers? |         var instance = c.lilv_plugin_instantiate(plugin, f64(44100), null); | ||||||
| 
 |  | ||||||
|         const instance = c.lilv_plugin_instantiate(plugin, @as(f64, 44100), null); |  | ||||||
|         errdefer c.lilv_instance_free(instance); |         errdefer c.lilv_instance_free(instance); | ||||||
| 
 | 
 | ||||||
|         if (instance == null) { |         if (instance == null) { | ||||||
|  | @ -88,67 +103,63 @@ pub const RunContext = struct { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return RunContext{ |         return RunContext{ | ||||||
|             .buffers = RunBuffers{}, |             .buffers = try RunBuffers.init(allocator), | ||||||
|             .instance = instance.?, |             .instance = instance.?, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn deinit(self: *RunContext) void { |     pub fn deinit(self: *RunContext) void { | ||||||
|         c.lilv_instance_free(self.instance); |         c.lilv_instance_free(self.instance); | ||||||
|  |         self.buffers.deinit(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn connectPorts(self: *RunContext, ports: []lv2.Port) void { |     pub fn connectPorts(self: *RunContext, ports: []lv2.Port) void { | ||||||
|         var i: usize = 0; |         var i: usize = 0; | ||||||
|         var o: usize = 0; |         var o: usize = 0; | ||||||
| 
 | 
 | ||||||
|         for (ports, 0..) |_, p_idx| { |         var in_buf = self.buffers.in; | ||||||
|             const p = @as(u32, @intCast(p_idx)); |         var out_buf = self.buffers.out; | ||||||
|  | 
 | ||||||
|  |         for (ports) |_, p_idx| { | ||||||
|  |             var p = @intCast(u32, p_idx); | ||||||
|             var port: *lv2.Port = &ports[p_idx]; |             var port: *lv2.Port = &ports[p_idx]; | ||||||
| 
 | 
 | ||||||
|             switch (port.ptype) { |             switch (port.ptype) { | ||||||
|                 .Control => lv2.lilv_instance_connect_port(self.instance, p, &port.value), |                 .Control => lv2.lilv_instance_connect_port(self.instance, p, &port.value), | ||||||
|                 .Audio => { |                 .Audio => blk: { | ||||||
|                     if (port.is_input) { |                     if (port.is_input) { | ||||||
|                         lv2.lilv_instance_connect_port( |                         lv2.lilv_instance_connect_port(self.instance, p, &in_buf[i]); | ||||||
|                             self.instance, |  | ||||||
|                             p, |  | ||||||
|                             &self.buffers.in[i], |  | ||||||
|                         ); |  | ||||||
|                         i += 1; |                         i += 1; | ||||||
|                     } else { |                     } else { | ||||||
|                         lv2.lilv_instance_connect_port( |                         lv2.lilv_instance_connect_port(self.instance, p, &out_buf[o]); | ||||||
|                             self.instance, |  | ||||||
|                             p, |  | ||||||
|                             &self.buffers.out[o], |  | ||||||
|                         ); |  | ||||||
|                         o += 1; |                         o += 1; | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|                 // else => lv2.lilv_instance_connect_port(self.instance, p, null), |                 else => lv2.lilv_instance_connect_port(self.instance, p, null), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub fn makeContext(allocator: std.mem.Allocator, plugin_uri: []const u8) !Context { | pub fn makeContext(allocator: *std.mem.Allocator, plugin_uri: []const u8) !Context { | ||||||
|     const cstr_plugin_uri = try allocator.dupeZ(u8, plugin_uri); |     const cstr_plugin_uri = try std.cstr.addNullByte(allocator, plugin_uri); | ||||||
|     defer allocator.free(cstr_plugin_uri); |     defer allocator.free(cstr_plugin_uri); | ||||||
| 
 | 
 | ||||||
|     const world: *c.LilvWorld = c.lilv_world_new().?; |     var world: *c.LilvWorld = c.lilv_world_new().?; | ||||||
|     errdefer c.lilv_world_free(world); |     errdefer c.lilv_world_free(world); | ||||||
| 
 | 
 | ||||||
|     c.lilv_world_load_all(world); |     c.lilv_world_load_all(world); | ||||||
| 
 | 
 | ||||||
|     const uri: *c.LilvNode = c.lilv_new_uri(world, cstr_plugin_uri.ptr) orelse { |     var uri: *c.LilvNode = c.lilv_new_uri(world, cstr_plugin_uri.ptr) orelse blk: { | ||||||
|         log.debug("Invalid plugin URI <{s}>", .{plugin_uri}); |         std.debug.warn("Invalid plugin URI <{}>\n", plugin_uri); | ||||||
|         return ImageError.InvalidPlugin; |         return ImageError.InvalidPlugin; | ||||||
|     }; |     }; | ||||||
|     defer c.lilv_node_free(uri); |     defer c.lilv_node_free(uri); | ||||||
| 
 | 
 | ||||||
|     const plugins: *const c.LilvPlugins = c.lilv_world_get_all_plugins(world).?; |     const plugins: *const c.LilvPlugins = c.lilv_world_get_all_plugins(world); | ||||||
| 
 | 
 | ||||||
|     const plugin: *const c.LilvPlugin = c.lilv_plugins_get_by_uri(plugins, uri) orelse { |     var plugin: *const c.LilvPlugin = c.lilv_plugins_get_by_uri(plugins, uri) orelse blk: { | ||||||
|         log.debug("Plugin <{s}> not found", .{plugin_uri}); |         std.debug.warn("Plugin <{}> not found\n", plugin_uri); | ||||||
|         return ImageError.UnknownPlugin; |         return ImageError.UnknownPlugin; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										116
									
								
								src/printer.zig
									
										
									
									
									
								
							
							
						
						
									
										116
									
								
								src/printer.zig
									
										
									
									
									
								
							|  | @ -1,88 +1,40 @@ | ||||||
| const std = @import("std"); |  | ||||||
| const langs = @import("lang.zig"); | const langs = @import("lang.zig"); | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher_printer); | pub fn printList(list: langs.CommandList, stream: var) !void { | ||||||
|  |     for (list.toSlice()) |cmd| { | ||||||
|  |         var command = switch (cmd.command) { | ||||||
|  |             .Noop => "noop", | ||||||
|  |             .Load => "load", | ||||||
|  |             .Quicksave => "quicksave", | ||||||
|  |             .RunQS => "runqs", | ||||||
| 
 | 
 | ||||||
| fn printCommandWithParams(stream: anytype, command: anytype) !void { |             .Amp => "amp", | ||||||
|     const Parameters = @TypeOf(command.parameters); |             .RFlanger => "rflanger", | ||||||
|     try stream.print(" {d} {d}", .{ command.split, command.index }); |             .Eq => "eq", | ||||||
|     inline for (@typeInfo(Parameters).Struct.fields) |field| { |             .Phaser => "phaser", | ||||||
|         if (field.type == f32 or field.type == f64) { |             .Mbeq => "mbeq", | ||||||
|             try stream.print(" {}", .{@field(command.parameters, field.name)}); |             .Chorus => "chorus", | ||||||
|         } else if (field.type == usize or field.type == u64) { |             .PitchScaler => "pitchscaler", | ||||||
|             try stream.print(" {d}", .{@field(command.parameters, field.name)}); |             .Reverb => "reverb", | ||||||
|         } else { |             .Highpass => "highpass", | ||||||
|             try stream.print(" {s}", .{@field(command.parameters, field.name)}); |             .Delay => "delay", | ||||||
|  |             .Vinyl => "vinyl", | ||||||
|  |             .RevDelay => "revdelay", | ||||||
|  | 
 | ||||||
|  |             .Noise => "noise", | ||||||
|  |             .WildNoise => "wildnoise", | ||||||
|  |             .Write => "write", | ||||||
|  |             .Embed => "embed", | ||||||
|  | 
 | ||||||
|  |             .Rotate => "rotate", | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         try stream.print("{}", command); | ||||||
|  | 
 | ||||||
|  |         for (cmd.args.toSlice()) |arg| { | ||||||
|  |             try stream.print(" {}", arg); | ||||||
|         } |         } | ||||||
|     } | 
 | ||||||
| } |         try stream.write(";\n"); | ||||||
| 
 |  | ||||||
| fn printCommand(stream: anytype, cmd: *langs.Command, comptime tag: langs.Command.Tag) !void { |  | ||||||
|     const CommandStruct = langs.Command.tagToType(tag); |  | ||||||
|     const casted = cmd.cast(CommandStruct).?; |  | ||||||
| 
 |  | ||||||
|     const ctype = CommandStruct.command_type; |  | ||||||
|     switch (ctype) { |  | ||||||
|         .lv2_command => try printCommandWithParams(stream, casted), |  | ||||||
|         .custom_command => try printCommandWithParams(stream, casted), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn printList(list: langs.CommandList, stream: anytype) !void { |  | ||||||
|     for (list.list.items) |cmd| { |  | ||||||
|         const command = @tagName(cmd.tag); |  | ||||||
|         try stream.print("{s}", .{command}); |  | ||||||
| 
 |  | ||||||
|         switch (cmd.tag) { |  | ||||||
|             .load => { |  | ||||||
|                 const load = cmd.cast(langs.Command.Load).?; |  | ||||||
|                 try stream.print(" {s}", .{load.path}); |  | ||||||
|             }, |  | ||||||
|             .runqs => { |  | ||||||
|                 const runqs = cmd.cast(langs.Command.RunQS).?; |  | ||||||
|                 try stream.print(" {s}", .{runqs.program}); |  | ||||||
|             }, |  | ||||||
|             .noop, .quicksave => {}, |  | ||||||
|             .rotate => { |  | ||||||
|                 const rotate = cmd.cast(langs.Command.Rotate).?; |  | ||||||
|                 try stream.print(" {d} {s}", .{ rotate.deg, rotate.bgfill }); |  | ||||||
|             }, |  | ||||||
| 
 |  | ||||||
|             .amp => try printCommand(stream, cmd, .amp), |  | ||||||
|             .rflanger => try printCommand(stream, cmd, .rflanger), |  | ||||||
|             .eq => try printCommand(stream, cmd, .eq), |  | ||||||
|             .phaser => try printCommand(stream, cmd, .phaser), |  | ||||||
|             .mbeq => try printCommand(stream, cmd, .mbeq), |  | ||||||
|             .chorus => try printCommand(stream, cmd, .chorus), |  | ||||||
|             .pitchscaler => try printCommand(stream, cmd, .pitchscaler), |  | ||||||
|             .reverb => try printCommand(stream, cmd, .reverb), |  | ||||||
|             .highpass => try printCommand(stream, cmd, .highpass), |  | ||||||
|             .delay => try printCommand(stream, cmd, .delay), |  | ||||||
|             .vinyl => try printCommand(stream, cmd, .vinyl), |  | ||||||
|             .revdelay => try printCommand(stream, cmd, .revdelay), |  | ||||||
|             .gate => try printCommand(stream, cmd, .gate), |  | ||||||
|             .detune => try printCommand(stream, cmd, .detune), |  | ||||||
|             .overdrive => try printCommand(stream, cmd, .overdrive), |  | ||||||
|             .degrade => try printCommand(stream, cmd, .degrade), |  | ||||||
|             .repsycho => try printCommand(stream, cmd, .repsycho), |  | ||||||
|             .talkbox => try printCommand(stream, cmd, .talkbox), |  | ||||||
|             .dyncomp => try printCommand(stream, cmd, .dyncomp), |  | ||||||
|             .thruzero => try printCommand(stream, cmd, .thruzero), |  | ||||||
|             .foverdrive => try printCommand(stream, cmd, .foverdrive), |  | ||||||
|             .gverb => try printCommand(stream, cmd, .gverb), |  | ||||||
|             .invert => try printCommand(stream, cmd, .invert), |  | ||||||
|             .tapedelay => try printCommand(stream, cmd, .tapedelay), |  | ||||||
|             .moddelay => try printCommand(stream, cmd, .moddelay), |  | ||||||
|             .multichorus => try printCommand(stream, cmd, .multichorus), |  | ||||||
|             .saturator => try printCommand(stream, cmd, .saturator), |  | ||||||
|             .vintagedelay => try printCommand(stream, cmd, .vintagedelay), |  | ||||||
| 
 |  | ||||||
|             .noise => try printCommand(stream, cmd, .noise), |  | ||||||
|             .wildnoise => try printCommand(stream, cmd, .wildnoise), |  | ||||||
|             .write => try printCommand(stream, cmd, .write), |  | ||||||
|             .embed => try printCommand(stream, cmd, .embed), |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         _ = try stream.write(";\n"); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										474
									
								
								src/runner.zig
									
										
									
									
									
								
							
							
						
						
									
										474
									
								
								src/runner.zig
									
										
									
									
									
								
							|  | @ -5,8 +5,6 @@ const plugin = @import("plugin.zig"); | ||||||
| const custom = @import("custom.zig"); | const custom = @import("custom.zig"); | ||||||
| const magick = @import("magick.zig"); | const magick = @import("magick.zig"); | ||||||
| 
 | 
 | ||||||
| const log = std.log.scoped(.scritcher_runner); |  | ||||||
| 
 |  | ||||||
| const Position = plugin.Position; | const Position = plugin.Position; | ||||||
| const ParamList = plugin.ParamList; | const ParamList = plugin.ParamList; | ||||||
| const ParamMap = plugin.ParamMap; | const ParamMap = plugin.ParamMap; | ||||||
|  | @ -20,21 +18,18 @@ pub const RunError = error{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub const Runner = struct { | pub const Runner = struct { | ||||||
|     allocator: std.mem.Allocator, |     allocator: *std.mem.Allocator, | ||||||
| 
 | 
 | ||||||
|     /// The currently opened image in the runner |     /// The currently opened image in the runner | ||||||
|     image: ?*Image = null, |     image: ?*Image = null, | ||||||
| 
 | 
 | ||||||
|     /// If the runner is in REPL mode |     /// If the runner is in REPL mode | ||||||
|     repl: bool, |     repl: bool = false, | ||||||
| 
 | 
 | ||||||
|     args: []const [:0]u8, |     pub fn init(allocator: *std.mem.Allocator, repl: bool) Runner { | ||||||
| 
 |  | ||||||
|     pub fn init(allocator: std.mem.Allocator, repl: bool) Runner { |  | ||||||
|         return Runner{ |         return Runner{ | ||||||
|             .allocator = allocator, |             .allocator = allocator, | ||||||
|             .repl = repl, |             .repl = repl, | ||||||
|             .args = std.process.argsAlloc(allocator) catch unreachable, |  | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -42,59 +37,50 @@ pub const Runner = struct { | ||||||
|         if (self.image) |image| { |         if (self.image) |image| { | ||||||
|             image.close(); |             image.close(); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         std.process.argsFree(self.allocator, self.args); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn clone(self: *Runner) !Runner { |     pub fn clone(self: *Runner) !Runner { | ||||||
|         const cloned_image = if (self.image) |image| try image.clone() else null; |         var cloned_image = if (self.image) |image| try image.clone() else null; | ||||||
|         return Runner{ |         return Runner{ .allocator = self.allocator, .image = cloned_image }; | ||||||
|             .allocator = self.allocator, |  | ||||||
|             .image = cloned_image, |  | ||||||
|             .repl = self.repl, |  | ||||||
|             .args = self.args, |  | ||||||
|         }; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn resolveArg(self: *Runner, load_path: []const u8) ![]const u8 { |     fn resolveArg(self: *Runner, load_path: []const u8) ![]const u8 { | ||||||
|         std.debug.assert(load_path.len > 0); |  | ||||||
|         if (load_path[0] == ':') { |         if (load_path[0] == ':') { | ||||||
|             // parse the index from 1 to end |             // parse the index from 1 to end | ||||||
|             var index = try std.fmt.parseInt(usize, load_path[1..], 10); |             var index = try std.fmt.parseInt(usize, load_path[1..], 10); | ||||||
| 
 | 
 | ||||||
|             // if it isn't in the repl, args look like this: |             // don't care about the 'repl' being prepended when we're in repl | ||||||
|             // 'scritcher ./script ./image' |             if (self.repl) index += 1; | ||||||
|             // if it is, it looks like this |  | ||||||
|             // 'scritcher repl ./script ./image' |  | ||||||
| 
 | 
 | ||||||
|             // ':0' should ALWAYS point to the image. |             var args_it = std.process.args(); | ||||||
|             if (self.repl) index += 3 else index += 3; |             _ = args_it.skip(); | ||||||
| 
 | 
 | ||||||
|             for (self.args, 0..) |arg, idx| { |             var i: usize = 0; | ||||||
|                 log.debug("arg{d} = {s}", .{ idx, arg }); |             while (i <= index) : (i += 1) { | ||||||
|  |                 _ = args_it.skip(); | ||||||
|             } |             } | ||||||
|             log.debug("fetch arg idx={d}", .{index}); | 
 | ||||||
|             log.debug("fetch arg val={s}", .{self.args[index]}); |             const arg = try (args_it.next(self.allocator) orelse @panic("expected argument")); | ||||||
|             return self.args[index]; | 
 | ||||||
|  |             return arg; | ||||||
|         } else { |         } else { | ||||||
|             return load_path; |             return load_path; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Caller owns returned memory. |  | ||||||
|     fn resolveArgPath(self: *Runner, path_or_argidx: []const u8) ![]const u8 { |     fn resolveArgPath(self: *Runner, path_or_argidx: []const u8) ![]const u8 { | ||||||
|         const path = try self.resolveArg(path_or_argidx); |         const path = try self.resolveArg(path_or_argidx); | ||||||
|         const resolved_path = try std.fs.path.resolve( |         const resolved_path = try std.fs.path.resolve( | ||||||
|             self.allocator, |             self.allocator, | ||||||
|             &[_][]const u8{path}, |             [_][]const u8{path}, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         return resolved_path; |         return resolved_path; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn loadCmd(self: *Runner, path_or_argidx: []const u8) !void { |     fn loadCmd(self: *Runner, path_or_argidx: []const u8) !void { | ||||||
|         const load_path = try self.resolveArgPath(path_or_argidx); |         var load_path = try self.resolveArgPath(path_or_argidx); | ||||||
|         log.debug("\tload path: {s}", .{load_path}); |         std.debug.warn("\tload path: {}\n", load_path); | ||||||
| 
 | 
 | ||||||
|         // we could use ImageMagick to convert from X to BMP |         // we could use ImageMagick to convert from X to BMP | ||||||
|         // but i can't find an easy way to do things in memory. |         // but i can't find an easy way to do things in memory. | ||||||
|  | @ -103,15 +89,14 @@ pub const Runner = struct { | ||||||
|         // before loading the file into scritcher. for example, you can start |         // before loading the file into scritcher. for example, you can start | ||||||
|         // krita/gimp and make it export a bmp and while in the program you can |         // krita/gimp and make it export a bmp and while in the program you can | ||||||
|         // apply filters, etc. |         // apply filters, etc. | ||||||
|         if (!std.mem.endsWith(u8, load_path, ".bmp") and !std.mem.endsWith(u8, load_path, ".ppm")) { |         if (!std.mem.endsWith(u8, load_path, ".bmp")) { | ||||||
|             log.debug("Only BMP files are allowed to be loaded. Got path '{s}'", .{load_path}); |             std.debug.warn("Only BMP files are allowed to be loaded.\n"); | ||||||
|             return RunError.NoBMP; |             return RunError.NoBMP; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // we don't copy load_path into a temporary file because we're already |         // we don't copy load_path into a temporary file because we're already | ||||||
|         // loading it under the SFM_READ mode, which won't cause any destructive |         // loading it under the SFM_READ mode, which won't cause any destructive | ||||||
|         // operations on the file. |         // operations on the file. | ||||||
|         if (self.image) |image| image.close(); |  | ||||||
|         self.image = try Image.open(self.allocator, load_path); |         self.image = try Image.open(self.allocator, load_path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -119,21 +104,20 @@ pub const Runner = struct { | ||||||
|         if (self.image) |image| { |         if (self.image) |image| { | ||||||
|             return image; |             return image; | ||||||
|         } else { |         } else { | ||||||
|             log.debug("image is required!", .{}); |             std.debug.warn("image is required!\n"); | ||||||
|             return RunError.ImageRequired; |             return RunError.ImageRequired; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Caller owns returned memory. |  | ||||||
|     fn makeGlitchedPath(self: *Runner) ![]const u8 { |     fn makeGlitchedPath(self: *Runner) ![]const u8 { | ||||||
|         // we want to transform basename, if it is "x.bmp" to "x_gN.bmp", where |         // we want to transform basename, if it is "x.bmp" to "x_gN.bmp", where | ||||||
|         // N is the maximum non-used integer. |         // N is the maximum non-used integer. | ||||||
|         const image = try self.getImage(); |         var image = try self.getImage(); | ||||||
| 
 | 
 | ||||||
|         const basename = std.fs.path.basename(image.path); |         const basename = std.fs.path.basename(image.path); | ||||||
|         const dirname = std.fs.path.dirname(image.path).?; |         const dirname = std.fs.path.dirname(image.path).?; | ||||||
| 
 | 
 | ||||||
|         var dir = try std.fs.cwd().openDir(dirname, .{ .iterate = true }); |         var dir = try std.fs.Dir.open(self.allocator, dirname); | ||||||
|         defer dir.close(); |         defer dir.close(); | ||||||
| 
 | 
 | ||||||
|         const period_idx = std.mem.lastIndexOf(u8, basename, ".").?; |         const period_idx = std.mem.lastIndexOf(u8, basename, ".").?; | ||||||
|  | @ -141,18 +125,18 @@ pub const Runner = struct { | ||||||
| 
 | 
 | ||||||
|         // starts_with would be "x_g", we want to find all files in the directory |         // starts_with would be "x_g", we want to find all files in the directory | ||||||
|         // that start with that name. |         // that start with that name. | ||||||
|         const starts_with = try std.fmt.allocPrint(self.allocator, "{s}_g", .{ |         const starts_with = try std.fmt.allocPrint( | ||||||
|  |             self.allocator, | ||||||
|  |             "{}_g", | ||||||
|             basename[0..period_idx], |             basename[0..period_idx], | ||||||
|         }); |         ); | ||||||
|         defer self.allocator.free(starts_with); |         defer self.allocator.free(starts_with); | ||||||
| 
 | 
 | ||||||
|         var max: usize = 0; |         var max: usize = 0; | ||||||
| 
 | 
 | ||||||
|         var it = dir.iterate(); |         while (try dir.next()) |entry| { | ||||||
| 
 |  | ||||||
|         while (try it.next()) |entry| { |  | ||||||
|             switch (entry.kind) { |             switch (entry.kind) { | ||||||
|                 .file => blk: { |                 .File => blk: { | ||||||
|                     if (!std.mem.startsWith(u8, entry.name, starts_with)) break :blk {}; |                     if (!std.mem.startsWith(u8, entry.name, starts_with)) break :blk {}; | ||||||
| 
 | 
 | ||||||
|                     // we want to get the N in x_gN.ext |                     // we want to get the N in x_gN.ext | ||||||
|  | @ -166,8 +150,6 @@ pub const Runner = struct { | ||||||
|                     // if N isn't a number, we just ignore that file |                     // if N isn't a number, we just ignore that file | ||||||
|                     const idx_str = entry.name[entry_gidx + 2 .. entry_pidx]; |                     const idx_str = entry.name[entry_gidx + 2 .. entry_pidx]; | ||||||
|                     const idx = std.fmt.parseInt(usize, idx_str, 10) catch |err| { |                     const idx = std.fmt.parseInt(usize, idx_str, 10) catch |err| { | ||||||
|                         log.debug("ignoring file {s}", .{@errorName(err)}); |  | ||||||
| 
 |  | ||||||
|                         break :blk {}; |                         break :blk {}; | ||||||
|                     }; |                     }; | ||||||
| 
 | 
 | ||||||
|  | @ -177,12 +159,14 @@ pub const Runner = struct { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const out_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}{d}{s}", .{ |         const out_path = try std.fmt.allocPrint( | ||||||
|  |             self.allocator, | ||||||
|  |             "{}/{}{}{}", | ||||||
|             dirname, |             dirname, | ||||||
|             starts_with, |             starts_with, | ||||||
|             max + 1, |             max + 1, | ||||||
|             extension, |             extension, | ||||||
|         }); |         ); | ||||||
| 
 | 
 | ||||||
|         return out_path; |         return out_path; | ||||||
|     } |     } | ||||||
|  | @ -190,155 +174,349 @@ pub const Runner = struct { | ||||||
|     fn quicksaveCmd(self: *Runner) !void { |     fn quicksaveCmd(self: *Runner) !void { | ||||||
|         var image = try self.getImage(); |         var image = try self.getImage(); | ||||||
|         const out_path = try self.makeGlitchedPath(); |         const out_path = try self.makeGlitchedPath(); | ||||||
|         defer self.allocator.free(out_path); |  | ||||||
|         try image.saveTo(out_path); |         try image.saveTo(out_path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn runQSCmd(self: *Runner, cmd: *lang.Command) !void { |     fn runQSCmd(self: *Runner, program: []const u8) !void { | ||||||
|         const runqs = cmd.cast(lang.Command.RunQS).?; |  | ||||||
|         var image = try self.getImage(); |         var image = try self.getImage(); | ||||||
|         const out_path = try self.makeGlitchedPath(); |         const out_path = try self.makeGlitchedPath(); | ||||||
|         defer self.allocator.free(out_path); |  | ||||||
|         try image.saveTo(out_path); |         try image.saveTo(out_path); | ||||||
| 
 | 
 | ||||||
|         var proc = std.ChildProcess.init( |         var proc = try std.ChildProcess.init( | ||||||
|             &[_][]const u8{ runqs.program, out_path }, |             [_][]const u8{ program, out_path }, | ||||||
|             self.allocator, |             self.allocator, | ||||||
|         ); |         ); | ||||||
|         //defer proc.deinit(); |         defer proc.deinit(); | ||||||
| 
 | 
 | ||||||
|         log.debug("running '{s} {s}'", .{ runqs.program, out_path }); |         std.debug.warn("running '{} {}'\n", program, out_path); | ||||||
|         _ = try proc.spawnAndWait(); |         _ = try proc.spawnAndWait(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn rotateCmd(self: *Runner, cmd: *lang.Command) !void { |     /// Run the http://lv2plug.in/plugins/eg-amp plugin over the file. | ||||||
|         const rotate_cmd = cmd.cast(lang.Command.Rotate).?; |     fn ampCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
| 
 |         var image = try self.getImage(); | ||||||
|         const image = try self.getImage(); |         try image.runPlugin("http://lv2plug.in/plugins/eg-amp", pos, params); | ||||||
|         const c_bgfill = try self.allocator.dupeZ(u8, rotate_cmd.bgfill); |  | ||||||
|         defer self.allocator.free(c_bgfill); |  | ||||||
| 
 |  | ||||||
|         try magick.runRotate(image, rotate_cmd.deg, c_bgfill); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn executeLV2Command(self: *@This(), command: anytype) !void { |     fn rFlangerCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|         const pos = plugin.Position{ |         var image = try self.getImage(); | ||||||
|             .split = command.split, |         try image.runPlugin("http://plugin.org.uk/swh-plugins/retroFlange", pos, params); | ||||||
|             .index = command.index, |     } | ||||||
|         }; |  | ||||||
| 
 | 
 | ||||||
|  |     fn eqCmd(self: *Runner, position: Position, params: ParamList) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runPlugin("http://plugin.org.uk/swh-plugins/dj_eq_mono", position, params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn phaserCmd(self: *Runner, position: Position, params: ParamList) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runPlugin("http://plugin.org.uk/swh-plugins/lfoPhaser", position, params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn mbeqCmd(self: *Runner, position: Position, bands: []const f32) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|         var params = ParamList.init(self.allocator); |         var params = ParamList.init(self.allocator); | ||||||
|         defer params.deinit(); |         defer params.deinit(); | ||||||
| 
 | 
 | ||||||
|         const typ = @TypeOf(command); |         for (bands) |band_value, idx| { | ||||||
| 
 |             var sym = try std.fmt.allocPrint(self.allocator, "band_{}", idx + 1); | ||||||
|         inline for (@typeInfo(@TypeOf(command.parameters)).Struct.fields) |cmd_field| { |  | ||||||
|             try params.append(plugin.Param{ |             try params.append(plugin.Param{ | ||||||
|                 .sym = cmd_field.name, |                 .sym = sym, | ||||||
|                 .value = @field(command.parameters, cmd_field.name), |                 .value = band_value, | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var image = try self.getImage(); |         try image.runPlugin("http://plugin.org.uk/swh-plugins/mbeq", position, params); | ||||||
|         try image.runPlugin(typ.lv2_url, pos, params); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn executeCustomCommand(self: *@This(), command: anytype) !void { |     fn chorusCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|         const pos = plugin.Position{ |  | ||||||
|             .split = command.split, |  | ||||||
|             .index = command.index, |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         var image = try self.getImage(); |         var image = try self.getImage(); | ||||||
|         try image.runCustomPlugin(@TypeOf(command).plugin_type, pos, command.parameters); |         try image.runPlugin("http://plugin.org.uk/swh-plugins/multivoiceChorus", pos, params); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn runSingleCommand( |     fn pitchScalerCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|         self: *@This(), |         var image = try self.getImage(); | ||||||
|         cmd: *lang.Command, |         try image.runPlugin("http://plugin.org.uk/swh-plugins/pitchScaleHQ", pos, params); | ||||||
|         comptime tag: lang.Command.Tag, |     } | ||||||
|  | 
 | ||||||
|  |     fn reverbCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runPlugin("http://invadarecords.com/plugins/lv2/erreverb/mono", pos, params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn highpassCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runPlugin("http://invadarecords.com/plugins/lv2/filter/hpf/mono", pos, params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn delayCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runPlugin("http://plugin.org.uk/swh-plugins/delayorama", pos, params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn vinylCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runPlugin("http://plugin.org.uk/swh-plugins/vynil", pos, params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn revDelayCmd(self: *Runner, pos: Position, params: ParamList) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runPlugin("http://plugin.org.uk/swh-plugins/revdelay", pos, params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn noiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runCustomPlugin(custom.RandomNoise, pos, *ParamMap, map); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn wildNoiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runCustomPlugin(custom.WildNoise, pos, *ParamMap, map); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn writeCmd(self: *Runner, pos: Position, map: *ParamMap) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runCustomPlugin(custom.Write, pos, *ParamMap, map); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn embedCmd(self: *Runner, pos: Position, path: []const u8) !void { | ||||||
|  |         var image = try self.getImage(); | ||||||
|  |         try image.runCustomPlugin(custom.Embed, pos, []const u8, path); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn rotateCmd( | ||||||
|  |         self: *Runner, | ||||||
|  |         deg: f32, | ||||||
|  |         bgfill: []const u8, | ||||||
|     ) !void { |     ) !void { | ||||||
|         const typ = lang.Command.tagToType(tag); |         var image = try self.getImage(); | ||||||
|         const command = cmd.cast(typ).?; |         var c_bgfill = try std.cstr.addNullByte(self.allocator, bgfill); | ||||||
|         const ctype = typ.command_type; |         defer self.allocator.free(c_bgfill); | ||||||
|         switch (ctype) { | 
 | ||||||
|             .lv2_command => try self.executeLV2Command(command.*), |         try magick.runRotate(image, deg, c_bgfill); | ||||||
|             .custom_command => try self.executeCustomCommand(command.*), |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn runCommand(self: *@This(), cmd: *lang.Command) !void { |     fn runCommand(self: *Runner, cmd: *lang.Command) !void { | ||||||
|         switch (cmd.tag) { |         var params = ParamList.init(self.allocator); | ||||||
|             .noop => {}, |         defer params.deinit(); | ||||||
|             .load => { | 
 | ||||||
|                 const command = cmd.cast(lang.Command.Load).?; |         var map = ParamMap.init(self.allocator); | ||||||
|                 try self.loadCmd(command.path); |         defer map.deinit(); | ||||||
|  | 
 | ||||||
|  |         return switch (cmd.command) { | ||||||
|  |             .Noop => {}, | ||||||
|  |             .Load => blk: { | ||||||
|  |                 var path = cmd.args.at(0); | ||||||
|  |                 try self.loadCmd(path); | ||||||
|  | 
 | ||||||
|  |                 // TODO is this needed? | ||||||
|  |                 break :blk; | ||||||
|             }, |             }, | ||||||
|             .quicksave => try self.quicksaveCmd(), |             .Quicksave => try self.quicksaveCmd(), | ||||||
|             .rotate => try self.rotateCmd(cmd), |             .RunQS => try self.runQSCmd(cmd.args.at(0)), | ||||||
|             .runqs => try self.runQSCmd(cmd), |  | ||||||
| 
 | 
 | ||||||
|             .amp => try self.runSingleCommand(cmd, .amp), |             .Amp => blk: { | ||||||
|             .rflanger => try self.runSingleCommand(cmd, .rflanger), |                 const pos = try cmd.consumePosition(); | ||||||
|             .eq => try self.runSingleCommand(cmd, .eq), |                 try cmd.appendParam(¶ms, "gain"); | ||||||
|             .phaser => try self.runSingleCommand(cmd, .phaser), |                 try self.ampCmd(pos, params); | ||||||
|             .mbeq => try self.runSingleCommand(cmd, .mbeq), |             }, | ||||||
|             .chorus => try self.runSingleCommand(cmd, .chorus), |  | ||||||
|             .pitchscaler => try self.runSingleCommand(cmd, .pitchscaler), |  | ||||||
|             .reverb => try self.runSingleCommand(cmd, .reverb), |  | ||||||
|             .highpass => try self.runSingleCommand(cmd, .highpass), |  | ||||||
|             .delay => try self.runSingleCommand(cmd, .delay), |  | ||||||
|             .vinyl => try self.runSingleCommand(cmd, .vinyl), |  | ||||||
|             .revdelay => try self.runSingleCommand(cmd, .revdelay), |  | ||||||
|             .gate => try self.runSingleCommand(cmd, .gate), |  | ||||||
|             .detune => try self.runSingleCommand(cmd, .detune), |  | ||||||
|             .overdrive => try self.runSingleCommand(cmd, .overdrive), |  | ||||||
|             .degrade => try self.runSingleCommand(cmd, .degrade), |  | ||||||
|             .repsycho => try self.runSingleCommand(cmd, .repsycho), |  | ||||||
|             .talkbox => try self.runSingleCommand(cmd, .talkbox), |  | ||||||
|             .dyncomp => try self.runSingleCommand(cmd, .dyncomp), |  | ||||||
|             .thruzero => try self.runSingleCommand(cmd, .thruzero), |  | ||||||
|             .foverdrive => try self.runSingleCommand(cmd, .foverdrive), |  | ||||||
|             .gverb => try self.runSingleCommand(cmd, .gverb), |  | ||||||
|             .invert => try self.runSingleCommand(cmd, .invert), |  | ||||||
|             .tapedelay => try self.runSingleCommand(cmd, .tapedelay), |  | ||||||
|             .moddelay => try self.runSingleCommand(cmd, .moddelay), |  | ||||||
|             .multichorus => try self.runSingleCommand(cmd, .multichorus), |  | ||||||
|             .saturator => try self.runSingleCommand(cmd, .saturator), |  | ||||||
|             .vintagedelay => try self.runSingleCommand(cmd, .vintagedelay), |  | ||||||
| 
 | 
 | ||||||
|             .noise => try self.runSingleCommand(cmd, .noise), |             .RFlanger => blk: { | ||||||
|             .wildnoise => try self.runSingleCommand(cmd, .wildnoise), |                 const pos = try cmd.consumePosition(); | ||||||
|             .write => try self.runSingleCommand(cmd, .write), |                 try cmd.appendParam(¶ms, "delay_depth_avg"); | ||||||
|             .embed => try self.runSingleCommand(cmd, .embed), |                 try cmd.appendParam(¶ms, "law_freq"); | ||||||
|         } |                 try self.rFlangerCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Eq => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  |                 try cmd.appendParam(¶ms, "lo"); | ||||||
|  |                 try cmd.appendParam(¶ms, "mid"); | ||||||
|  |                 try cmd.appendParam(¶ms, "hi"); | ||||||
|  | 
 | ||||||
|  |                 try self.eqCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Phaser => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParam(¶ms, "lfo_rate"); | ||||||
|  |                 try cmd.appendParam(¶ms, "lfo_depth"); | ||||||
|  |                 try cmd.appendParam(¶ms, "fb"); | ||||||
|  |                 try cmd.appendParam(¶ms, "spread"); | ||||||
|  | 
 | ||||||
|  |                 try self.phaserCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Mbeq => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  |                 const bands = try cmd.floatArgMany(self.allocator, 2, 15, f32(0)); | ||||||
|  |                 defer self.allocator.free(bands); | ||||||
|  | 
 | ||||||
|  |                 try self.mbeqCmd(pos, bands); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Chorus => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParam(¶ms, "voices"); | ||||||
|  |                 try cmd.appendParam(¶ms, "delay_base"); | ||||||
|  |                 try cmd.appendParam(¶ms, "voice_spread"); | ||||||
|  |                 try cmd.appendParam(¶ms, "detune"); | ||||||
|  |                 try cmd.appendParam(¶ms, "law_freq"); | ||||||
|  |                 try cmd.appendParam(¶ms, "attendb"); | ||||||
|  | 
 | ||||||
|  |                 try self.chorusCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .PitchScaler => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  |                 try cmd.appendParam(¶ms, "mult"); | ||||||
|  |                 try self.pitchScalerCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Reverb => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParam(¶ms, "roomLength"); | ||||||
|  |                 try cmd.appendParam(¶ms, "roomWidth"); | ||||||
|  |                 try cmd.appendParam(¶ms, "roomHeight"); | ||||||
|  |                 try cmd.appendParam(¶ms, "sourceLR"); | ||||||
|  |                 try cmd.appendParam(¶ms, "sourceFB"); | ||||||
|  |                 try cmd.appendParam(¶ms, "listLR"); | ||||||
|  |                 try cmd.appendParam(¶ms, "listFB"); | ||||||
|  |                 try cmd.appendParam(¶ms, "hpf"); | ||||||
|  |                 try cmd.appendParam(¶ms, "warmth"); | ||||||
|  |                 try cmd.appendParam(¶ms, "diffusion"); | ||||||
|  | 
 | ||||||
|  |                 try self.reverbCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Highpass => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParam(¶ms, "freq"); | ||||||
|  |                 try cmd.appendParam(¶ms, "gain"); | ||||||
|  |                 try cmd.appendParam(¶ms, "noClip"); | ||||||
|  | 
 | ||||||
|  |                 try self.highpassCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Delay => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParam(¶ms, "seed"); | ||||||
|  |                 try cmd.appendParam(¶ms, "gain"); | ||||||
|  |                 try cmd.appendParam(¶ms, "feedback_pc"); | ||||||
|  |                 try cmd.appendParam(¶ms, "tap_count"); | ||||||
|  |                 try cmd.appendParam(¶ms, "first_delay"); | ||||||
|  |                 try cmd.appendParam(¶ms, "delay_range"); | ||||||
|  |                 try cmd.appendParam(¶ms, "delay_scale"); | ||||||
|  |                 try cmd.appendParam(¶ms, "delay_rand_pc"); | ||||||
|  |                 try cmd.appendParam(¶ms, "gain_scale"); | ||||||
|  |                 try cmd.appendParam(¶ms, "wet"); | ||||||
|  | 
 | ||||||
|  |                 try self.delayCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Vinyl => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParam(¶ms, "year"); | ||||||
|  |                 try cmd.appendParam(¶ms, "rpm"); | ||||||
|  |                 try cmd.appendParam(¶ms, "warp"); | ||||||
|  |                 try cmd.appendParam(¶ms, "click"); | ||||||
|  |                 try cmd.appendParam(¶ms, "wear"); | ||||||
|  | 
 | ||||||
|  |                 try self.vinylCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .RevDelay => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParam(¶ms, "delay_time"); | ||||||
|  |                 try cmd.appendParam(¶ms, "dry_level"); | ||||||
|  |                 try cmd.appendParam(¶ms, "wet_level"); | ||||||
|  |                 try cmd.appendParam(¶ms, "feedback"); | ||||||
|  |                 try cmd.appendParam(¶ms, "xfade_samp"); | ||||||
|  | 
 | ||||||
|  |                 try self.revDelayCmd(pos, params); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Noise => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParamMap(&map, "seed"); | ||||||
|  |                 try cmd.appendParamMap(&map, "fill_bytes"); | ||||||
|  | 
 | ||||||
|  |                 try self.noiseCmd(pos, &map); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .WildNoise => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  | 
 | ||||||
|  |                 try cmd.appendParamMap(&map, "seed"); | ||||||
|  |                 try cmd.appendParamMap(&map, "fill_bytes"); | ||||||
|  | 
 | ||||||
|  |                 try self.wildNoiseCmd(pos, &map); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Write => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  |                 try cmd.appendParamMap(&map, "data"); | ||||||
|  |                 try self.writeCmd(pos, &map); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Embed => blk: { | ||||||
|  |                 const pos = try cmd.consumePosition(); | ||||||
|  |                 const path = cmd.args.at(2); | ||||||
|  |                 try self.embedCmd(pos, path); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             .Rotate => blk: { | ||||||
|  |                 const deg = try cmd.floatArgAt(0); | ||||||
|  |                 const bgfill = try cmd.argAt(1); | ||||||
|  |                 try self.rotateCmd(deg, bgfill); | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |             else => blk: { | ||||||
|  |                 std.debug.warn("Unsupported command: {}\n", cmd.command); | ||||||
|  |                 break :blk RunError.UnknownCommand; | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     /// Run a list of commands. |     /// Run a list of commands. | ||||||
|     pub fn runCommands( |     pub fn runCommands( | ||||||
|         self: *Runner, |         self: *Runner, | ||||||
|         cmds: lang.CommandList, |         cmds: lang.CommandList, | ||||||
|         debug_flag: bool, |         debug_flag: bool, | ||||||
|     ) !void { |     ) !void { | ||||||
|         _ = debug_flag; |         for (cmds.toSlice()) |const_cmd| { | ||||||
|         for (cmds.list.items) |cmd| { |             if (debug_flag) const_cmd.print(); | ||||||
|             cmd.print(); | 
 | ||||||
|  |             // copy the command so we own its memory | ||||||
|  |             var cmd = try const_cmd.copy(self.allocator); | ||||||
|  |             defer self.allocator.destroy(cmd); | ||||||
|  | 
 | ||||||
|             try self.runCommand(cmd); |             try self.runCommand(cmd); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| test "running noop" { | test "running noop" { | ||||||
|     const allocator = std.testing.allocator; |     const allocator = std.heap.direct_allocator; | ||||||
|     var cmds = lang.CommandList.init(allocator); |     var cmds = lang.CommandList.init(allocator); | ||||||
|     defer cmds.deinit(); |     defer cmds.deinit(); | ||||||
| 
 | 
 | ||||||
|     const command = lang.Command{ .tag = .noop }; |     var cmd_ptr = try allocator.create(lang.Command); | ||||||
|  |     cmd_ptr.* = lang.Command{ | ||||||
|  |         .command = .Noop, | ||||||
|  |         .args = lang.ArgList.init(allocator), | ||||||
|  |     }; | ||||||
|  |     try cmds.append(cmd_ptr); | ||||||
| 
 | 
 | ||||||
|     var noop = try allocator.create(lang.Command.Noop); |     var runner = Runner.init(allocator); | ||||||
|     noop.* = lang.Command.Noop{ .base = command }; |  | ||||||
|     try cmds.append(&noop.base); |  | ||||||
| 
 |  | ||||||
|     var runner = Runner.init(allocator, false); |  | ||||||
|     defer runner.deinit(); |     defer runner.deinit(); | ||||||
| 
 | 
 | ||||||
|     try runner.runCommands(cmds, false); |     try runner.runCommands(cmds, false); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue