moved to public git repo
This commit is contained in:
commit
e4f9405f62
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
src/*.o
|
||||||
|
src/vocoder
|
16
AUTHORS.md
Normal file
16
AUTHORS.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Authors and people who have helped
|
||||||
|
|
||||||
|
## idea, main code
|
||||||
|
|
||||||
|
Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
|
||||||
|
|
||||||
|
## ported to LADSPA
|
||||||
|
|
||||||
|
Josh Green <jgreen@users.NOSPAM-sourceforge.net>, <br />
|
||||||
|
available at https://www.sirlab.de/linux/vocoder/
|
||||||
|
|
||||||
|
|
||||||
|
## contributions
|
||||||
|
|
||||||
|
Vincent Neuville - ported to jackd 0.116.1
|
11
COPYRIGHT
Normal file
11
COPYRIGHT
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Copyright (C) 2007-2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
|
||||||
|
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.
|
19
INSTALL.md
Normal file
19
INSTALL.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## Requirements
|
||||||
|
|
||||||
|
* libsndfile development files<br />
|
||||||
|
tested with version 1.0.16 and 1.0.31
|
||||||
|
* GNU C++ compiler<br />
|
||||||
|
Ubuntu: `g++-12`<br />
|
||||||
|
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`
|
11
Makefile
Normal file
11
Makefile
Normal file
@ -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
|
13
README.md
Normal file
13
README.md
Normal file
@ -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 <vocoder@m1.sirlab.de>
|
BIN
data/carrier.wav
Normal file
BIN
data/carrier.wav
Normal file
Binary file not shown.
BIN
data/formant.wav
Normal file
BIN
data/formant.wav
Normal file
Binary file not shown.
40
src/Makefile
Normal file
40
src/Makefile
Normal file
@ -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
|
1
src/VERSION
Normal file
1
src/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
0.31
|
111
src/engine.cpp
Normal file
111
src/engine.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#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<VOCODER_MAXBANDS; i++){
|
||||||
|
vocoderLeft.setBandVolume(i, options.volume, options.volume);
|
||||||
|
vocoderRight.setBandVolume(i, options.volume, options.volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while(1){
|
||||||
|
nframes_t numFramesFormant=formantTrack.readBlock(maxFrames);
|
||||||
|
if(numFramesFormant==0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nframes_t numFramesCarrier=carrierTrack.readBlock(numFramesFormant);
|
||||||
|
if(numFramesCarrier==0)
|
||||||
|
break;
|
||||||
|
nframes_t numFramesToProcess=numFramesCarrier;
|
||||||
|
|
||||||
|
|
||||||
|
cout << "processing " << numFramesCarrier << " frames" << endl;
|
||||||
|
processBlock(formantTrack,
|
||||||
|
carrierTrack,
|
||||||
|
outputTrack,
|
||||||
|
vocoderLeft,
|
||||||
|
vocoderRight,
|
||||||
|
numFramesToProcess);
|
||||||
|
|
||||||
|
nframes_t numFramesWritten=outputTrack.writeBlock(numFramesToProcess);
|
||||||
|
|
||||||
|
if(numFramesWritten!=numFramesToProcess)
|
||||||
|
throw runtime_error(options.outputFile + ": write error");
|
||||||
|
|
||||||
|
// no full block processed? then we're done here
|
||||||
|
if(numFramesToProcess<maxFrames)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}catch(exception &e){
|
||||||
|
cerr << e.what() << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::processBlock(InputTrack &formantTrack,
|
||||||
|
InputTrack &carrierTrack,
|
||||||
|
OutputTrack &outputTrack,
|
||||||
|
Vocoder &vocoderLeft,
|
||||||
|
Vocoder &vocoderRight,
|
||||||
|
nframes_t numFramesToProcess){
|
||||||
|
|
||||||
|
sample_t *formantBufferPtr=formantTrack.getBufferPtr();
|
||||||
|
sample_t *carrierBufferPtr=carrierTrack.getBufferPtr();
|
||||||
|
sample_t *outputBufferPtr=outputTrack.getBufferPtr();
|
||||||
|
|
||||||
|
for(nframes_t framePos=0; framePos<numFramesToProcess; framePos++){
|
||||||
|
sample_t dummySample;
|
||||||
|
vocoderLeft.process(formantBufferPtr + (framePos * 2),
|
||||||
|
carrierBufferPtr + (framePos * 2),
|
||||||
|
outputBufferPtr + (framePos * 2),
|
||||||
|
&dummySample,
|
||||||
|
1);
|
||||||
|
vocoderRight.process(formantBufferPtr + (framePos * 2) + 1,
|
||||||
|
carrierBufferPtr + (framePos * 2) + 1,
|
||||||
|
&dummySample,
|
||||||
|
outputBufferPtr + (framePos * 2) + 1,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
src/engine.h
Normal file
32
src/engine.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
* 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
|
77
src/inputtrack.cpp
Normal file
77
src/inputtrack.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <math.h>
|
||||||
|
#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<numFrames; dstFrameNr++){
|
||||||
|
if(sndFileInfo.channels==1){
|
||||||
|
// mono input
|
||||||
|
nframes_t srcFrameNr=lround(dstFrameNr / stretchFactor);
|
||||||
|
sampleBufferPtr[dstFrameNr * 2]=inputBufferPtr[srcFrameNr];
|
||||||
|
sampleBufferPtr[dstFrameNr * 2 + 1]=inputBufferPtr[srcFrameNr];
|
||||||
|
}else{
|
||||||
|
// stereo input
|
||||||
|
nframes_t srcFrameNr=lround(dstFrameNr / stretchFactor);
|
||||||
|
sampleBufferPtr[dstFrameNr * 2]=inputBufferPtr[srcFrameNr * 2 ];
|
||||||
|
sampleBufferPtr[dstFrameNr * 2 + 1]=inputBufferPtr[srcFrameNr * 2 + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numFramesAvailable;
|
||||||
|
}
|
30
src/inputtrack.h
Normal file
30
src/inputtrack.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
* representation of a stereo input track
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __INPUTTRACK_H__
|
||||||
|
#define __INPUTTRACK_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#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
|
86
src/main.cpp
Normal file
86
src/main.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2007-2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#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 <file> -c <file> [-o <file>] [ -v <volume> ]" << endl;
|
||||||
|
cout << " -f <file> name of input formant WAV file" << endl;
|
||||||
|
cout << " -c <file> name of input carrier WAV file" << endl;
|
||||||
|
cout << " -o <file> name of output WAV file" << endl;
|
||||||
|
cout << " -v <vol> 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;
|
||||||
|
}
|
25
src/options.h
Normal file
25
src/options.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
* structure that stores the command line options
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __OPTIONS_H__
|
||||||
|
#define __OPTIONS_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
struct options_t{
|
||||||
|
std::string formantFile;
|
||||||
|
std::string carrierFile;
|
||||||
|
std::string outputFile;
|
||||||
|
|
||||||
|
double volume;
|
||||||
|
|
||||||
|
nframes_t fps;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
43
src/outputtrack.cpp
Normal file
43
src/outputtrack.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <math.h>
|
||||||
|
#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;
|
||||||
|
}
|
28
src/outputtrack.h
Normal file
28
src/outputtrack.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
* representation of a stereo output track
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __OUTPUTTRACK_H__
|
||||||
|
#define __OUTPUTTRACK_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#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
|
185
src/play_loop.cpp
Normal file
185
src/play_loop.cpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* play_loop
|
||||||
|
* © 2007 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includes /*fold00*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sndfile.h>
|
||||||
|
#include <jack/jack.h>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
using std::list;
|
||||||
|
|
||||||
|
// Globals /*fold00*/
|
||||||
|
jack_client_t *jackClient=NULL;
|
||||||
|
list<string> 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(pos<nframes){
|
||||||
|
double stretch=sndFileInfo.samplerate/(double)sampleRate;
|
||||||
|
|
||||||
|
jack_nframes_t maxcount=int(SAMPLEBUFFERSIZE/sndFileInfo.channels);
|
||||||
|
if(int((nframes-pos)*stretch)<maxcount) maxcount=int((nframes-pos)*stretch);
|
||||||
|
sf_count_t count=sf_readf_double(sndFileFD, sampleBuffer, maxcount);
|
||||||
|
if(count<=0){
|
||||||
|
// fill the rest of the buffer with silence and set the flag we're done
|
||||||
|
playSampleFlag=false;
|
||||||
|
for(; pos<nframes; pos++){
|
||||||
|
outLeft[pos]=0;
|
||||||
|
outRight[pos]=0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy sample to output buffer. Does convert sample rates but no interpolation is done
|
||||||
|
jack_nframes_t channel=(sndFileInfo.channels>1) ? 1 : 0;
|
||||||
|
for(jack_nframes_t i=0; pos<nframes; i++, pos++){
|
||||||
|
jack_nframes_t srcPos=int(i*stretch);
|
||||||
|
if(srcPos>=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 [<options>] <file> [<file> ...]\n");
|
||||||
|
printf(" available options are:\n");
|
||||||
|
printf(" -h, --help show help\n");
|
||||||
|
printf(" -l, --loop loop sample forever\n");
|
||||||
|
printf(" -p, --port <name> set name of JACK port\n");
|
||||||
|
printf(" -c, --connect <name> 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<argc; optind++){
|
||||||
|
files.push_back(argv[optind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up jack client
|
||||||
|
jackClient=jack_client_new(port.c_str());
|
||||||
|
if(jackClient==0){
|
||||||
|
std::cerr << "Can't connect to jack, jack_client_new() failed" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jack_on_shutdown(jackClient, jackExit, NULL);
|
||||||
|
jack_set_process_callback(jackClient, jackProcess, NULL);
|
||||||
|
|
||||||
|
jackPortOutLeft=jack_port_register(jackClient, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||||
|
jackPortOutRight=jack_port_register(jackClient, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||||
|
|
||||||
|
sampleRate=jack_get_sample_rate(jackClient);
|
||||||
|
if(jack_activate(jackClient)){
|
||||||
|
std::cerr << "cannot activate client" << std::endl;
|
||||||
|
jack_client_close(jackClient);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// main loop: play sample(s)
|
||||||
|
while(!exitFlag){
|
||||||
|
for(list<string>::iterator i=files.begin(); i!=files.end(); i++){
|
||||||
|
playSample(*i);
|
||||||
|
}
|
||||||
|
if(!loop) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
jack_client_close(jackClient);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
31
src/track.cpp
Normal file
31
src/track.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
33
src/track.h
Normal file
33
src/track.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
* representation of one input or output stereo track
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TRACK_H__
|
||||||
|
#define __TRACK_H__
|
||||||
|
|
||||||
|
#include <sndfile.h>
|
||||||
|
#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
|
15
src/types.h
Normal file
15
src/types.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
* basic type definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TYPES_H__
|
||||||
|
#define __TYPES_H__
|
||||||
|
|
||||||
|
typedef float sample_t;
|
||||||
|
typedef unsigned int nframes_t;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
111
src/vocoder.cpp
Normal file
111
src/vocoder.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2007-2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <string.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <math.h>
|
||||||
|
#include "vocoder.h"
|
||||||
|
|
||||||
|
// Vocoder
|
||||||
|
Vocoder::Vocoder(){
|
||||||
|
volumeMain=16.0;
|
||||||
|
setSampleRate(48000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
void Vocoder::init(){
|
||||||
|
for(int i=0; i<VOCODER_MAXBANDS; i++){
|
||||||
|
memset(&bands[i], 0, sizeof(band_t));
|
||||||
|
bands[i].volumeLeft=1.0;
|
||||||
|
bands[i].volumeRight=1.0;
|
||||||
|
bands[i].oldval=0.0;
|
||||||
|
bands[i].decay=1/(100.0+i*10.0);
|
||||||
|
|
||||||
|
if(i<4){
|
||||||
|
bands[i].freq=150+420*i/4.0;
|
||||||
|
}else{
|
||||||
|
bands[i].freq=600*pow(1.23,i-4.0);
|
||||||
|
}
|
||||||
|
double steps=bands[i].freq/(double)sampleRate;
|
||||||
|
double c=steps*2*M_PI;
|
||||||
|
bands[i].c=c*c;
|
||||||
|
bands[i].f=0.4/c;
|
||||||
|
bands[i].att=1/(6.0+((exp(steps)-1)*10.0));
|
||||||
|
|
||||||
|
bands[i].formant.low1=0.0;
|
||||||
|
bands[i].formant.low2=0.0;
|
||||||
|
bands[i].formant.mid1=0.0;
|
||||||
|
bands[i].formant.mid2=0.0;
|
||||||
|
bands[i].formant.high1=0.0;
|
||||||
|
bands[i].formant.high2=0.0;
|
||||||
|
bands[i].formant.y=0.0;
|
||||||
|
|
||||||
|
bands[i].carrier.low1=0.0;
|
||||||
|
bands[i].carrier.low2=0.0;
|
||||||
|
bands[i].carrier.mid1=0.0;
|
||||||
|
bands[i].carrier.mid2=0.0;
|
||||||
|
bands[i].carrier.high1=0.0;
|
||||||
|
bands[i].carrier.high2=0.0;
|
||||||
|
bands[i].carrier.y=0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// setSampleRate
|
||||||
|
void Vocoder::setSampleRate(const nframes_t _sampleRate){
|
||||||
|
if(sampleRate==_sampleRate) return;
|
||||||
|
|
||||||
|
sampleRate=_sampleRate;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// setBandVolume
|
||||||
|
void Vocoder::setBandVolume(unsigned int bandNr,
|
||||||
|
double volumeLeft,
|
||||||
|
double volumeRight){
|
||||||
|
bands[bandNr].volumeLeft=volumeLeft;
|
||||||
|
bands[bandNr].volumeRight=volumeRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process
|
||||||
|
void Vocoder::process(sample_t *formant,
|
||||||
|
sample_t *carrier,
|
||||||
|
sample_t *outLeft,
|
||||||
|
sample_t *outRight,
|
||||||
|
const nframes_t nframes){
|
||||||
|
|
||||||
|
for(nframes_t pos=0; pos<nframes; pos++){
|
||||||
|
sample_t l=0.0;
|
||||||
|
sample_t r=0.0;
|
||||||
|
sample_t x;
|
||||||
|
|
||||||
|
for(int i=0; i<VOCODER_MAXBANDS; i++){
|
||||||
|
Vocoder::doBandpass(&bands[i], &bands[i].formant, formant[pos]);
|
||||||
|
Vocoder::doBandpass(&bands[i], &bands[i].carrier, carrier[pos]);
|
||||||
|
|
||||||
|
bands[i].oldval+=(fabs(bands[i].formant.y)-bands[i].oldval)*bands[i].decay;
|
||||||
|
x=bands[i].carrier.y*bands[i].oldval;
|
||||||
|
l+=x*bands[i].volumeLeft;
|
||||||
|
r+=x*bands[i].volumeRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
outLeft[pos]=l*volumeMain;
|
||||||
|
outRight[pos]=r*volumeMain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doBandpass
|
||||||
|
void Vocoder::doBandpass(band_t *b, bandpass_t *bp, sample_t sample){
|
||||||
|
bp->high1=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;
|
||||||
|
}
|
62
src/vocoder.h
Normal file
62
src/vocoder.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Vocoder
|
||||||
|
* © 2007-2012 Achim Settelmeier <vocoder@m1.sirlab.de>
|
||||||
|
*
|
||||||
|
* 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
|
Loading…
x
Reference in New Issue
Block a user