458 lines
11 KiB
C++
458 lines
11 KiB
C++
/*
|
|
Copyright (C) 2006-2009 Nasca Octavian Paul
|
|
Author: Nasca Octavian Paul
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of version 2 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 (version 2) for more details.
|
|
|
|
You should have received a copy of the GNU General Public License (version 2)
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#ifdef PLAYERCLASSSTILLPRESENT
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
//#include <unistd.h>
|
|
#include <math.h>
|
|
#include "Player.h"
|
|
#include "globals.h"
|
|
|
|
extern std::unique_ptr<AudioFormatManager> g_audioformatmanager;
|
|
|
|
static int player_count=0;
|
|
|
|
Player::Player() : Thread("psplayerthread"){
|
|
player_count++;
|
|
if (player_count>1) {
|
|
printf("Error: Player class multiples instances.\n");
|
|
exit(1);
|
|
};
|
|
|
|
newtask.mode=TASK_NONE;
|
|
newtask.startpos=0.0;
|
|
newtask.rap=1.0;
|
|
newtask.fftsize=4096;
|
|
|
|
task=newtask;
|
|
mode=MODE_STOP;
|
|
|
|
outbuf.n=0;
|
|
|
|
outbuf.size=0;
|
|
outbuf.computek=0;
|
|
outbuf.outk=0;
|
|
outbuf.outpos=0;
|
|
outbuf.nfresh=0;
|
|
|
|
|
|
info.position=0;
|
|
freeze_mode=false;
|
|
bypass_mode=false;
|
|
first_in_buf=true;
|
|
window_type=W_HANN;
|
|
|
|
|
|
paused=false;
|
|
|
|
info.playing=false;
|
|
info.samplerate=44100;
|
|
info.eof=true;
|
|
volume=1.0;
|
|
onset_detection_sensitivity=0.0;
|
|
};
|
|
|
|
Player::~Player(){
|
|
player_count--;
|
|
|
|
stop();
|
|
};
|
|
|
|
Player::ModeType Player::getmode(){
|
|
return mode;
|
|
};
|
|
|
|
void Player::startplay(String filename, REALTYPE startpos,REALTYPE rap, int fftsize,bool bypass,
|
|
ProcessParameters *ppar,BinauralBeatsParameters *bbpar, double loop_start, double loop_end, int numoutchans)
|
|
{
|
|
info.playing=false;
|
|
info.eof=false;
|
|
bypass_mode=bypass;
|
|
if (bypass) freeze_mode=false;
|
|
paused=false;
|
|
taskmutex.lock();
|
|
newtask.mode=TASK_START;
|
|
newtask.filename=filename;
|
|
newtask.startpos=startpos;
|
|
newtask.m_num_outchans = numoutchans;
|
|
newtask.rap=rap;
|
|
newtask.fftsize=fftsize;
|
|
newtask.bypass=bypass;
|
|
newtask.ppar=ppar;
|
|
newtask.bbpar=bbpar;
|
|
newtask.m_loop_start = loop_start;
|
|
newtask.m_loop_end = loop_end;
|
|
taskmutex.unlock();
|
|
};
|
|
|
|
void Player::stop(){
|
|
//pun 0 la outbuf
|
|
info.playing=false;
|
|
/* taskmutex.lock();
|
|
newtask.mode=TASK_STOP;
|
|
taskmutex.unlock();*/
|
|
};
|
|
void Player::pause(){
|
|
paused=!paused;
|
|
};
|
|
|
|
void Player::seek(REALTYPE pos){
|
|
taskmutex.lock();
|
|
newtask.mode=TASK_SEEK;
|
|
newtask.startpos=pos;
|
|
taskmutex.unlock();
|
|
};
|
|
void Player::freeze(){
|
|
freeze_mode=!freeze_mode;
|
|
if (bypass_mode) freeze_mode=false;
|
|
};
|
|
|
|
void Player::setrap(REALTYPE newrap){
|
|
taskmutex.lock();
|
|
newtask.mode=TASK_RAP;
|
|
newtask.rap=newrap;
|
|
taskmutex.unlock();
|
|
};
|
|
|
|
void Player::set_process_parameters(ProcessParameters *ppar,BinauralBeatsParameters *bbpar){
|
|
taskmutex.lock();
|
|
newtask.mode=TASK_PARAMETERS;
|
|
newtask.ppar=ppar;
|
|
newtask.bbpar=bbpar;
|
|
taskmutex.unlock();
|
|
};
|
|
|
|
|
|
void Player::set_window_type(FFTWindow window){
|
|
window_type=window;
|
|
};
|
|
|
|
void Player::set_volume(REALTYPE vol){
|
|
volume=vol;
|
|
};
|
|
void Player::set_onset_detection_sensitivity(REALTYPE onset){
|
|
onset_detection_sensitivity=onset;
|
|
};
|
|
|
|
void Player::getaudiobuffer(int nsamples, float *out)
|
|
{
|
|
int numoutchans = task.m_num_outchans;
|
|
if (mode==MODE_PREPARING){
|
|
for (int i=0;i<nsamples*numoutchans;i++)
|
|
{
|
|
out[i]=0.0;
|
|
};
|
|
return;
|
|
};
|
|
if (paused){
|
|
for (int i=0;i<nsamples*numoutchans;i++)
|
|
{
|
|
out[i]=0.0;
|
|
};
|
|
return;
|
|
};
|
|
|
|
bufmutex.lock();
|
|
if ((outbuf.n==0)||(outbuf.nfresh==0)){
|
|
bufmutex.unlock();
|
|
for (int i=0;i<nsamples*numoutchans;i++)
|
|
{
|
|
out[i]=0.0;
|
|
};
|
|
return;
|
|
};
|
|
int k=outbuf.outk,pos=outbuf.outpos;
|
|
|
|
// printf("%d in_pos=%g\n",info.eof,outbuf.in_position[k]);
|
|
if (info.eof) mode=MODE_STOP;
|
|
else info.position=outbuf.in_position[k];
|
|
for (int i=0;i<nsamples;i++)
|
|
{
|
|
for (int j=0;j<numoutchans;++j)
|
|
out[i*numoutchans+j]=jlimit(-1.0f,1.0f, outbuf.channeldatas[j][k][pos]*volume);
|
|
pos++;
|
|
if (pos>=outbuf.size)
|
|
{
|
|
pos=0;
|
|
k++;
|
|
if (k>=outbuf.n) k=0;
|
|
|
|
outbuf.nfresh--;
|
|
//printf("%d %d\n",k,outbuf.nfresh);
|
|
|
|
if (outbuf.nfresh<0){//Underflow
|
|
outbuf.nfresh=0;
|
|
for (int j=i;j<nsamples;j++)
|
|
{
|
|
for (int ch=0;ch<numoutchans;++ch)
|
|
out[j*numoutchans+ch]=0.0;
|
|
};
|
|
break;
|
|
};
|
|
|
|
};
|
|
};
|
|
outbuf.outk=k;
|
|
outbuf.outpos=pos;
|
|
bufmutex.unlock();
|
|
|
|
|
|
};
|
|
|
|
|
|
void Player::run(){
|
|
while(1)
|
|
{
|
|
if (threadShouldExit())
|
|
break;
|
|
newtaskcheck();
|
|
|
|
if (mode==MODE_STOP) sleep(10);
|
|
if ((mode==MODE_PLAY)||(mode==MODE_PREPARING)){
|
|
computesamples();
|
|
};
|
|
task.mode=TASK_NONE;
|
|
|
|
};
|
|
};
|
|
|
|
void Player::newtaskcheck(){
|
|
TaskMode newmode=TASK_NONE;
|
|
taskmutex.lock();
|
|
if (task.mode!=newtask.mode) {
|
|
newmode=newtask.mode;
|
|
task=newtask;
|
|
};
|
|
newtask.mode=TASK_NONE;
|
|
taskmutex.unlock();
|
|
|
|
if (newmode==TASK_START){
|
|
if (current_filename!=task.filename)
|
|
{
|
|
current_filename=task.filename;
|
|
//task.startpos=0.0;
|
|
};
|
|
ai = std::make_unique<AInputS>(g_audioformatmanager.get());
|
|
if (ai->openAudioFile(task.filename))
|
|
{
|
|
info.samplerate=ai->info.samplerate;
|
|
mode=MODE_PREPARING;
|
|
ai->seek(task.startpos);
|
|
if (task.m_loop_start>=0.0 && task.m_loop_end>task.m_loop_start)
|
|
{
|
|
ai->m_loop_start = task.m_loop_start;
|
|
ai->m_loop_end = task.m_loop_end;
|
|
ai->m_loop_enabled = true;
|
|
} else
|
|
{
|
|
ai->m_loop_enabled = false;
|
|
}
|
|
bufmutex.lock();
|
|
m_stretchers.resize(task.m_num_outchans);
|
|
for (int i=0;i<m_stretchers.size();++i)
|
|
{
|
|
m_stretchers[i]=std::make_shared<ProcessedStretch>(task.rap,task.fftsize,window_type,task.bypass,ai->info.samplerate,i+1);
|
|
m_stretchers[i]->set_parameters(task.ppar);
|
|
}
|
|
binaural_beats=std::make_unique<BinauralBeats>(ai->info.samplerate);
|
|
|
|
if (binaural_beats)
|
|
binaural_beats->pars=*(task.bbpar);
|
|
|
|
inbufsize=m_stretchers[0]->get_max_bufsize();
|
|
|
|
inbuf.m_inbuf.resize(task.m_num_outchans);
|
|
for (int i=0;i<inbuf.m_inbuf.size();++i)
|
|
inbuf.m_inbuf[i].resize(inbufsize);
|
|
for (int i=0;i<inbufsize;i++)
|
|
for (int j=0;j<inbuf.m_inbuf.size();++j)
|
|
inbuf.m_inbuf[j][i]=0.0;
|
|
|
|
|
|
inbuf_i.resize(inbufsize*task.m_num_outchans);
|
|
for (int i=0;i<inbufsize*task.m_num_outchans;i++)
|
|
{
|
|
inbuf_i[i]=0;
|
|
};
|
|
first_in_buf=true;
|
|
|
|
outbuf.size=m_stretchers[0]->get_bufsize();
|
|
|
|
int min_samples=ai->info.samplerate*m_prebufferamount;
|
|
int hwbufsize = 1024;
|
|
int n=2*hwbufsize/outbuf.size;
|
|
if (n<3) n=3;//min 3 buffers
|
|
if (n<(min_samples/outbuf.size)) n=(min_samples/outbuf.size);//the internal buffers sums "min_samples" amount
|
|
outbuf.n=n;
|
|
outbuf.nfresh=0;
|
|
outbuf.channeldatas.resize(task.m_num_outchans);
|
|
for (int i=0;i<task.m_num_outchans;++i)
|
|
outbuf.channeldatas[i].resize(outbuf.n);
|
|
outbuf.computek=0;
|
|
outbuf.outk=0;
|
|
outbuf.outpos=0;
|
|
outbuf.in_position.resize(outbuf.n);
|
|
for (int j=0;j<outbuf.n;j++){
|
|
for (int k=0;k<task.m_num_outchans;++k)
|
|
{
|
|
outbuf.channeldatas[k][j].resize(outbuf.size);
|
|
for (int i=0;i<outbuf.size;i++)
|
|
outbuf.channeldatas[k][j][i]=0.0;
|
|
}
|
|
|
|
outbuf.in_position[j]=0.0;
|
|
};
|
|
|
|
bufmutex.unlock();
|
|
|
|
};
|
|
};
|
|
if (newmode==TASK_SEEK){
|
|
if (ai) ai->seek(task.startpos);
|
|
first_in_buf=true;
|
|
};
|
|
if (newmode==TASK_RAP)
|
|
{
|
|
for (int i=0;i<m_stretchers.size();++i)
|
|
m_stretchers[i]->set_rap(task.rap);
|
|
|
|
};
|
|
if (newmode==TASK_PARAMETERS){
|
|
for (int i=0;i<m_stretchers.size();++i)
|
|
m_stretchers[i]->set_parameters(task.ppar);
|
|
if (binaural_beats) binaural_beats->pars=*(task.bbpar);
|
|
};
|
|
};
|
|
|
|
void Player::computesamples(){
|
|
bufmutex.lock();
|
|
|
|
bool exitnow=(outbuf.n==0);
|
|
if (outbuf.nfresh>=(outbuf.n-1)) exitnow=true;//buffers are full
|
|
|
|
bufmutex.unlock();
|
|
if (exitnow) {
|
|
if (mode==MODE_PREPARING) {
|
|
info.playing=true;
|
|
mode=MODE_PLAY;
|
|
};
|
|
sleep(10);
|
|
return;
|
|
};
|
|
int outchs = task.m_num_outchans;
|
|
bool eof=false;
|
|
if (!ai)
|
|
eof=true;
|
|
else if (ai->eof) eof=true;
|
|
if (eof)
|
|
{
|
|
|
|
for (int i=0;i<inbufsize*outchs;i++)
|
|
{
|
|
inbuf_i[i] = 0.0;
|
|
};
|
|
outbuf.nfresh++;
|
|
bufmutex.lock();
|
|
for (int i=0;i<outbuf.size;i++)
|
|
{
|
|
for (int j=0;j<outchs;++j)
|
|
outbuf.channeldatas[j][outbuf.computek][i]=0.0;
|
|
};
|
|
outbuf.computek++;
|
|
if (outbuf.computek>=outbuf.n){
|
|
outbuf.computek=0;
|
|
};
|
|
bufmutex.unlock();
|
|
info.eof=true;
|
|
return;
|
|
};
|
|
|
|
bool result=true;
|
|
float in_pos_100=(REALTYPE) ai->info.currentsample/(REALTYPE)ai->info.nsamples*100.0;
|
|
info.liveposition = in_pos_100/100.0;
|
|
int readsize=m_stretchers[0]->get_nsamples(in_pos_100);
|
|
|
|
|
|
if (first_in_buf)
|
|
readsize=m_stretchers[0]->get_nsamples_for_fill();
|
|
else if (freeze_mode) readsize=0;
|
|
if (readsize)
|
|
result=(ai->read(readsize,outchs,inbuf_i.data())==(readsize));
|
|
if (result)
|
|
{
|
|
float in_pos=(REALTYPE) ai->info.currentsample/(REALTYPE)ai->info.nsamples;
|
|
|
|
if (ai->eof) in_pos=0.0;
|
|
|
|
for (int i=0;i<readsize;i++)
|
|
{
|
|
for (int j=0;j<inbuf.m_inbuf.size();++j)
|
|
inbuf.m_inbuf[j][i]=inbuf_i[i*outchs+j];
|
|
|
|
};
|
|
REALTYPE max_onset = std::numeric_limits<REALTYPE>::min();
|
|
for (int i=0;i<m_stretchers.size();++i)
|
|
{
|
|
m_stretchers[i]->window_type=window_type;
|
|
REALTYPE s_onset=onset_detection_sensitivity;
|
|
m_stretchers[i]->set_onset_detection_sensitivity(s_onset);
|
|
bool freezing=freeze_mode&&(!first_in_buf);
|
|
m_stretchers[i]->set_freezing(freezing);
|
|
|
|
REALTYPE onset_amt=m_stretchers[i]->process(inbuf.m_inbuf[i].data(),readsize);
|
|
max_onset = std::max(onset_amt,max_onset);
|
|
}
|
|
for (int i=0;i<m_stretchers.size();++i)
|
|
m_stretchers[i]->here_is_onset(max_onset);
|
|
binaural_beats->process(m_stretchers[0]->out_buf.data(),m_stretchers[1]->out_buf.data(),
|
|
m_stretchers[0]->get_bufsize(),in_pos_100);
|
|
// stretchl->process_output(stretchl->out_buf,stretchl->out_bufsize);
|
|
// stretchr->process_output(stretchr->out_buf,stretchr->out_bufsize);
|
|
int nskip=m_stretchers[0]->get_skip_nsamples();
|
|
if (nskip>0) ai->skip(nskip);
|
|
|
|
|
|
first_in_buf=false;
|
|
bufmutex.lock();
|
|
|
|
for (int ch=0;ch<outchs;++ch)
|
|
{
|
|
for (int i=0;i<outbuf.size;i++)
|
|
{
|
|
REALTYPE out_sample=m_stretchers[ch]->out_buf[i];
|
|
outbuf.channeldatas[ch][outbuf.computek][i]=out_sample;
|
|
}
|
|
}
|
|
outbuf.in_position[outbuf.computek]=in_pos;
|
|
outbuf.computek++;
|
|
if (outbuf.computek>=outbuf.n){
|
|
outbuf.computek=0;
|
|
};
|
|
bufmutex.unlock();
|
|
outbuf.nfresh++;
|
|
}else{
|
|
info.eof=true;
|
|
mode=MODE_STOP;
|
|
stop();
|
|
};
|
|
};
|
|
|
|
#endif
|