summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Redfern <tim@herge.(none)>2013-03-26 18:33:54 +0000
committerTim Redfern <tim@herge.(none)>2013-03-26 18:33:54 +0000
commit7cf0551b85deed68d0eefd51e7b233e5c488f63b (patch)
tree651b793a986bc847b954d05d1d87c5a07653cb05
parent1cd4f29bbb6a1f79bc75ca345e28b0fd4a86f20d (diff)
nearly working thumbnailer
-rw-r--r--rotord/rotor.cpp181
-rwxr-xr-xrotord/rotor.h77
-rw-r--r--rotord/rotord.cbp2
3 files changed, 124 insertions, 136 deletions
diff --git a/rotord/rotor.cpp b/rotord/rotor.cpp
index de9f4ea..85382ea 100644
--- a/rotord/rotor.cpp
+++ b/rotord/rotor.cpp
@@ -6,7 +6,7 @@ bool fequal(const float u,const float v){
if (abs(u-v)<.001) return true;
else return false;
};
-
+
using namespace Rotor;
@@ -17,7 +17,7 @@ using namespace Rotor;
string output="";
int outputNo=0;
*/
-
+
void Render_context::runTask() {
while (!isCancelled()) {
int cmd=0;
@@ -32,7 +32,7 @@ void Render_context::runTask() {
//audio_analyser.process(audio_filename);
//vampHost::runPlugin("","qm-vamp-plugins","qm-tempotracker", "",0, audio_filename, cerr,true);
vector<base_audio_processor*> proc;
- proc.push_back(new audio_thumbnailer());
+ proc.push_back(audio_thumb);
if (load_audio(audio_filename,proc)) {
state=AUDIO_READY;
//set the response
@@ -83,8 +83,8 @@ Command_response Render_context::session_command(const std::vector<std::string>&
//check file exists
Poco::File f=Poco::File(command[3]);
//std::auto_ptr<std::istream> pStr(URIStreamOpener::defaultOpener().open(command[3]));
-
-
+
+
if (f.exists()) {
//pass to worker thread ??if engine is ready?? ??what if engine has finished but results aren't read??
audio_filename=command[3]; //for now, store session variables in memory
@@ -95,7 +95,7 @@ Command_response Render_context::session_command(const std::vector<std::string>&
response.status=HTTPResponse::HTTP_NOT_FOUND;
response.description="<status context='"+command[1]+"'>File "+command[3]+" not found</status>\n";
}
-
+
}
else {
response.status=HTTPResponse::HTTP_BAD_REQUEST;
@@ -108,24 +108,20 @@ Command_response Render_context::session_command(const std::vector<std::string>&
response.status=HTTPResponse::HTTP_OK;
response.description="<status context='"+command[1]+"'>Rotor: analysing audio</status>\n";
char c[20];
- sprintf(c,"%02f",audio_analyser.get_progress());
+ sprintf(c,"%02f",progress);
response.description+="<progress>"+string(c)+"</progress>\n";
}
- if (state==AUDIO_READY) {
- //not sure about this-- should this state be retained?
- //can the data only be read once?
- //for now
- response.status=HTTPResponse::HTTP_OK;
- response.description="<status context='"+command[1]+"'>Rotor: audio ready</status>\n";
- response.description+="<beats>";
- for (auto& i: audio_analyser.beats) { //is actually giving no data?
- char c[20];
- sprintf(c,"%02f",i);
- response.description+="<beat>"+string(c)+"</beat>";
- }
- response.description+="\n</beats>";
- state=IDLE;
- }
+ if (state==AUDIO_READY) {
+ //not sure about this-- should this state be retained?
+ //can the data only be read once?
+ //for now
+ response.status=HTTPResponse::HTTP_OK;
+ response.description="<status context='"+command[1]+"'>Rotor: audio ready</status>\n";
+ response.description+="<audio>\n";
+ response.description+=audio_thumb->print();
+ response.description+="\n</audio>";
+ state=IDLE;
+ }
}
if (command[0]=="DELETE") {
//for now
@@ -151,7 +147,7 @@ Command_response Render_context::session_command(const std::vector<std::string>&
if (state==IDLE) { //eventually not like this
Poco::File f=Poco::File(command[3]);
if (f.exists()) {
- string graph_filename=command[3];
+ string graph_filename=command[3];
if (load_graph(graph_filename)) {
response.status=HTTPResponse::HTTP_OK;
//response.description="<status context='"+command[1]+"'>Rotor: loaded graph "+command[3]+"</status>\n";
@@ -241,7 +237,7 @@ Command_response Render_context::session_command(const std::vector<std::string>&
response.description="<status>DUMMY RESPONSE 1</status>\n";
response.status=HTTPResponse::HTTP_OK;
}
-
+
}
if (command[2]=="render") {
if (command[0]=="GET") {
@@ -281,22 +277,22 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
//in the case of audio analysis, the daemon will pass each audio analysis object each chunk of data as it gets it
//there could even be an array of audio analysis functions to perform simultaneously?
//how about a vector of objects that subclass the base audio processor class?
-
+
//1st get parameters and initialise the processors
//then begin data loop locking progress variable after each frame
-
+
//
//
//the example in ffmpeg works, but it isn't one that identifies a codec- it is hard coded to look for a codec for AV_CODEC_ID_MP2
//it also doesn't load through libavformat - which opens containers- it just loads a naked .mp2 stream
//
//
-
+
/*
av_register_all();
-
+
std::shared_ptr<AVFormatContext> avFormat(avformat_alloc_context(), &avformat_free_context);
-
+
auto avFormatPtr = avFormat.get();
if (avformat_open_input(&avFormatPtr,filename.c_str(),nullptr, nullptr) != 0) {
cerr <<"Rotor: Error while calling avformat_open_input (probably invalid file format)" << endl;
@@ -307,11 +303,11 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
cerr << "Rotor: Error while calling avformat_find_stream_info" << endl;
return false;
}
-
+
av_dump_format(avFormat.get(), 0, 0, false); //avformat.h line 1256
-
-
+
+
AVStream* stream = nullptr;
for (unsigned int i = 0; i < avFormat->nb_streams; ++i) {
if (avFormat->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -324,16 +320,16 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
cerr <<"Rotor: Didn't find any audio stream in the file"<< endl;
return false;
}
-
+
// getting the required codec structure
const auto codec = avcodec_find_decoder(stream->codec->codec_id); //returns AVCodec*
if (codec == nullptr) {
cerr <<"Rotor: Audio codec not available"<< endl;
return false;
}
-
- //AVCodecContext?? avFormat->streams[i]->codec
-
+
+ //AVCodecContext?? avFormat->streams[i]->codec
+
// allocating a structure
std::shared_ptr<AVCodecContext> audioCodec(avcodec_alloc_context3(codec), [](AVCodecContext* c) { avcodec_close(c); av_free(c); });
@@ -342,7 +338,7 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
// make sure that this vector exists as long as the avVideoCodec exists
std::vector<uint8_t> codecContextExtraData;
codecContextExtraData.assign(stream->codec->extradata, stream->codec->extradata + stream->codec->extradata_size);
-
+
audioCodec->extradata = reinterpret_cast<uint8_t*>(codecContextExtraData.data());
audioCodec->extradata_size = codecContextExtraData.size();
@@ -351,23 +347,23 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
cerr <<"Rotor: Could not open codec"<< endl;
return false;
}
-
-
+
+
//avcodec.h line 1026
-
-
+
+
Packet packet;
// decoding code here
//cerr << "audio codec context - sample rate: "<< audioCodec->sample_rate <<", channels: "<<audioCodec->channels<<", sample format: "<<audioCodec->sample_fmt<<endl;
-
+
// suspect why was this in twice 210313
//do {
// if (packet.packet.stream_index != stream->index)
/// continue; // the packet is not about the stream we want, let's jump again the start of the loop
//} while(0);
//
-
+
// allocating an AVFrame
std::shared_ptr<AVFrame> avFrame(avcodec_alloc_frame(), &av_free);
@@ -375,10 +371,10 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
//Packet packet;
// data in the packet of data already processed
size_t offsetInData = 0;
-
-
+
+
bool foundPacket=false;
-
+
// the decoding loop, running until EOF
while (true) {
// reading a packet using libavformat
@@ -392,20 +388,20 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
cerr << "audio codec context - sample rate: "<< audioCodec->sample_rate <<", channels: "<<audioCodec->channels<<", sample format: "<<audioCodec->sample_fmt<<endl;
foundPacket=true;
}
-
+
}
-
-
+
+
// preparing the packet that we will send to libavcodec
AVPacket packetToSend;
packetToSend.data = packet.packet.data + offsetInData;
packetToSend.size = packet.packet.size - offsetInData;
-
+
// sending data to libavcodec
int isFrameAvailable = 0;
//const auto processedLength = avcodec_decode_video2(avVideoCodec.get(), avFrame.get(), &isFrameAvailable, &packetToSend);
-
+
const auto processedLength = avcodec_decode_audio4(audioCodec.get(), avFrame.get(), &isFrameAvailable, &packetToSend);
//utils.c line 2018
@@ -414,16 +410,16 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
//unsigned int numberOfSamplesContainedInTheFrame = stream.frame->nb_samples * stream.codecContext->channels;
//unsigned int numberOfSamplesToPlayPerSecond = stream.codecContext->sample_rate; // usually 44100 or 48000
//const void* data = stream.frame->data[0];
-
-
+
+
if (processedLength < 0) {
//av_free_packet(&packet); shouldn't have to because of type safe wrapper
cerr <<"Error "<<processedLength<< " while processing the data"<< endl;
return false;
}
offsetInData += processedLength;
-
-
+
+
// processing the image if available
if (isFrameAvailable) {
// display image on the screen
@@ -433,8 +429,8 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
std::this_thread::sleep(std::chrono::milliseconds(msToWait));
}
*/
-
-
+
+
http://www.gamedev.net/topic/624876-how-to-read-an-audio-file-with-ffmpeg-in-c/
// Initialize FFmpeg
av_register_all();
@@ -455,7 +451,7 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
std::cout << "Error opening the file" << std::endl;
return false;
}
-
+
if (avformat_find_stream_info(formatContext, NULL) < 0)
{
@@ -504,22 +500,20 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
av_dump_format(formatContext, 0, 0, false); //avformat.h line 1256
int samples = ((formatContext->duration + 5000)*codecContext->sample_rate)/AV_TIME_BASE;
-
+
std::cout << "This stream has " << codecContext->channels << " channels, a sample rate of " << codecContext->sample_rate << "Hz and "<<samples <<" samples" << std::endl;
std::cout << "The data is in format " <<codecContext->sample_fmt<< " (aka "<< av_get_sample_fmt_name(codecContext->sample_fmt) << ") "<<std::endl;
-
+
//we can now tell the processors the format
//we can work out the number of samples at this point
-
-
-
-
+
for (auto p: processors) {
p->init(codecContext->channels,16,samples);
}
AVPacket packet;
av_init_packet(&packet);
+ int sample_processed=0;
// Read the packets in a loop
while (av_read_frame(formatContext, &packet) == 0)
@@ -541,18 +535,19 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
// the audio data, so I won't add any junk here that might confuse you. Typically, if I want to find
// documentation on an FFmpeg structure or function, I just type "<name> doxygen" into google (like
// "AVFrame doxygen" for AVFrame's docs)
-
+
//std::cout << "Got a frame: bytes " << bytes << std::endl;
//now we can pass the data to the processor(s)
for (auto p: processors) {
- p->process_frame(frame->data[0],frame->nb_samples);
+ sample_processed=p->process_frame(frame->data[0],frame->nb_samples);
}
-
+ mutex.lock();
+ progress=((double)sample_processed)/samples;
+ mutex.unlock();
}
}
-
// You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory
- av_free_packet(&packet);
+ av_free_packet(&packet); //crashes here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! SIGSEV In _int_free (av=0xb4600010, p=0xb46025c8, have_lock=0) at malloc.c:4085 ()
}
// Some codecs will cause frames to be buffered up in the decoding process. If the CODEC_CAP_DELAY flag
@@ -568,6 +563,9 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
for (auto p: processors) {
p->process_frame(frame->data[0],frame->nb_samples);
}
+ mutex.lock();
+ progress=((double)sample_processed)/samples;
+ mutex.unlock();
}
}
@@ -575,9 +573,6 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process
av_free(frame);
avcodec_close(codecContext);
av_close_input_file(formatContext);
-
-
-
return true;
}
@@ -614,7 +609,7 @@ bool Render_context::load_graph(string &filename){
}
else cerr << "Rotor: linked input " << i2 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl;
}
- else cerr << "Rotor: linking input " << i2 << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl;
+ else cerr << "Rotor: linking input " << i2 << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl;
}
xml.popTag();
}
@@ -626,44 +621,6 @@ bool Render_context::load_graph(string &filename){
}
xml.popTag();
}
- /*
- model_name=XML.getAttribute("map4","model","none",0);
- model_x=ofToFloat(XML.getAttribute("map4","x","none",0));
- model_y=ofToFloat(XML.getAttribute("map4","y","none",0));
- model_z=ofToFloat(XML.getAttribute("map4","z","none",0));
- if(XML.pushTag("map4")) {
- numViews=XML.getNumTags("view");
- if(numViews) {
- views=new viewpoint[numViews];
- for (int i=0;i<numViews;i++){
- XML.pushTag("view",i);
- vector<string>keys;
- XML.getAttributeNames("settings", keys, 0);
- map<string,string>settings;
- for (int k=0;k<keys.size();k++) {
- settings[keys[k]]=XML.getAttribute("settings",keys[k],"none",0);
- }
- views[i].setup(settings);
- XML.popTag();
- }
- }
-
- numClips=XML.getNumTags("clip");
- if (numClips) {
- clips=new string[numClips];
- for (int i=0;i< numClips;i++) {
- XML.pushTag("clip",i);
- clips[i]=XML.getAttribute("settings","file","none",0);
- XML.popTag();
- }
- }
-
- XML.popTag();
- for (int i=0;i< numClips;i++) {
- printf("clip: %s\n",clips[i].c_str());
- }
- }
- */
return true;
}
else return false;
@@ -675,4 +632,4 @@ Node_factory::Node_factory(){
add_type("divide",new Signal_divide());
add_type("==",new Is_new_integer());
add_type("signal_output",new Signal_output());
-} \ No newline at end of file
+}
diff --git a/rotord/rotor.h b/rotord/rotor.h
index e16520b..de8e686 100755
--- a/rotord/rotor.h
+++ b/rotord/rotor.h
@@ -335,7 +335,7 @@ namespace Rotor {
};
class base_audio_processor {
public:
- virtual bool process_frame(uint8_t *data,int samples)=0;
+ virtual int process_frame(uint8_t *data,int samples)=0;
void init(int _channels,int _bits,int _samples) {
channels=_channels;
bits=_bits;
@@ -350,7 +350,7 @@ namespace Rotor {
public:
audio_thumbnailer(){
height=64;
- width=512;
+ width=128; //fit
data=new uint8_t[height*width];
memset(data,0,height*width);
}
@@ -360,39 +360,66 @@ namespace Rotor {
void init(int _channels,int _bits,int _samples) {
base_audio_processor::init(_channels,_bits,_samples);
samples_per_column=samples/width;
- stored_data=new uint8_t[samples_per_column*channels*(bits>>3)];
- sample=0;
+ column=0; //point thumbnail bitmap
+ out_sample=0; //sample in whole track
+ offset=(int)(pow(2.0,bits)/2.0);
+ scale=1.0/offset;
}
- bool process_frame(uint8_t *data,int samples_in_frame){
+ int process_frame(uint8_t *data,int samples_in_frame){
//begin by processing remaining samples
//samples per column could be larger than a frame! (probably is)
//but all we are doing is averaging
int bytes=(bits>>3);
int stride=channels*bytes;
- int next_sample=0;
- uint64_t accum;
- while (next_sample<samples_in_frame) {
- while (sample<samples_in_column&&next_sample<samples_in_frame) {
- //do the accumulation
+ int in_sample=0;
+ while (in_sample<samples_in_frame&&column<width) {
+ int sample=0;
+ int samples=0;
+ double accum=0;
+ while (sample<samples_per_column&&in_sample<samples_in_frame) {
+ //accumulate squares for this column
for (int i=0;i<channels;i++) {
int this_val=0;
for (int j=0;j<bytes;j++) {
this_val+=data[(sample*stride)+(i*bytes)+j]<<j;
}
- accum+=this_val*this_val;
- next_sample++;
- sample++;
- }
+ //convert from integer data format - i.e s16p - to audio signal in -1..1 range
+ //don't know how many bytes we are dealing with necessarily?
+ double val=(this_val-offset)*scale;
+ accum+=val*val;
+ samples++;
}
- } do;
-
- } do;
- return true;
+ in_sample++;
+ sample++;
+ out_sample++;
+ }
+ //get root-mean
+ double mean=pow(accum/samples,0.5);
+ int colheight=height*mean*0.5;
+ int hh=height>>1;
+ for (int i=0;i<height;i++) {
+ data[i*width+column]=abs(i-hh)<colheight?0xff:0x00;
+ }
+ column++;
+ }
+ return out_sample;
+ }
+ string print(){
+ string output;
+ for (int j=0;j<height;j++) {
+ for (int i=0;i<width;i++) {
+ output+=data[j*width+i]<0x7f?"0":"1";
+ }
+ output +="\n";
+ }
+ return output;
}
uint8_t *data;
- uint8_t *stored_data;
int height,width,samples_per_column;
- int sample;
+ int column,out_sample;
+ //for drawing graph
+ int offset;
+ double scale;
};
class Render_context: public Poco::Task { //Poco task object
//manages a 'patchbay'
@@ -400,7 +427,8 @@ namespace Rotor {
//and low level interface onto the graph
public:
Render_context(const std::string& name): Task(name) {
- state=IDLE;
+ audio_thumb=new audio_thumbnailer();
+ state=IDLE;
};
void runTask();
void add_queue(int item);
@@ -417,11 +445,12 @@ namespace Rotor {
private:
int state;
- float progress; //for a locking process: audio analysis or rendering
+ double progress; //for a locking process: audio analysis or rendering
//thread only does one thing at once
std::deque<int> work_queue;
Poco::Mutex mutex; //lock for access from parent thread
std::string audio_filename;
+ audio_thumbnailer *audio_thumb;
vampHost::QMAnalyser audio_analyser;
xmlIO xml;
Graph graph;
@@ -431,6 +460,6 @@ namespace Rotor {
/*
coding style
-Types begin with a capital, use underscore as a seperator
-instances begin with a small letter
+Types begin with capitals 'CamelCase'
+variables/ instances use lower case with underscore as a seperator
*/
diff --git a/rotord/rotord.cbp b/rotord/rotord.cbp
index 4f964e9..f4eafc3 100644
--- a/rotord/rotord.cbp
+++ b/rotord/rotord.cbp
@@ -49,6 +49,8 @@
<Add option="-Wall" />
</Compiler>
<Unit filename="Makefile" />
+ <Unit filename="avCodec.cpp" />
+ <Unit filename="avCodec.h" />
<Unit filename="ofUtils.cpp" />
<Unit filename="ofUtils.h" />
<Unit filename="rotor.cpp" />