mirror of
https://github.com/vale981/openAV-Luppp
synced 2025-03-04 16:51:37 -05:00
clip save implemented, fixes #121
This commit is contained in:
parent
e5e57d7a74
commit
5acf1f33d6
9 changed files with 121 additions and 28 deletions
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "clipselector.hxx"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
|
||||
#include "../gui.hxx"
|
||||
|
@ -292,6 +294,7 @@ int ClipSelector::handle(int event)
|
|||
Fl_Menu_Item rclick_menu[] =
|
||||
{
|
||||
{ "Load" },
|
||||
{ "Save" },
|
||||
{ "Special"},
|
||||
{ "Beats", 0, 0, 0, FL_SUBMENU | FL_MENU_DIVIDER },
|
||||
{"1 "},
|
||||
|
@ -317,6 +320,22 @@ int ClipSelector::handle(int event)
|
|||
{
|
||||
gui->selectLoadSample( ID, clipNum );
|
||||
}
|
||||
else if ( strcmp(m->label(), "Save") == 0 )
|
||||
{
|
||||
//gui->saveBufferPath = "/tmp/test.wav";
|
||||
char* tmp = gui->selectSavePath();
|
||||
if(tmp && strlen(tmp)) {
|
||||
if( access( tmp, F_OK ) != -1 ) {
|
||||
int overwrite = fl_choice("Overwrite file?","Cancel","Overwrite",0);
|
||||
if (!overwrite) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
gui->saveBufferPath = tmp;
|
||||
free(tmp);
|
||||
gui->selectSaveSample( ID, clipNum );
|
||||
}
|
||||
}
|
||||
else if ( strcmp(m->label(), "1 ") == 0 ) {
|
||||
EventLooperLoopLength e = EventLooperLoopLength(ID, clipNum ,1);
|
||||
writeToDspRingbuffer( &e );
|
||||
|
|
|
@ -308,34 +308,42 @@ int DiskWriter::writeControllerFile( Controller* c )
|
|||
return LUPPP_RETURN_OK;
|
||||
}
|
||||
|
||||
int DiskWriter::writeAudioBuffer(int track, int scene, AudioBuffer* ab )
|
||||
int DiskWriter::writeAudioBuffer(int track, int scene, AudioBuffer* ab,
|
||||
const char *gui_path)
|
||||
{
|
||||
if ( !foldersCreated )
|
||||
stringstream path;
|
||||
if ( gui_path && strlen(gui_path) ) {
|
||||
printf("saving single buffer to %s\n", gui_path);
|
||||
path << gui_path;
|
||||
}
|
||||
else if ( foldersCreated )
|
||||
{
|
||||
stringstream filename;
|
||||
filename << "t_" << track << "_s_" << scene << ".wav";
|
||||
|
||||
// store the clip in clipData, we will write the session JSON for it in writeSession
|
||||
clipData.push_back( ClipData( track, scene, filename.str() ) );
|
||||
|
||||
// add the AudioBuffer metadata to the sample JSON node
|
||||
cJSON* sampleClip = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(audioJson, filename.str().c_str(), sampleClip );
|
||||
cJSON_AddNumberToObject(sampleClip,"beats", ab->getBeats() );
|
||||
|
||||
// get pretty name from GUI
|
||||
std::string clipName = gui->getTrack(track)->getClipSelector()->clipName( scene );
|
||||
cJSON_AddItemToObject ( sampleClip, "name", cJSON_CreateString( clipName.c_str() ));
|
||||
|
||||
// write the AudioBuffer contents to <path>/audio/ as <name>.wav
|
||||
// or alternatively t_<track>_s_<scene>.wav
|
||||
|
||||
path << audioDir << "/" << filename.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
LUPPP_WARN("%s", "Session folders not created yet, while trying to write audioBuffers.");
|
||||
return LUPPP_RETURN_ERROR;
|
||||
}
|
||||
|
||||
stringstream filename;
|
||||
filename << "t_" << track << "_s_" << scene << ".wav";
|
||||
|
||||
// store the clip in clipData, we will write the session JSON for it in writeSession
|
||||
clipData.push_back( ClipData( track, scene, filename.str() ) );
|
||||
|
||||
// add the AudioBuffer metadata to the sample JSON node
|
||||
cJSON* sampleClip = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(audioJson, filename.str().c_str(), sampleClip );
|
||||
cJSON_AddNumberToObject(sampleClip,"beats", ab->getBeats() );
|
||||
|
||||
// get pretty name from GUI
|
||||
std::string clipName = gui->getTrack(track)->getClipSelector()->clipName( scene );
|
||||
cJSON_AddItemToObject ( sampleClip, "name", cJSON_CreateString( clipName.c_str() ));
|
||||
|
||||
// write the AudioBuffer contents to <path>/audio/ as <name>.wav
|
||||
// or alternatively t_<track>_s_<scene>.wav
|
||||
|
||||
stringstream path;
|
||||
path << audioDir << "/" << filename.str();
|
||||
|
||||
SndfileHandle outfile( path.str(), SFM_WRITE, SF_FORMAT_WAV | SF_FORMAT_FLOAT, 1, gui->samplerate );
|
||||
|
||||
|
|
|
@ -59,8 +59,10 @@ class DiskWriter
|
|||
/// sets up session write path etc
|
||||
void initialize( std::string path, std::string sessionName );
|
||||
|
||||
/// writes a single audio buffer to disk
|
||||
int writeAudioBuffer(int track, int scene, AudioBuffer* ab );
|
||||
/// writes a single audio buffer to disk for saving whole state.
|
||||
// When gui_path is set, it only saves a single AB* to that path
|
||||
int writeAudioBuffer(int track, int scene, AudioBuffer* ab,
|
||||
const char* gui_path = 0);
|
||||
|
||||
/// flush the JSON to disk, finalizing the save
|
||||
int writeSession();
|
||||
|
|
|
@ -663,8 +663,9 @@ class EventStateSaveBuffer : public EventBase
|
|||
int scene;
|
||||
// pointer to the AudioBuffer to be saved
|
||||
AudioBuffer* ab;
|
||||
bool no_dealloc;
|
||||
|
||||
EventStateSaveBuffer(): track(0), scene(0), ab(0) {}
|
||||
EventStateSaveBuffer(): track(0), scene(0), ab(0), no_dealloc(0) {}
|
||||
EventStateSaveBuffer(int t, int s, AudioBuffer* a): track(t), scene(s), ab(a) {}
|
||||
};
|
||||
|
||||
|
|
|
@ -104,6 +104,20 @@ void handleDspEvents()
|
|||
jack_ringbuffer_read( rbToDsp, (char*)&ev, sizeof(EventStateReset) );
|
||||
jack->getState()->reset();
|
||||
} break; }
|
||||
case Event::STATE_SAVE_BUFFER: {
|
||||
if ( availableRead >= sizeof(EventStateReset) ) {
|
||||
EventStateSaveBuffer ev;
|
||||
jack_ringbuffer_read( rbToDsp, (char*)&ev, sizeof(EventStateSaveBuffer) );
|
||||
printf("jack got save buffer in %d, %d\n", ev.track, ev.scene);
|
||||
LooperClip* lc = jack->getLooper(ev.track)->getClip(ev.scene);
|
||||
if(!lc) break;
|
||||
EventStateSaveBuffer e;
|
||||
e.track = ev.track;
|
||||
e.scene = ev.scene;
|
||||
e.ab = lc->getAudioBuffer();
|
||||
e.no_dealloc = 1;
|
||||
writeToGuiRingbuffer( &e );
|
||||
} break; }
|
||||
|
||||
// ========= MASTER ===
|
||||
case Event::MASTER_VOL: {
|
||||
|
|
|
@ -181,8 +181,17 @@ void handleGuiEvents()
|
|||
cout << "EventSaveBuffer: " << ev.track << " " << ev.scene << " " << ev.ab->getID() << endl;
|
||||
#endif
|
||||
gui->getDiskWriter()->writeAudioBuffer( ev.track, ev.scene, ev.ab );
|
||||
// de allocate the AudioBuffer
|
||||
delete ev.ab;
|
||||
// de allocate the AudioBuffer only if reqested
|
||||
if(!ev.no_dealloc) {
|
||||
gui->getDiskWriter()->writeAudioBuffer( ev.track, ev.scene, ev.ab );
|
||||
delete ev.ab;
|
||||
} else
|
||||
{
|
||||
gui->getDiskWriter()->writeAudioBuffer(ev.track, ev.scene, ev.ab,
|
||||
gui->saveBufferPath.c_str());
|
||||
gui->saveBufferPath = "";
|
||||
}
|
||||
|
||||
} break; }
|
||||
|
||||
case Event::STATE_SAVE_FINISH: {
|
||||
|
|
34
src/gui.cxx
34
src/gui.cxx
|
@ -252,6 +252,40 @@ void Gui::selectLoadController(Fl_Widget* w, void*)
|
|||
|
||||
}
|
||||
|
||||
void Gui::selectSaveSample( int track, int scene )
|
||||
{
|
||||
EventStateSaveBuffer e;
|
||||
e.track = track,
|
||||
e.scene = scene,
|
||||
writeToDspRingbuffer( &e );
|
||||
}
|
||||
|
||||
char *
|
||||
Gui::selectSavePath()
|
||||
{
|
||||
string path;
|
||||
Fl_Native_File_Chooser fnfc;
|
||||
fnfc.title("Save filename?");
|
||||
fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
|
||||
|
||||
std::string defLoadPath = gui->getDiskReader()->getLastLoadedSamplePath();
|
||||
fnfc.directory( defLoadPath.c_str() ); // default directory to use
|
||||
|
||||
// Show native chooser
|
||||
switch ( fnfc.show() ) {
|
||||
case -1: /*printf("ERROR: %s\n", fnfc.errmsg()); */ break; // ERROR
|
||||
case 1: /*(printf("CANCEL\n"); */ break; // CANCEL
|
||||
default: /*printf("Loading directory: %s\n", fnfc.filename()); */
|
||||
path = fnfc.filename();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( strcmp( path.c_str(), "" ) == 0 )
|
||||
return 0;
|
||||
|
||||
return strdup(path.c_str());
|
||||
}
|
||||
|
||||
void Gui::selectLoadSample( int track, int scene )
|
||||
{
|
||||
// FIXME: refactor
|
||||
|
|
|
@ -76,6 +76,8 @@ class Gui
|
|||
|
||||
/// used to load samples into the grid
|
||||
void selectLoadSample( int track, int clip );
|
||||
void selectSaveSample( int track, int clip );
|
||||
char* selectSavePath();
|
||||
|
||||
/// allows the user to select a Controller definition
|
||||
static void selectLoadController(Fl_Widget* w, void*);
|
||||
|
@ -91,7 +93,9 @@ class Gui
|
|||
/// current special clip:
|
||||
int specialTrack;
|
||||
int specialScene;
|
||||
|
||||
|
||||
// save a particular sample to path
|
||||
std::string saveBufferPath;
|
||||
|
||||
private:
|
||||
vector<std::string> controllerVector;
|
||||
|
|
|
@ -82,6 +82,8 @@ class LooperClip : public Stately
|
|||
//Return the nr of samples holding actual audio. This is less then getBufferLength();
|
||||
long getActualAudioLength();
|
||||
size_t audioBufferSize();
|
||||
|
||||
AudioBuffer* getAudioBuffer() {return _buffer;}
|
||||
|
||||
/// set clip state
|
||||
void queuePlay(bool=true);
|
||||
|
|
Loading…
Add table
Reference in a new issue