pax_global_header00006660000000000000000000000064117556031170014520gustar00rootroot0000000000000052 comment=d20f15409a7c828ca744f7d8c16045eef5b85786 LICENSE000066400000000000000000000006371175560311700120770ustar00rootroot00000000000000This program is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Makefile000066400000000000000000000011341175560311700125230ustar00rootroot00000000000000CFLAGS = -O3 -Wall -Wextra --std=c99 `pkg-config --cflags argtable2 sndfile lilv-0` LDLIBS = `pkg-config --libs argtable2 sndfile lilv-0` -lm BINDIR = $(DESTDIR)/usr/bin INSTALL_PROGRAM = install all: lv2file lv2file.o: lv2file.c $(CC) -c $(CFLAGS) -o lv2file.o lv2file.c lv2file: lv2file.o $(CC) $(LDFLAGS) lv2file.o -o lv2file $(LDLIBS) tarball: lv2file cd ..;tar -czvf lv2file.tar.gz lv2file/*; .PHONY: install uninstall clean clean: rm -f lv2file.o lv2file install: all $(INSTALL_PROGRAM) -d $(BINDIR) $(DESTDIR) $(INSTALL_PROGRAM) lv2file $(BINDIR)/lv2file uninstall: rm $(BINDIR)/lv2file README000066400000000000000000000073471175560311700117570ustar00rootroot00000000000000lv2file is a simple program which you can use to apply effects to your audio files without much hassle. Possible use cases are: * When you want to apply an effect without having to open a GUI or start a project. * When you want to apply effects to a large number of files, or in an automated manner. * When you need a deterministic environment to debug a plugin. * You like everything to be on the command line. lv2file uses the LV2 plugin format (http://lv2plug.in/) for the effects it uses. ==Command Line Syntax== lv2file can be used in the following ways: lv2file --list * Lists and numbers the available plugins lv2file --nameports PLUGIN * Lists the input and control ports for plugin PLUGIN lv2file -i IFILE -o OFILE -c CHANNEL:PORT -p PORT:VALUE PLUGIN * Applies the PLUGIN to to IFILE and outputs the results to OFILE. ==Example== lv2file -c 1:voice -p pitch_correction:1 -i speechsample.wav -o outfile.wav http://hyperglitch.com/dev/VocProc Applies the "vocproc" plugin to the file speechsample.wav, outputting it to /tmp/outfile.wav. It connects the first channel of the audio to the "voice" input on the vocproc effect, and tells vocproc to set the "pitch_correction" amount to 1. lv2file -i speechsample.wav -o outfile.wav 2 Applies the 2nd plugin to speechsample.wav, outputting to outfile.wav. To get a listing of the plugins and their numbers, you can use lv2file --list. Note that the number is subject to change when plugins are installed or removed, so it should not be used to permanently identify a plugin. ==Options== ===-c=== The -c option tells lv2file to connect the channel CHANNEL in the input file to the audio port PORT of the plugin. If you connect multiple channels to the same port, they will be mixed together. The -c option is often not necessary, as lv2file will try to guess how you would like to connect the ports. It is possible to run multiple instances of a plugin using the syntax "-c 5:2.left" which, for example, would connect the fifth channel of audio to the port labeled "left" in the second copy of the plugin. You don't need to specify how many plugins to run, lv2file automatically makes enough according to the connections you make. ===-m=== There is also a -m or --mono option which will simply mix down all of the channels together and pass them to the plugin. This will only work if the plugin has only a single audio input. This is to be used instead of manually specifying connections. ===-p=== The -p option is used to pass values to the control ports of the plugin, essentially telling the effect *how* to handle the audio. The syntax is simple PORT is the name of the control port, and VALUE is the value to set it to. For example "-p volume:1" sets the effects "volume" control to 1. You should note that because lv2file uses LV2 plugins, the VALUES will always be floating point numbers. It is not possible to vary a parameter with time. It is also not possible to have different control values for multiple plugin instances. The former may be addressed at some future point, but the latter will probably never be implemented. It is outside the scope of this program. Instead, split up the channels of your audio files, and process them in batches whose parameters are all the same. ===-b=== The option -b, or --blocksize, controls the size of the chunks the audio is processed in. Larger sizes may be slightly faster, but will use more memory. The default is 512 frames. ===--ignore-clipping=== By default, lv2file will check every sample for clipping and will warn the user if any clipping occurs. However, if know that the effect won't produce clipping, or you don't care if it does, you can use this option to turn off the check for clipping. This will make lv2file run slightly faster. lv2file.c000066400000000000000000000464161175560311700126060ustar00rootroot00000000000000#include #include #include #include #include #include #include #include //From lv2_simple_jack_host in slv2 (GPL code) void list_plugins(const LilvPlugins* list) { int j=1; LILV_FOREACH(plugins,i,list) { const LilvPlugin* p=lilv_plugins_get(list,i); printf("%d\t%s\n", j++, lilv_node_as_uri(lilv_plugin_get_uri(p))); } } const LilvPlugin* plugins_get_at(const LilvPlugins* plugins, unsigned int n) { unsigned int j=0; LILV_FOREACH(plugins,i,plugins) { if(j==n) { return lilv_plugins_get(plugins,i); } j++; } return NULL; } const LilvPlugin* getplugin(const char* name, const LilvPlugins* plugins, LilvWorld* lilvworld) { int index=atoi(name); if(index!=0) { return plugins_get_at(plugins,index-1); } else { LilvNode* plugin_uri = lilv_new_uri(lilvworld, name); const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri); lilv_node_free(plugin_uri); return plugin; } } unsigned int popcount(bool* connections, unsigned int numchannels) { unsigned int result=0; for(unsigned int i=0; i1) { clipped=true; buffer[i]=1; } } for(unsigned int i=0; isval[0]); goto cleanup_listnamestable; } struct arg_rex *connectargs= arg_rexn("c","connect","(\\d+:(\\d+\\.)?\\w+,?)*",":",0,200,REG_EXTENDED,"Connect between audio file channels and plugin input channels."); struct arg_file* infile = arg_file1("i", NULL,"input", "Input sound file"); struct arg_file* outfile = arg_file1("o", NULL,"output", "Output sound file"); struct arg_rex* controls = arg_rexn("p", "parameters","(\\w+:\\w+,?)*",":",0,200,REG_EXTENDED, "Pass a value to a plugin control port."); pluginname = arg_str1(NULL,NULL,"plugin","The LV2 URI of the plugin"); struct arg_int* blksize = arg_int0("b","blocksize","","Chunk size in which the sound is processed. This is frames, not samples."); struct arg_lit* mono = arg_lit0("m","mono","Mix all of the channels together before processing."); struct arg_lit* ignore_clipping = arg_lit0(NULL,"ignore-clipping", "Do not check for clipping. This option is slightly faster"); blksize->ival[0]=512; struct arg_end *endarg = arg_end(20); void *argtable[] = {infile, outfile,controls,connectargs,blksize,mono,ignore_clipping,pluginname, endarg}; if (arg_nullcheck(argtable) != 0) { fprintf(stderr,"Error: insufficient memory\n"); goto cleanup_argtable; } int nerrors = arg_parse(argc,argv,argtable); if(nerrors) { arg_print_errors(stderr,endarg,"lv2file"); fprintf(stderr,"usage:\nlv2file\t"); arg_print_syntaxv(stderr, listtable,"\n\t"); arg_print_syntaxv(stderr, listnamestable,"\n\t"); arg_print_syntaxv(stderr, argtable,"\n"); arg_print_glossary_gnu(stderr, listtable); arg_print_glossary_gnu(stderr, listnamestable); arg_print_glossary_gnu(stderr, argtable); goto cleanup_argtable; } bool mixdown=mono->count; const LilvPlugin* plugin=getplugin(pluginname->sval[0],plugins,lilvworld); if(!plugin) { fprintf(stderr,"No such plugin %s\n", pluginname->sval[0]); goto cleanup_argtable; } SF_INFO formatinfo; formatinfo.format=0; SNDFILE* insndfile=sf_open(*(infile->filename), SFM_READ, &formatinfo); int sndfileerr=sf_error(insndfile) ; if(sndfileerr) { fprintf(stderr,"Error reading input file: %s\n",sf_error_number(sndfileerr)); goto cleanup_sndfile; } unsigned int numchannels=formatinfo.channels; unsigned int blocksize=blksize->ival[0]; LilvNode* input_class = lilv_new_uri(lilvworld, LILV_URI_INPUT_PORT); LilvNode* output_class = lilv_new_uri(lilvworld, LILV_URI_OUTPUT_PORT); LilvNode* control_class = lilv_new_uri(lilvworld, LILV_URI_CONTROL_PORT); LilvNode* audio_class = lilv_new_uri(lilvworld, LILV_URI_AUDIO_PORT); LilvNode* event_class = lilv_new_uri(lilvworld, LILV_URI_EVENT_PORT); LilvNode* midi_class = lilv_new_uri(lilvworld, LILV_URI_MIDI_EVENT); LilvNode* optional = lilv_new_uri(lilvworld, LILV_NS_LV2 "connectionOptional"); { uint32_t numports=lilv_plugin_get_num_ports(plugin); unsigned int numout=0; uint32_t outindices[numports]; unsigned int numin=0; uint32_t inindices[numports]; unsigned int numcontrol=0; uint32_t controlindices[numports]; unsigned int numcontrolout=0; uint32_t controloutindices[numports]; bool portsproblem=false; for(uint32_t i=0; ifilename), SFM_WRITE, &formatinfo); sndfileerr=sf_error(outsndfile) ; if(sndfileerr) { fprintf(stderr,"Error reading output file: %s\n",sf_error_number(sndfileerr)); goto cleanup_outfile; } { unsigned int numplugins=1; if(connectargs->count) { for(int i=0; icount; i++) { const char * connectionlist=connectargs->sval[i]; while(*connectionlist) { char* nextcomma=strchr(connectionlist,','); char* nextcolon; if(nextcomma) { nextcolon=memchr(connectionlist,':',nextcomma-connectionlist); } else { nextcolon=strchr(connectionlist,':'); } if(!nextcolon) { fprintf(stderr, "Error parsing connection: Expected colon between channel and port.\n"); goto cleanup_outfile; } nextcolon++; int pluginstance=0; char* nextperiod=strchr(nextcolon,'.'); if(nextperiod) { char tmpbuffer[nextperiod-nextcolon+1]; memcpy(tmpbuffer,nextcolon,sizeof(char)*(nextperiod-nextcolon)); tmpbuffer[nextperiod-nextcolon]=0; pluginstance=atoi(tmpbuffer)-1; if(pluginstance<0) { fprintf(stderr, "Invalid plugin instance specified"); goto cleanup_outfile; } } else { nextperiod=nextcolon; } if(((unsigned)pluginstance)>=numplugins) { //Make sure we are instantiating enough instances of the plugin. numplugins=pluginstance+1; } if(nextcomma) { connectionlist=nextcomma+1; } else { break; } } } } else if(numin==1 && !mixdown) { numplugins=numchannels; } printf("Note: Running %i instances of the plugin.\n",numplugins); bool connections[numplugins][numin][numchannels]; memset(connections,0,sizeof(connections)); LilvInstance* instances[numplugins]; if(connectargs->count) { for(int i=0; icount; i++) { const char * connectionlist=connectargs->sval[i]; while(*connectionlist) { int channel=atoi(connectionlist)-1; if(channel<0|| ((unsigned)channel)>=numchannels) { fprintf(stderr, "Input sound file does not have channel %u. It has %u channels.\n",channel+1,numchannels); goto cleanup_outfile; } char* nextcomma=strchr(connectionlist,','); if(nextcomma) { *nextcomma=0; } char* nextcolon=strchr(connectionlist,':'); //Will not be nill otherwise it would have been caught when counting plugin instances *nextcolon=0; nextcolon++; unsigned int pluginstance=0; char* nextperiod=strchr(nextcolon,'.'); if(nextperiod) { *nextperiod=0; pluginstance=atoi(nextcolon+1)-1; } else { nextperiod=nextcolon; } bool foundmatch=false; for(uint32_t port=0; portnumin) { printf("Note: Extra channels ignored when mapping channels to plugin ports\n"); for(unsigned int i=0; icount) { for(int i=0; icount; i++) { const char * parameters=controls->sval[i]; while(*parameters) { char* nextcomma=strchr(parameters,','); if(nextcomma) { *nextcomma=0; } char* nextcolon=strchr(parameters,':'); if(nextcolon) { *nextcolon=0; } else { fprintf(stderr, "Error parsing parameters: Expected colon between port and value.\n"); goto cleanup_outfile; } nextcolon++; float value=strtof(nextcolon,NULL); bool foundmatch=false; for(uint32_t port=0; portcount) { process_no_check_clipping(blocksize, numchannels, numin, numout, numplugins, connections, pluginbuffers, outputbuffers, instances, insndfile, outsndfile); } else { process_check_clipping(blocksize, numchannels, numin, numout, numplugins, connections, pluginbuffers, outputbuffers, instances, insndfile, outsndfile); } } //cleanup_lv2: for(unsigned int i=0; i