commit e4f9405f629d8731daf4deb022fa95ec8076a391 Author: Settel Date: Sun Jan 8 16:58:00 2023 +0100 moved to public git repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..adc312b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +src/*.o +src/vocoder diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..21388db --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,16 @@ +# Authors and people who have helped + +## idea, main code + +Achim Settelmeier + + +## ported to LADSPA + +Josh Green ,
+available at https://www.sirlab.de/linux/vocoder/ + + +## contributions + +Vincent Neuville - ported to jackd 0.116.1 diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..cacd174 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,11 @@ +Copyright (C) 2007-2012 Achim Settelmeier + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 3 of +the License, or (at your option) any later version. + +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. diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..9f5caf7 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,19 @@ + ## Requirements + + * libsndfile development files
+ tested with version 1.0.16 and 1.0.31 + * GNU C++ compiler
+ Ubuntu: `g++-12`
+ tested with version 4.1.2 and 12.2.0 + * GNU make + +### Ubuntu + +```bash +apt install libsndfile1 libsndfile1-dev g++-12 make +``` + +## Compile + + * run `make` + * find the compiled result in `./src/vocoder` diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7c7b028 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +all: + $(MAKE) -C src/ vocoder + +clean: + rm -f *~ + rm -f output.wav + $(MAKE) -C src/ clean + +test: + rm -f output.wav + src/vocoder -f data/formant.wav -c data/carrier.wav diff --git a/README.md b/README.md new file mode 100644 index 0000000..f48678c --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Sirlab Vocoder + +See file COPYRIGHT for information about copyright status of this package. + +Make sure you visit http://www.sirlab.de/linux/vocoder/ for updates and +further information about vocoder. + + +## Contact + +I'd like to hear from you. If you like the program or not, tell me! +My email address is + Achim Settelmeier diff --git a/data/carrier.wav b/data/carrier.wav new file mode 100644 index 0000000..314fe02 Binary files /dev/null and b/data/carrier.wav differ diff --git a/data/formant.wav b/data/formant.wav new file mode 100644 index 0000000..320613d Binary files /dev/null and b/data/formant.wav differ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..c6429d0 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,40 @@ +# Settings +CPPFLAGS= -O2 -Wall -W +LFLAGS= -lm -lsndfile + +VERSION=$(shell cat VERSION) +CPPFLAGS+= '-DVERSION="$(VERSION)"' + +CC=g++ +LD=g++ + +OBJECTS=main.o vocoder.o engine.o track.o inputtrack.o outputtrack.o + + +.SUFFIXES: .cpp .h .o +.PHONY: all clean libvocoder + +# Rules +%.o: %.cpp + $(CC) $(CPPFLAGS) -o $@ -c $< + +vocoder: $(OBJECTS) + $(LD) -o $@ $(OBJECTS) $(LFLAGS) + + +# Tool rules +run: + $(MAKE) vocoder + +clean: + rm -f *~ *.bak + rm -f *.o + rm -f vocoder + +# Dependencies +main.o: engine.h options.h +vocoder.o: vocoder.h types.h +engine.o: engine.h vocoder.h options.h types.h track.h inputtrack.h outputtrack.h +track.o: track.h types.h +inputtrack.o: track.h inputtrack.h types.h +outputtrack.o: track.h outputtrack.h types.h \ No newline at end of file diff --git a/src/VERSION b/src/VERSION new file mode 100644 index 0000000..48b9990 --- /dev/null +++ b/src/VERSION @@ -0,0 +1 @@ +0.31 diff --git a/src/engine.cpp b/src/engine.cpp new file mode 100644 index 0000000..8297f2c --- /dev/null +++ b/src/engine.cpp @@ -0,0 +1,111 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + */ + +// Includes +#include +#include +#include +#include "options.h" +#include "types.h" +#include "vocoder.h" +#include "track.h" +#include "inputtrack.h" +#include "outputtrack.h" +#include "vocoder.h" +#include "engine.h" + +using std::cout; +using std::cerr; +using std::endl; +using std::exception; +using std::runtime_error; + +// Engine +Engine::Engine(const options_t &_options): + options(_options) +{ + +} + +// run +void Engine::run(){ + const nframes_t maxFrames=65536; + + try{ + InputTrack formantTrack(options.formantFile, maxFrames, options.fps); + InputTrack carrierTrack(options.carrierFile, maxFrames, options.fps); + OutputTrack outputTrack(options.outputFile, maxFrames, options.fps); + Vocoder vocoderLeft; + Vocoder vocoderRight; + vocoderLeft.setSampleRate(options.fps); + vocoderRight.setSampleRate(options.fps); + for(unsigned int i=0; i + * + * brings input and output channels together + */ + +#ifndef __ENGINE_H__ +#define __ENGINE_H__ + +#include "types.h" +#include "inputtrack.h" +#include "outputtrack.h" +#include "vocoder.h" +#include "options.h" + +class Engine{ +public: + Engine(const options_t &options); + void run(); + void processBlock(InputTrack &formantTrack, + InputTrack &carrierTrack, + OutputTrack &outputTrack, + Vocoder &vocoderLeft, + Vocoder &vocoderRight, + nframes_t numFramesToProcess); + +private: + options_t options; +}; + +#endif diff --git a/src/inputtrack.cpp b/src/inputtrack.cpp new file mode 100644 index 0000000..dd2c001 --- /dev/null +++ b/src/inputtrack.cpp @@ -0,0 +1,77 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + */ + +// Includes +#include +#include +#include +#include "inputtrack.h" + +using std::string; +using std::runtime_error; + + +// InputTrack +InputTrack::InputTrack(const string &filename, + const nframes_t _maxFrames, + const nframes_t _fps): + Track(_maxFrames, _fps), + stretchFactor(1.0), + inputBufferSize(0), + inputBufferPtr(NULL) +{ + sndFileInfo.format=0; + sndFileFDPtr=sf_open(filename.c_str(), SFM_READ, &sndFileInfo); + + if(sndFileFDPtr==NULL) + throw runtime_error(filename + ": " + sf_strerror(NULL)); + + if(sndFileInfo.channels>2) + throw runtime_error(filename + ": too many channels (only mono or stereo supported)"); + + stretchFactor=double(fps) / sndFileInfo.samplerate; + inputBufferSize=lround(maxFrames / stretchFactor); + inputBufferPtr=new sample_t[inputBufferSize * sndFileInfo.channels]; +} + +// ~InputTrack +InputTrack::~InputTrack(){ + sf_close(sndFileFDPtr); + + if(inputBufferPtr) + delete inputBufferPtr; +} + +// readBlock +nframes_t InputTrack::readBlock(const nframes_t numFrames){ + + nframes_t numFramesToRead=lround(numFrames / stretchFactor); + nframes_t numFramesRead=sf_readf_float(sndFileFDPtr, + inputBufferPtr, + numFramesToRead); + + nframes_t numFramesAvailable=lround(numFramesRead * stretchFactor); + // level out rouding errors + if(numFramesToRead==numFramesRead) + numFramesAvailable=numFrames; + + // resample from inputBufferPtr to sampleBufferPtr + for(nframes_t dstFrameNr=0; dstFrameNr + * + * representation of a stereo input track + */ + +#ifndef __INPUTTRACK_H__ +#define __INPUTTRACK_H__ + +#include +#include "types.h" +#include "track.h" + +class InputTrack : public Track{ +public: + InputTrack(const std::string &filename, + const nframes_t maxFrames, + const nframes_t fps); + ~InputTrack(); + + nframes_t readBlock(const nframes_t numFrames); + +private: + double stretchFactor; + unsigned int inputBufferSize; + sample_t *inputBufferPtr; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6e8b7e1 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,86 @@ +/* + * Vocoder + * © 2007-2012 Achim Settelmeier + * + */ + +// Includes +#include +#include +#include +#include "options.h" +#include "engine.h" + +using std::cout; +using std::cerr; +using std::endl; + +void initOptions(options_t &o){ + o.outputFile="output.wav"; + o.fps=44100; + o.volume=1.0; +} + +void printUsage(){ + cout << "usage: vocode -f -c [-o ] [ -v ]" << endl; + cout << " -f name of input formant WAV file" << endl; + cout << " -c name of input carrier WAV file" << endl; + cout << " -o name of output WAV file" << endl; + cout << " -v output volume in percent, eg. -v 50 (=50%) or -v 180 (=180%)" << endl; +} + +void processCommandLineArguments(options_t &o, + int argc, + char **argv){ + int opt; + + while((opt=getopt(argc, argv, "hf:c:o:v:"))!=-1){ + switch(opt){ + case 'f': + o.formantFile=optarg; + break; + case 'c': + o.carrierFile=optarg; + break; + case 'o': + o.outputFile=optarg; + break; + case 'v': + o.volume=atoi(optarg)/100.0; + break; + case 'h': + printUsage(); + exit(0); + default: + cerr << "try vocoder -h for help." << endl; + exit(1); + } + } + + if(o.formantFile.empty()){ + cerr << "no formant file given, aborting." << endl; + cerr << "try vocoder -h for help." << endl; + exit(1); + }else if(o.carrierFile.empty()){ + cerr << "no carrier file given, aborting." << endl; + cerr << "try vocoder -h for help." << endl; + exit(1); + } +} + + +// main +int main(int argc, char **argv){ + options_t options; + initOptions(options); + processCommandLineArguments(options, argc, argv); + + cout << "D " << "input (formant): " << options.formantFile << endl; + cout << "D " << "input (carrier): " << options.carrierFile << endl; + cout << "D " << "output: " << options.outputFile << endl; + + Engine e(options); + e.run(); + + return 0; +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..db69d98 --- /dev/null +++ b/src/options.h @@ -0,0 +1,25 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + * structure that stores the command line options + */ + +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + +#include +#include "types.h" + +struct options_t{ + std::string formantFile; + std::string carrierFile; + std::string outputFile; + + double volume; + + nframes_t fps; +}; + + +#endif diff --git a/src/outputtrack.cpp b/src/outputtrack.cpp new file mode 100644 index 0000000..14137df --- /dev/null +++ b/src/outputtrack.cpp @@ -0,0 +1,43 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + */ + +// Includes +#include +#include +#include +#include "outputtrack.h" + +using std::string; +using std::runtime_error; + + +// OutputTrack +OutputTrack::OutputTrack(const string &filename, + const nframes_t _maxFrames, + const nframes_t _fps): + Track(_maxFrames, _fps) +{ + sndFileInfo.samplerate=fps; + sndFileInfo.channels=2; + sndFileInfo.format=SF_FORMAT_WAV + SF_FORMAT_PCM_16; + sndFileFDPtr=sf_open(filename.c_str(), SFM_WRITE, &sndFileInfo); + + if(sndFileFDPtr==NULL) + throw runtime_error(filename + ": " + sf_strerror(NULL)); +} + +// ~OutputTrack +OutputTrack::~OutputTrack(){ + sf_close(sndFileFDPtr); +} + +// writeBlock +nframes_t OutputTrack::writeBlock(const nframes_t numFrames){ + nframes_t numFramesWritten=sf_writef_float(sndFileFDPtr, + sampleBufferPtr, + numFrames); + return numFramesWritten; +} diff --git a/src/outputtrack.h b/src/outputtrack.h new file mode 100644 index 0000000..0ae9ce2 --- /dev/null +++ b/src/outputtrack.h @@ -0,0 +1,28 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + * representation of a stereo output track + */ + +#ifndef __OUTPUTTRACK_H__ +#define __OUTPUTTRACK_H__ + +#include +#include "types.h" +#include "track.h" + +class OutputTrack : public Track{ +public: + OutputTrack(const std::string &filename, + const nframes_t maxFrames, + const nframes_t fps); + ~OutputTrack(); + + nframes_t writeBlock(const nframes_t numFrames); + +private: + +}; + +#endif diff --git a/src/play_loop.cpp b/src/play_loop.cpp new file mode 100644 index 0000000..599157f --- /dev/null +++ b/src/play_loop.cpp @@ -0,0 +1,185 @@ +/** + * play_loop + * © 2007 Achim Settelmeier + * + */ + +// Includes /*fold00*/ +#include +#include +#include +#include +#include +#include +#include + +using std::string; +using std::cout; +using std::endl; +using std::list; + +// Globals /*fold00*/ +jack_client_t *jackClient=NULL; +list files; +string port="play_loop"; +string connectPort=""; +bool loop=false; +bool exitFlag=false; +jack_port_t *jackPortOutLeft=NULL; +jack_port_t *jackPortOutRight=NULL; +jack_nframes_t sampleRate; +SNDFILE *sndFileFD=NULL; +SF_INFO sndFileInfo; +bool playSampleFlag=false; + +#define SAMPLEBUFFERSIZE 4096 +double sampleBuffer[SAMPLEBUFFERSIZE]; + + +// play_sample /*fold00*/ +void playSample(const string &file){ + sndFileInfo.format=0; + sndFileFD=sf_open(file.c_str(), SFM_READ, &sndFileInfo); + if(sndFileFD==NULL){ + std::cerr << file << ": can't open, skipping." << std::endl; + return; + } + + playSampleFlag=true; + while(playSampleFlag) sleep(1); + + + sf_close(sndFileFD); +} + +// jackExit /*fold00*/ +void jackExit(void *arg){ + exitFlag=true; +} + + +// jackProcess /*fold00*/ +int jackProcess(jack_nframes_t nframes, void *arg){ + jack_default_audio_sample_t *outLeft=(jack_default_audio_sample_t *) jack_port_get_buffer(jackPortOutLeft, nframes); + jack_default_audio_sample_t *outRight=(jack_default_audio_sample_t *) jack_port_get_buffer(jackPortOutRight, nframes); + if(!playSampleFlag){ + memset(outLeft, 0, sizeof(jack_default_audio_sample_t) * nframes); + memset(outRight, 0, sizeof(jack_default_audio_sample_t) * nframes); + return 0; + } + + // read file in chunks, just as much as it fits into our buffer + jack_nframes_t pos=0; + while(pos1) ? 1 : 0; + for(jack_nframes_t i=0; pos=count) break; + srcPos*=sndFileInfo.channels; + outLeft[pos]=sampleBuffer[srcPos]; + outRight[pos]=sampleBuffer[srcPos+channel]; + } + } + + return 0; +} + +// main /*fold00*/ +int main(int argc, char **argv){ + // read command line options + while(1){ + int option_index=0; + static struct option long_options[]={ + {"help", 0, 0, 'h'}, + {"port", 1, 0, 'p'}, + {"connect", 1, 0, 'c'}, + {"loop", 0, 0, 'l'} + }; + int c=getopt_long(argc, argv, "hp:l", + long_options, &option_index); + if(c==-1) break; + if(c==0) + c=long_options[option_index].val; + + switch(c){ + case 'h': + printf("usage: play_loop [] [ ...]\n"); + printf(" available options are:\n"); + printf(" -h, --help show help\n"); + printf(" -l, --loop loop sample forever\n"); + printf(" -p, --port set name of JACK port\n"); + printf(" -c, --connect set name of JACK port to connect to\n"); + return 0; + case 'l': + loop=true; + break; + case 'p': + port=optarg; + break; + case 'c': + connectPort=optarg; + break; + } + } + + // error handling + if(optind>=argc){ + std::cerr << "no file to play" << std::endl; + return 1; + } + + // save all non-option arguments as filenames + for(;optind::iterator i=files.begin(); i!=files.end(); i++){ + playSample(*i); + } + if(!loop) break; + } + + // cleanup + jack_client_close(jackClient); + + return 0; +} diff --git a/src/track.cpp b/src/track.cpp new file mode 100644 index 0000000..ca21e76 --- /dev/null +++ b/src/track.cpp @@ -0,0 +1,31 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + */ + +// Includes +#include "track.h" + + +// Track +Track::Track(const nframes_t _maxFrames, + const nframes_t _fps): + maxFrames(_maxFrames), + fps(_fps), + numChannels(2) +{ + sampleBufferPtr=new sample_t[maxFrames * numChannels]; +} + + +// ~Track +Track::~Track(){ + delete sampleBufferPtr; +} + + +// getBufferPtr +sample_t *Track::getBufferPtr(){ + return sampleBufferPtr; +} diff --git a/src/track.h b/src/track.h new file mode 100644 index 0000000..5782bac --- /dev/null +++ b/src/track.h @@ -0,0 +1,33 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + * representation of one input or output stereo track + */ + +#ifndef __TRACK_H__ +#define __TRACK_H__ + +#include +#include "types.h" + +class Track{ +public: + Track(const nframes_t maxFrames, + const nframes_t fps); + ~Track(); + + sample_t *getBufferPtr(); + +protected: + const nframes_t maxFrames; + const nframes_t fps; + const unsigned int numChannels; + + SNDFILE *sndFileFDPtr; + SF_INFO sndFileInfo; + + sample_t *sampleBufferPtr; +}; + +#endif diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..6fa341a --- /dev/null +++ b/src/types.h @@ -0,0 +1,15 @@ +/* + * Vocoder + * © 2012 Achim Settelmeier + * + * basic type definitions + */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +typedef float sample_t; +typedef unsigned int nframes_t; + + +#endif diff --git a/src/vocoder.cpp b/src/vocoder.cpp new file mode 100644 index 0000000..6f700db --- /dev/null +++ b/src/vocoder.cpp @@ -0,0 +1,111 @@ +/* + * Vocoder + * © 2007-2012 Achim Settelmeier + * + */ + +// Includes +#include +#include +#include +#include "vocoder.h" + +// Vocoder +Vocoder::Vocoder(){ + volumeMain=16.0; + setSampleRate(48000); +} + +// init +void Vocoder::init(){ + for(int i=0; ihigh1=sample - b->f * bp->mid1 - bp->low1; + bp->mid1+=bp->high1 * b->c; + bp->low1+=bp->mid1; + + bp->high2=bp->low1 - b->f * bp->mid2 - bp->low2; + bp->mid2+=bp->high2 * b->c; + bp->low2+=bp->mid2; + bp->y=bp->high2 * b->att; +} diff --git a/src/vocoder.h b/src/vocoder.h new file mode 100644 index 0000000..168c92c --- /dev/null +++ b/src/vocoder.h @@ -0,0 +1,62 @@ +/* + * Vocoder + * © 2007-2012 Achim Settelmeier + * + * vocoder core routine + */ + +#ifndef __VOCODER_H__ +#define __VOCODER_H__ + +#include "types.h" + +#define VOCODER_MAXBANDS 16 + + +struct bandpass_t{ + sample_t low1, low2; + sample_t mid1, mid2; + sample_t high1, high2; + sample_t y; +}; + +struct band_t{ + double volumeLeft, volumeRight; + + double freq; + double c; + double f; + double att; + + double oldval; + double decay; + + bandpass_t formant; + bandpass_t carrier; +}; + +class Vocoder{ +public: + Vocoder(); + void process(sample_t *formant, + sample_t *carrier, + sample_t *outLeft, + sample_t *outRight, + const nframes_t nframes); + + void setSampleRate(const nframes_t sampleRate); + void setBandVolume(const unsigned int bandNr, + const double volumeLeft, + const double volumeRight); + +private: + void init(); + static void doBandpass(band_t *b, bandpass_t *bp, sample_t sample); + + band_t bands[VOCODER_MAXBANDS]; + nframes_t sampleRate; + double volumeMain; +}; + + +#endif