New upstream version 0.14.2~dfsg1
authorJaromír Mikeš <mira.mikes@seznam.cz>
Tue, 15 Aug 2017 02:22:07 +0000 (04:22 +0200)
committerJaromír Mikeš <mira.mikes@seznam.cz>
Tue, 15 Aug 2017 02:22:07 +0000 (04:22 +0200)
56 files changed:
ChangeLog
Makefile.am
src/core/channel.cpp
src/core/channel.h
src/core/clock.cpp
src/core/conf.cpp
src/core/const.h
src/core/midiChannel.cpp
src/core/midiChannel.h
src/core/midiMapConf.cpp
src/core/mixer.cpp
src/core/mixerHandler.cpp
src/core/patch.cpp
src/core/plugin.cpp
src/core/sampleChannel.cpp
src/core/sampleChannel.h
src/core/wave.cpp
src/core/wave.h
src/core/waveFx.cpp
src/core/waveFx.h
src/core/waveManager.cpp [new file with mode: 0644]
src/core/waveManager.h [new file with mode: 0644]
src/glue/channel.cpp
src/glue/main.cpp
src/glue/sampleEditor.cpp
src/glue/sampleEditor.h
src/glue/storage.cpp
src/gui/dialogs/gd_about.cpp
src/gui/dialogs/gd_devInfo.cpp
src/gui/dialogs/gd_pluginList.cpp
src/gui/dialogs/midiIO/midiInputChannel.cpp
src/gui/dialogs/sampleEditor.cpp
src/gui/dialogs/sampleEditor.h
src/gui/elems/config/tabAudio.cpp
src/gui/elems/config/tabPlugins.cpp
src/gui/elems/mainWindow/keyboard/channelStatus.cpp
src/gui/elems/mainWindow/keyboard/column.cpp
src/gui/elems/mainWindow/keyboard/keyboard.cpp
src/gui/elems/mainWindow/keyboard/keyboard.h
src/gui/elems/mainWindow/keyboard/sampleChannel.cpp
src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp
src/gui/elems/mainWindow/mainMenu.cpp
src/gui/elems/sampleEditor/boostTool.cpp
src/gui/elems/sampleEditor/pitchTool.cpp
src/gui/elems/sampleEditor/rangeTool.cpp
src/gui/elems/sampleEditor/rangeTool.h
src/gui/elems/sampleEditor/waveTools.cpp
src/gui/elems/sampleEditor/waveform.cpp
src/gui/elems/sampleEditor/waveform.h
src/utils/gui.cpp
src/utils/string.cpp
src/utils/string.h
tests/utils.cpp
tests/wave.cpp
tests/waveFx.cpp [new file with mode: 0644]
tests/waveManager.cpp [new file with mode: 0644]

index d2e15953690ff4ed3417d823d1a08f3228539a1c..f9e8033e1b3314a8db0d69c922e94de249f79fb0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 --------------------------------------------------------------------------------
 
 
+0.14.2 --- 2017 . 08 . 14
+- [Sample Editor] Audible preview (with optional loop mode)
+- [Sample Editor] Frame-precise editing
+- [Sample Editor] Show sample's information
+- [Sample Editor] Improved fade out algorithm
+- [Sample Editor] Process both left and right channel's data while drawing
+- Better Wave objects handling
+- Improved channels' memory management
+- Improved empty columns cleanup algorithm
+- Update Catch version
+- Update JUCE version (5.1.1)
+- Update Jansson version (2.10)
+- Fix missing tempo update on reset to init state
+- Fix wrong memory allocation for UI-less plugins
+
+
 0.14.1 --- 2017 . 07 . 16
 - Update JUCE library to 5.0.2
 - Show play head in Sample Editor
index 0c0e2da5154270867fe953388d43c862507a1871..6bdf82b1ed9779f8b89cc95a595a7427bcb8615c 100644 (file)
@@ -45,6 +45,8 @@ src/core/storager.h                      \
 src/core/storager.cpp                  \
 src/core/clock.h                       \
 src/core/clock.cpp                     \
+src/core/waveManager.h                 \
+src/core/waveManager.cpp               \
 src/glue/main.h                        \
 src/glue/main.cpp                      \
 src/glue/io.h                          \
@@ -279,9 +281,8 @@ endif
 
 if LINUX
 giada_SOURCES += src/deps/rtaudio-mod/RtAudio.h src/deps/rtaudio-mod/RtAudio.cpp
-# -Wno-error=misleading-indentation: don't stop on JUCE warnings on GCC6
 # -Wno-error=unused-function: don't stop on JUCE's unused functions
-giada_CXXFLAGS += -Wno-error=misleading-indentation -Wno-error=unused-function
+giada_CXXFLAGS += -Wno-error=unused-function
 giada_CPPFLAGS += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__
 giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
   -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \
@@ -338,13 +339,17 @@ giada_tests_SOURCES =        \
 tests/main.cpp               \
 tests/conf.cpp               \
 tests/wave.cpp               \
+tests/waveManager.cpp        \
 tests/patch.cpp              \
 tests/midiMapConf.cpp        \
 tests/pluginHost.cpp         \
 tests/utils.cpp              \
 tests/recorder.cpp           \
+tests/waveFx.cpp             \
 src/core/conf.cpp            \
 src/core/wave.cpp            \
+src/core/waveManager.cpp     \
+src/core/waveFx.cpp          \
 src/core/midiMapConf.cpp     \
 src/core/patch.cpp           \
 src/core/plugin.cpp          \
index f519e4a4ec6bbd20a3142dd2659a0e50e6f4c842..5677f328558a09ef420a5e9f96e2a348dbecde57 100644 (file)
@@ -53,6 +53,7 @@ Channel::Channel(int type, int status, int bufferSize)
 : bufferSize     (bufferSize),
   midiFilter     (-1),
   pan            (0.5f),
+  previewMode    (G_PREVIEW_NONE),
   type           (type),
   status         (status),
   key            (0),
@@ -82,10 +83,6 @@ Channel::Channel(int type, int status, int bufferSize)
   midiOutLmute   (0x0),
   midiOutLsolo   (0x0)
 {
-  vChan = (float *) malloc(bufferSize * sizeof(float));
-       if (!vChan)
-               gu_log("[Channel::allocVchan] unable to alloc memory for vChan\n");
-       std::memset(vChan, 0, bufferSize * sizeof(float));
 }
 
 
@@ -95,8 +92,23 @@ Channel::Channel(int type, int status, int bufferSize)
 Channel::~Channel()
 {
        status = STATUS_OFF;
-       if (vChan)
-               free(vChan);
+       if (vChan != nullptr)
+               delete[] vChan;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Channel::allocBuffers()
+{
+       vChan = new (std::nothrow) float[bufferSize];
+       if (vChan == nullptr) {
+               gu_log("[Channel::allocBuffers] unable to alloc memory for vChan!\n");
+               return false;
+       }
+       std::memset(vChan, 0, bufferSize * sizeof(float));      
+       return true;
 }
 
 
@@ -401,6 +413,21 @@ float Channel::calcPanning(int ch)
 /* -------------------------------------------------------------------------- */
 
 
+void Channel::setPreviewMode(int m)
+{
+       previewMode = m;
+}
+
+
+bool Channel::isPreview()
+{
+       return previewMode != G_PREVIEW_NONE;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 #ifdef WITH_VST
 
 juce::MidiBuffer &Channel::getPluginMidiEvents()
index 96491ad23fca486e87c168c4023b4f883c31d823..6f96257d07f03a73c3dbdd47b5eafc7fb2a02790 100644 (file)
@@ -72,11 +72,16 @@ protected:
 
        float pan;
 
+       /* previewMode
+       Whether the channel is in audio preview mode or not. */
+
+       int previewMode;
+
        /* sendMidiLMessage
        Composes a MIDI message by merging bytes from MidiMap conf class, and sends it 
        to KernelMidi. */
 
-       void sendMidiLmessage(uint32_t learn, const giada::m::midimap::message_t &msg);
+       void sendMidiLmessage(uint32_t learn, const giada::m::midimap::message_tmsg);
 
        /* calcPanning
        Given an audio channel (stereo: 0 or 1) computes the current panning value. */
@@ -92,19 +97,25 @@ public:
        /* copy
         * Make a shallow copy (no vChan/pChan allocation) of another channel. */
 
-       virtual void copy(const Channel *src, pthread_mutex_t *pluginMutex) = 0;
+       virtual void copy(const Channel* src, pthread_mutex_t* pluginMutex) = 0;
 
        /* readPatch
         * Fill channel with data from patch. */
 
-       virtual int readPatch(const std::string &basePath, int i,
-    pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
+       virtual int readPatch(const std::stringbasePath, int i,
+    pthread_mutex_tpluginMutex, int samplerate, int rsmpQuality);
 
        /* process
        Merges vChannels into buffer, plus plugin processing (if any). Warning:
        inBuffer might be nullptr if no input devices are available for recording. */
 
-       virtual void process(float *outBuffer, float *inBuffer) = 0;
+       virtual void process(float* outBuffer, float* inBuffer) = 0;
+
+       /* Preview
+       Makes itself audibile for audio preview, such as Sample Editor or other
+       tools. */
+
+       virtual void preview(float* outBuffer) = 0;
 
        /* start
        Action to do when channel starts. doQuantize = false (don't quantize)
@@ -167,7 +178,7 @@ public:
 
         // TODO - quantize is useless!
 
-       virtual void parseAction(giada::m::recorder::action *a, int localFrame,
+       virtual void parseAction(giada::m::recorder::actiona, int localFrame,
     int globalFrame, int quantize, bool mixerIsRunning) = 0;
 
        /* rewind
@@ -198,25 +209,31 @@ public:
 
        virtual void receiveMidi(uint32_t msg);
 
+       /* allocBuffers
+       Mandatory method to allocate memory for internal buffers. Call it after the
+       object has been constructed. */
+
+       virtual bool allocBuffers();
+
        /* ------------------------------------------------------------------------ */
 
-       int     index;                 // unique id
-       int     type;                  // midi or sample
-       int     status;                // status: see const.h
-       int     key;                   // keyboard button
-       float   volume;                // global volume
-       float   volume_i;              // internal volume
-       float   volume_d;              // delta volume (for envelope)
-       bool    mute_i;                // internal mute
-       bool      mute_s;                // previous mute status after being solo'd
-       bool    mute;                  // global mute
-       bool    solo;
-  bool    hasActions;            // has something recorded
-  bool    readActions;           // read what's recorded
-       bool    armed;                                                     // armed for recording
-       int       recStatus;             // status of recordings (waiting, ending, ...)
-       float  *vChan;                 // virtual channel
-  geChannel *guiChannel;         // pointer to a gChannel object, part of the GUI
+       int    index;                 // unique id
+       int    type;                  // midi or sample
+       int    status;                // status: see const.h
+       int    key;                   // keyboard button
+       float  volume;                // global volume
+       float  volume_i;              // internal volume
+       float  volume_d;              // delta volume (for envelope)
+       bool   mute_i;                // internal mute
+       bool     mute_s;                // previous mute status after being solo'd
+       bool   mute;                  // global mute
+       bool   solo;
+  bool   hasActions;            // has something recorded
+  bool   readActions;           // read what's recorded
+       bool   armed;                 // armed for recording
+       int      recStatus;             // status of recordings (waiting, ending, ...)
+       floatvChan;                 // virtual channel
+  geChannel* guiChannel;        // pointer to a gChannel object, part of the GUI
 
        // TODO - midi structs, please
 
@@ -240,7 +257,7 @@ public:
   uint32_t midiOutLsolo;
 
 #ifdef WITH_VST
-  std::vector <Plugin *> plugins;
+  std::vector <Plugin*> plugins;
 #endif
 
 
@@ -261,13 +278,16 @@ public:
        void setPan(float v);
        float getPan();
 
+       void setPreviewMode(int m);
+       bool isPreview();
+
 #ifdef WITH_VST
 
        /* getPluginMidiEvents
         * Return a reference to midiBuffer stack. This is available for any kind of
         * channel, but it makes sense only for MIDI channels. */
 
-       juce::MidiBuffer &getPluginMidiEvents();
+       juce::MidiBuffergetPluginMidiEvents();
 
        void clearMidiBuffer();
 
index fce225a457c67e3fc11bc9f3ce600cb528493a71..1a7e5d503f25cfd00d35d9392edebeb8e031eed4 100644 (file)
@@ -60,7 +60,7 @@ int midiTCseconds = 0;
 int midiTCminutes = 0;
 int midiTChours   = 0;
 
-#ifdef __linux__
+#ifdef G_OS_LINUX
 kernelAudio::JackState jackStatePrev;
 #endif
 
@@ -84,6 +84,11 @@ void updateQuanto()
 void init(int sampleRate, float midiTCfps)
 {
   midiTCrate = (sampleRate / midiTCfps) * 2;  // stereo values
+  running    = false;
+  bpm        = G_DEFAULT_BPM;
+  bars       = G_DEFAULT_BARS;
+  beats      = G_DEFAULT_BEATS;
+  quantize   = G_DEFAULT_QUANTIZE;
   updateFrameBars();
 }
 
@@ -339,7 +344,7 @@ void sendMIDIrewind()
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef __linux__
+#ifdef G_OS_LINUX
 
 void recvJackSync()
 {
index 8cfe0f9d2108e2c5d26eb2e93dd0bebefb059e37..39bc9ec3ebc5f4061976c7a31e9b20d40b7348ae 100644 (file)
@@ -58,7 +58,7 @@ void sanitize()
        if (soundDeviceIn < -1) soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
        if (channelsOut < 0) channelsOut = 0;
        if (channelsIn < 0)  channelsIn  = 0;
-       if (buffersize < 8 || buffersize > 4096) buffersize = G_DEFAULT_BUFSIZE;
+       if (buffersize < G_MIN_BUF_SIZE || buffersize > G_MAX_BUF_SIZE) buffersize = G_DEFAULT_BUFSIZE;
        if (delayComp < 0) delayComp = G_DEFAULT_DELAYCOMP;
        if (midiPortOut < -1) midiPortOut = G_DEFAULT_MIDI_SYSTEM;
        if (midiPortOut < -1) midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
@@ -72,14 +72,14 @@ void sanitize()
        if (actionEditorW < 640) actionEditorW = 640;
        if (actionEditorH < 176) actionEditorH = 176;
        if (actionEditorZoom < 100) actionEditorZoom = 100;
-       if (actionEditorGridVal < 0) actionEditorGridVal = 0;
+       if (actionEditorGridVal < 0 || actionEditorGridVal > G_MAX_GRID_VAL) actionEditorGridVal = 0;
        if (actionEditorGridOn < 0) actionEditorGridOn = 0;
        if (pianoRollH <= 0) pianoRollH = 422;
   if (sampleEditorX < 0) sampleEditorX = 0;
        if (sampleEditorY < 0) sampleEditorY = 0;
        if (sampleEditorW < 500) sampleEditorW = 500;
        if (sampleEditorH < 292) sampleEditorH = 292;
-       if (sampleEditorGridVal < 0) sampleEditorGridVal = 0;
+       if (sampleEditorGridVal < 0 || sampleEditorGridVal > G_MAX_GRID_VAL) sampleEditorGridVal = 0;
        if (sampleEditorGridOn < 0) sampleEditorGridOn = 0;
   if (midiInputX < 0) midiInputX = 0;
   if (midiInputY < 0) midiInputY = 0;
@@ -212,7 +212,7 @@ int sampleEditorX = 0;
 int sampleEditorY = 0;
 int sampleEditorW = 640;
 int sampleEditorH = 480;
-int sampleEditorGridVal = 1;
+int sampleEditorGridVal = 0;
 int sampleEditorGridOn  = false;
 
 int midiInputX = 0;
index 63c6a53c64f92b795c6156886063308575b1cc21..ad5384a728289290a921c0a021eb735bce291319 100644 (file)
 
 /* -- version --------------------------------------------------------------- */
 #define G_APP_NAME      "Giada"
-#define G_VERSION_STR   "0.14.1"
+#define G_VERSION_STR   "0.14.2"
 #define G_VERSION_MAJOR 0
 #define G_VERSION_MINOR 14
-#define G_VERSION_PATCH 1
+#define G_VERSION_PATCH 2
 
 #define CONF_FILENAME "giada.conf"
 
@@ -98,6 +98,9 @@
 #define G_MIN_COLUMN_WIDTH  140
 #define G_MAX_BOOST_DB      20.0f
 #define G_MAX_PITCH         4.0f
+#define G_MAX_GRID_VAL      64
+#define G_MIN_BUF_SIZE      8
+#define G_MAX_BUF_SIZE      4096
 
 
 
 #endif
 
 #define G_DEFAULT_SOUNDDEV_OUT    0      // FIXME - please override with rtAudio::getDefaultDevice (or similar)
-#define G_DEFAULT_SOUNDDEV_IN    -1                    // no recording by default: input disabled
+#define G_DEFAULT_SOUNDDEV_IN    -1      // no recording by default: input disabled
 #define G_DEFAULT_MIDI_SYSTEM     0
 #define G_DEFAULT_MIDI_PORT_IN   -1
 #define G_DEFAULT_MIDI_PORT_OUT  -1
 #define G_DEFAULT_SAMPLERATE      44100
 #define G_DEFAULT_BUFSIZE                    1024
 #define G_DEFAULT_DELAYCOMP                0
-#define G_DEFAULT_VOL                                1.0f
-#define G_DEFAULT_PITCH                              1.0f
-#define G_DEFAULT_BOOST                              1.0f
-#define G_DEFAULT_OUT_VOL            1.0f
-#define G_DEFAULT_IN_VOL             1.0f
-#define G_DEFAULT_CHANMODE           SINGLE_BASIC
-#define G_DEFAULT_BPM                                120.0f
-#define G_DEFAULT_BEATS                              4
-#define G_DEFAULT_BARS                       1
-#define G_DEFAULT_QUANTIZE        0              // quantizer off
+#define G_DEFAULT_BIT_DEPTH       32     // float
+#define G_DEFAULT_AUDIO_CHANS     2      // stereo for internal processing
+#define G_DEFAULT_VOL             1.0f
+#define G_DEFAULT_PITCH           1.0f
+#define G_DEFAULT_BOOST           1.0f
+#define G_DEFAULT_OUT_VOL         1.0f
+#define G_DEFAULT_IN_VOL          1.0f
+#define G_DEFAULT_CHANMODE       SINGLE_BASIC
+#define G_DEFAULT_BPM             120.0f
+#define G_DEFAULT_BEATS           4
+#define G_DEFAULT_BARS            1
+#define G_DEFAULT_QUANTIZE        0      // quantizer off
 #define G_DEFAULT_FADEOUT_STEP    0.01f  // micro-fadeout speed
 #define G_DEFAULT_COLUMN_WIDTH    380
-#define G_DEFAULT_PATCH_NAME       "(default patch)"
+#define G_DEFAULT_PATCH_NAME      "(default patch)"
 #define G_DEFAULT_MIDI_INPUT_UI_W 300
 #define G_DEFAULT_MIDI_INPUT_UI_H 350
 
 
 
 
+/* -- preview modes --------------------------------------------------------- */
+#define G_PREVIEW_NONE   0x00
+#define G_PREVIEW_NORMAL 0x01
+#define G_PREVIEW_LOOP   0x02
+
+
+
 /* -- actions --------------------------------------------------------------- */
 #define G_ACTION_KEYPRESS              0x01 // 0000 0001
 #define G_ACTION_KEYREL                        0x02 // 0000 0010
 
 
 
-/* -- mixerHandler signals -------------------------------------------------- */
-#define SAMPLE_LOADED_OK      0x01
-#define SAMPLE_LEFT_EMPTY     0x02
-#define SAMPLE_NOT_VALID      0x04
-#define SAMPLE_MULTICHANNEL   0x08
-#define SAMPLE_WRONG_BIT      0x10
-#define SAMPLE_WRONG_ENDIAN   0x20
-#define SAMPLE_WRONG_FORMAT   0x40
-#define SAMPLE_READ_ERROR     0x80
-#define SAMPLE_PATH_TOO_LONG  0x100
-
-/** FIXME - add to SAMPLE_ series those for when exporting */
+/* -- responses and return codes -------------------------------------------- */
+#define G_RES_ERR_PROCESSING    -6
+#define G_RES_ERR_WRONG_DATA    -5
+#define G_RES_ERR_NO_DATA       -4
+#define G_RES_ERR_PATH_TOO_LONG -3
+#define G_RES_ERR_IO            -2
+#define G_RES_ERR_MEMORY        -1
+#define G_RES_ERR                0
+#define G_RES_OK                 1
 
 
 
index 0349ac68bb4813b09dee820657daddac97025fba..fe58e7f34773c797bcec509e5ceff4536d14f479 100644 (file)
@@ -183,6 +183,15 @@ void MidiChannel::process(float *outBuffer, float *inBuffer)
 /* -------------------------------------------------------------------------- */
 
 
+void MidiChannel::preview(float *outBuffer)
+{
+       // No preview for MIDI channels (for now).
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void MidiChannel::start(int frame, bool doQuantize, int quantize,
                bool mixerIsRunning, bool forceStart, bool isUserGenerated)
 {
@@ -243,7 +252,7 @@ int MidiChannel::readPatch(const string &basePath, int i,
        midiOut     = pch->midiOut;
        midiOutChan = pch->midiOutChan;
 
-       return SAMPLE_LOADED_OK;  /// TODO - change name, it's meaningless here
+       return G_RES_OK;
 }
 
 
index 3d938e268214a56b3a3618f23b4caf7efc79cccb..feec2b1a55b292208142cbae54b4fc99e3e00cac 100644 (file)
@@ -52,6 +52,7 @@ public:
        void copy(const Channel *src, pthread_mutex_t *pluginMutex) override;
        void clear() override;
        void process(float *outBuffer, float *inBuffer) override;
+       void preview(float *outBuffer) override;
        void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
                  bool forceStart, bool isUserGenerated) override;
        void kill(int frame) override;
index 8b70f1488a4c2858c8ebbc53eaaa796a1de2099e..5d28774c65c3f5830873cee2fe03fbd9ef0df246 100644 (file)
@@ -57,7 +57,7 @@ bool readInitCommands(json_t *jContainer)
        json_t *jInitCommand;
        json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
 
-               string indexStr = "init command " + gu_itoa(commandIndex);
+               string indexStr = "init command " + gu_toString(commandIndex);
                if (!storager::checkObject(jInitCommand, indexStr.c_str()))
                        return 0;
 
index f3ce5b446bcf7ba5b404e591c0edb42054373c69..09927dcce344c16bbe6a7cb97e41619337a2a500 100644 (file)
@@ -80,7 +80,7 @@ float tick[TICKSIZE] = {
 /* lineInRec
 Records from line in. */
 
-void lineInRec(float *inBuf, unsigned frame)
+void lineInRec(floatinBuf, unsigned frame)
 {
        if (!mh::hasArmedSampleChannels() || !kernelAudio::isInputEnabled() || !recording)
                return;
@@ -106,7 +106,7 @@ void lineInRec(float *inBuf, unsigned frame)
 /* ProcessLineIn
 Computes line in peaks, plus handles "hear what you're playin'" thing. */
 
-void processLineIn(float *inBuf, unsigned frame)
+void processLineIn(floatinBuf, unsigned frame)
 {
        if (!kernelAudio::isInputEnabled())
                return;
@@ -131,7 +131,7 @@ void processLineIn(float *inBuf, unsigned frame)
 /* clearAllBuffers
 Cleans up every buffer, both in Mixer and in channels. */
 
-void clearAllBuffers(float *outBuf, unsigned bufferSize)
+void clearAllBuffers(floatoutBuf, unsigned bufferSize)
 {
        memset(outBuf, 0, sizeof(float) * bufferSize);         // out
        memset(vChanInToOut, 0, sizeof(float) * bufferSize);   // inToOut vChan
@@ -210,7 +210,7 @@ void sumChannels(unsigned frame)
 /* renderMetronome
 Generates metronome when needed and pastes it to the output buffer. */
 
-void renderMetronome(float *outBuf, unsigned frame)
+void renderMetronome(floatoutBuf, unsigned frame)
 {
        if (tockPlay) {
                outBuf[frame]   += tock[tockTracker];
@@ -239,11 +239,13 @@ void renderMetronome(float *outBuf, unsigned frame)
 Final processing stage. Take each channel and process it (i.e. copy its
 content to the output buffer). Process plugins too, if any. */
 
-void renderIO(float *outBuf, float *inBuf)
+void renderIO(float* outBuf, float* inBuf)
 {
        pthread_mutex_lock(&mutex_chans);
-       for (unsigned k=0; k<channels.size(); k++)
-               channels.at(k)->process(outBuf, inBuf);
+       for (Channel* channel : channels) {
+               channel->process(outBuf, inBuf);
+               channel->preview(outBuf);
+       }
        pthread_mutex_unlock(&mutex_chans);
 
 #ifdef WITH_VST
@@ -260,7 +262,7 @@ void renderIO(float *outBuf, float *inBuf)
 /* limitOutput
 Applies a very dumb hard limiter. */
 
-void limitOutput(float *outBuf, unsigned frame)
+void limitOutput(floatoutBuf, unsigned frame)
 {
        if (outBuf[frame] > 1.0f)
                outBuf[frame] = 1.0f;
@@ -280,7 +282,7 @@ void limitOutput(float *outBuf, unsigned frame)
 
 /* computePeak */
 
-void computePeak(float *outBuf, unsigned frame)
+void computePeak(floatoutBuf, unsigned frame)
 {
        /* TODO it takes into account only left channel so far! */
        if (outBuf[frame] > peakOut)
@@ -294,7 +296,7 @@ void computePeak(float *outBuf, unsigned frame)
 Last touches after the output has been rendered: apply inToOut if any, apply
 output volume. */
 
-void finalizeOutput(float *outBuf, unsigned frame)
+void finalizeOutput(floatoutBuf, unsigned frame)
 {
        /* merge vChanInToOut, if enabled */
 
@@ -432,8 +434,8 @@ void allocVirtualInput(int frames)
 /* -------------------------------------------------------------------------- */
 
 
-int masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize,
-       double streamTime, RtAudioStreamStatus status, void *userData)
+int masterPlay(void* _outBuf, void* _inBuf, unsigned bufferSize,
+       double streamTime, RtAudioStreamStatus status, voiduserData)
 {
        if (!ready)
                return 0;
@@ -442,8 +444,8 @@ int masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize,
   clock::recvJackSync();
 #endif
 
-       float *outBuf = (float*) _outBuf;
-       float *inBuf  = kernelAudio::isInputEnabled() ? (float*) _inBuf : nullptr;
+       floatoutBuf = (float*) _outBuf;
+       floatinBuf  = kernelAudio::isInputEnabled() ? (float*) _inBuf : nullptr;
        bufferSize   *= 2;     // stereo
        peakOut       = 0.0f;  // reset peak calculator
        peakIn        = 0.0f;  // reset peak calculator
@@ -536,7 +538,7 @@ void mergeVirtualInput()
                        continue;
                SampleChannel *ch = static_cast<SampleChannel*>(channels.at(i));
                if (ch->armed)
-                       memcpy(ch->wave->data, vChanInput, clock::getTotalFrames() * sizeof(float));
+                       memcpy(ch->wave->getData(), vChanInput, clock::getTotalFrames() * sizeof(float));
        }
        memset(vChanInput, 0, clock::getTotalFrames() * sizeof(float)); // clear vchan
 }
index b099c1ac3676e454774db9879aae99d9f8f255ff..4de62d083424a19b558c3aaa0a7d62749a8501f2 100644 (file)
@@ -31,7 +31,6 @@
 #include "../utils/log.h"
 #include "../glue/main.h"
 #include "../glue/channel.h"
-#include "mixerHandler.h"
 #include "kernelMidi.h"
 #include "mixer.h"
 #include "const.h"
@@ -49,6 +48,8 @@
 #include "sampleChannel.h"
 #include "midiChannel.h"
 #include "wave.h"
+#include "waveManager.h"
+#include "mixerHandler.h"
 
 
 using std::vector;
@@ -114,15 +115,15 @@ int getNewChanIndex()
 /* -------------------------------------------------------------------------- */
 
 
-bool uniqueSampleName(SampleChannel *ch, const string &name)
+bool uniqueSampleName(SampleChannel* ch, const string& name)
 {
        for (unsigned i=0; i<mixer::channels.size(); i++) {
                if (ch == mixer::channels.at(i))  // skip itself
                        continue;
                if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
                        continue;
-               SampleChannel *other = (SampleChannel*) mixer::channels.at(i);
-               if (other->wave != nullptr && name == other->wave->name)
+               SampleChannelother = (SampleChannel*) mixer::channels.at(i);
+               if (other->wave != nullptr && name == other->wave->getName())
                        return false;
        }
        return true;
@@ -132,9 +133,9 @@ bool uniqueSampleName(SampleChannel *ch, const string &name)
 /* -------------------------------------------------------------------------- */
 
 
-Channel *addChannel(int type)
+ChanneladdChannel(int type)
 {
-  Channel *ch;
+  Channelch;
        int bufferSize = kernelAudio::getRealBufSize() * 2;
 
        if (type == CHANNEL_SAMPLE)
@@ -142,6 +143,11 @@ Channel *addChannel(int type)
        else
                ch = new MidiChannel(bufferSize);
 
+       if (!ch->allocBuffers()) {
+               delete ch;
+               return nullptr;
+       }
+
        while (true) {
                if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
       continue;
@@ -160,7 +166,7 @@ Channel *addChannel(int type)
 /* -------------------------------------------------------------------------- */
 
 
-int deleteChannel(Channel *ch)
+int deleteChannel(Channelch)
 {
        int index = -1;
        for (unsigned i=0; i<mixer::channels.size(); i++) {
@@ -207,7 +213,7 @@ bool hasLogicalSamples()
     if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
       continue;
     SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
-    if (ch->wave && ch->wave->isLogical)
+    if (ch->wave && ch->wave->isLogical())
       return true;
   }
        return false;
@@ -224,7 +230,7 @@ bool hasEditedSamples()
                if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
       continue;
     SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
-    if (ch->wave && ch->wave->isEdited)
+    if (ch->wave && ch->wave->isEdited())
       return true;
   }
        return false;
@@ -306,27 +312,31 @@ bool startInputRec()
 {
        int channelsReady = 0;
 
-       for (unsigned i=0; i<mixer::channels.size(); i++) {
+       for (Channel* channel : mixer::channels) {
 
-               if (!mixer::channels.at(i)->canInputRec())
+               if (!channel->canInputRec())
                        continue;
 
-               SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
+               SampleChannel* ch = static_cast<SampleChannel*>(channel);
 
                /* Allocate empty sample for the current channel. */
 
-               if (!ch->allocEmpty(clock::getTotalFrames(), conf::samplerate, patch::lastTakeId))
-               {
+               Wave* wave = nullptr;
+               int result = waveManager::createEmpty(clock::getTotalFrames(), 
+                       conf::samplerate, string("TAKE-" + gu_toString(patch::lastTakeId)), &wave); 
+               if (result != G_RES_OK) {
                        gu_log("[startInputRec] unable to allocate new Wave in chan %d!\n",
                                ch->index);
                        continue;
                }
 
-               /* Increase lastTakeId until the sample name TAKE-[n] is unique */
+               ch->pushWave(wave, false);  // false: don't generate name, we provide it
+
+               /* Increase lastTakeId until the sample name TAKE-[n] is unique. */
 
-               while (!uniqueSampleName(ch, ch->wave->name)) {
+               while (!uniqueSampleName(ch, ch->wave->getName())) {
                        patch::lastTakeId++;
-                       ch->wave->name = "TAKE-" + gu_itoa(patch::lastTakeId);
+                       ch->wave->setName("TAKE-" + gu_toString(patch::lastTakeId));
                }
 
                gu_log("[startInputRec] start input recs using chan %d with size %d "
index f9152e0a840e741d88d09fa9cc562521efe2dba8..e0daf2e8cec8ff36a1ca3bc8fde655188ed3417a 100644 (file)
@@ -198,7 +198,7 @@ bool readChannels(json_t *jContainer)
   json_t *jChannel;
   json_array_foreach(jChannels, channelIndex, jChannel) {
 
-    string channelIndexStr = "channel " + gu_itoa(channelIndex);
+    string channelIndexStr = "channel " + gu_toString(channelIndex);
     if (!storager::checkObject(jChannel, channelIndexStr.c_str()))
       return 0;
 
@@ -262,7 +262,7 @@ bool readColumns(json_t *jContainer)
   json_t *jColumn;
   json_array_foreach(jColumns, columnIndex, jColumn) {
 
-    string columnIndexStr = "column " + gu_itoa(columnIndex);
+    string columnIndexStr = "column " + gu_toString(columnIndex);
     if (!storager::checkObject(jColumn, columnIndexStr.c_str()))
       return 0;
 
index 3616392b2aaa44139819be19a4c3871d2f7ed149..04028184d7568dee0c36047dfd30c4b6c2542a32 100644 (file)
@@ -56,15 +56,9 @@ Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate,
 
   /* Try to enable editor (i.e. plugin's UI) */
   
-  if (plugin->getActiveEditor() != nullptr) {
-    gu_log("[Plugin] plugin has an already active editor!\n");
-    return;
-  }
   ui = plugin->createEditorIfNeeded();
-  if (ui == nullptr) {
+  if (ui == nullptr)
     gu_log("[Plugin] unable to create editor, the plugin might be GUI-less!\n");
-    return;
-  }
 
   plugin->prepareToPlay(samplerate, buffersize);
 
index fd83e52dcc908ecdb3434b93541d1c35864ecb68..312101534f6a9e0c90fa5905c85851d9d7d1bdd9 100644 (file)
 
 #include <cmath>
 #include <cstring>
+#include <cassert>
 #include "../utils/log.h"
 #include "../utils/fs.h"
 #include "../utils/string.h"
-#include "sampleChannel.h"
 #include "patch.h"
 #include "const.h"
 #include "conf.h"
 #include "wave.h"
 #include "pluginHost.h"
 #include "waveFx.h"
+#include "waveManager.h"
 #include "mixerHandler.h"
 #include "kernelMidi.h"
 #include "kernelAudio.h"
+#include "sampleChannel.h"
 
 
 using std::string;
@@ -50,13 +52,17 @@ using namespace giada::m;
 
 SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
        : Channel          (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize),
+               rsmp_state       (nullptr),
+               pChan            (nullptr),
+               vChanPreview     (nullptr),
                frameRewind      (-1),
+               begin            (0),
+               end              (0),
                boost            (G_DEFAULT_BOOST),
                pitch            (G_DEFAULT_PITCH),
+               trackerPreview   (0),
                wave             (nullptr),
                tracker          (0),
-               begin            (0),
-               end              (0),
                mode             (G_DEFAULT_CHANMODE),
                qWait              (false),
                fadeinOn         (false),
@@ -65,12 +71,10 @@ SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
                fadeoutVol       (1.0f),
                fadeoutTracker   (0),
                fadeoutStep      (G_DEFAULT_FADEOUT_STEP),
-    inputMonitor     (inputMonitor),
-         midiInReadActions(0x0),
-         midiInPitch      (0x0)
+               inputMonitor     (inputMonitor),
+               midiInReadActions(0x0),
+               midiInPitch      (0x0)
 {
-       rsmp_state = src_new(SRC_LINEAR, 2, nullptr);
-       pChan = (float *) malloc(kernelAudio::getRealBufSize() * 2 * sizeof(float));
 }
 
 
@@ -79,20 +83,54 @@ SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
 
 SampleChannel::~SampleChannel()
 {
-       if (wave)
+       if (wave != nullptr)
                delete wave;
-       src_delete(rsmp_state);
-       free(pChan);
+       if (rsmp_state != nullptr)
+               src_delete(rsmp_state);
+       if (pChan != nullptr)
+               delete[] pChan; 
+       if (vChanPreview != nullptr)
+               delete[] vChanPreview;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::copy(const Channel *_src, pthread_mutex_t *pluginMutex)
+bool SampleChannel::allocBuffers()
+{
+       if (!Channel::allocBuffers())
+               return false;
+
+       rsmp_state = src_new(SRC_LINEAR, 2, nullptr);
+       if (rsmp_state == nullptr) {
+               gu_log("[SampleChannel::allocBuffers] unable to alloc memory for SRC_STATE!\n");
+               return false;
+       }
+
+       pChan = new (std::nothrow) float[bufferSize];
+       if (pChan == nullptr) {
+               gu_log("[SampleChannel::allocBuffers] unable to alloc memory for pChan!\n");
+               return false;
+       }
+
+       vChanPreview = new (std::nothrow) float[bufferSize];
+       if (vChanPreview == nullptr) {
+               gu_log("[SampleChannel::allocBuffers] unable to alloc memory for vChanPreview!\n");
+               return false;
+       }
+
+       return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::copy(const Channel* _src, pthread_mutex_t* pluginMutex)
 {
        Channel::copy(_src, pluginMutex);
-       SampleChannel *src = (SampleChannel *) _src;
+       const SampleChannel* src = static_cast<const SampleChannel*>(_src);
        tracker         = src->tracker;
        begin           = src->begin;
        end             = src->end;
@@ -109,11 +147,8 @@ void SampleChannel::copy(const Channel *_src, pthread_mutex_t *pluginMutex)
        fadeoutEnd      = src->fadeoutEnd;
        setPitch(src->pitch);
 
-       if (src->wave) {
-               Wave *w = new Wave(*src->wave); // invoke Wave's copy constructor
-               pushWave(w);
-               generateUniqueSampleName();
-       }
+       if (src->wave)
+               pushWave(new Wave(*src->wave)); // invoke Wave's copy constructor
 }
 
 
@@ -122,10 +157,10 @@ void SampleChannel::copy(const Channel *_src, pthread_mutex_t *pluginMutex)
 
 void SampleChannel::generateUniqueSampleName()
 {
-       string oldName = wave->name;
-       int k = 1; // Start from k = 1, zero is too nerdy
-       while (!mh::uniqueSampleName(this, wave->name)) {
-               wave->updateName((oldName + "-" + gu_itoa(k)).c_str());
+       string oldName = wave->getName();
+       int k = 0;
+       while (!mh::uniqueSampleName(this, wave->getName())) {
+               wave->setName(oldName + "-" + gu_toString(k));
                k++;
        }
 }
@@ -139,8 +174,8 @@ void SampleChannel::clear()
        /** TODO - these memsets can be done only if status PLAY (if below),
         * but it would require extra clearPChan calls when samples stop */
 
-               std::memset(vChan, 0, sizeof(float) * bufferSize);
-               std::memset(pChan, 0, sizeof(float) * bufferSize);
+       std::memset(vChan, 0, sizeof(float) * bufferSize);
+       std::memset(pChan, 0, sizeof(float) * bufferSize);
 
        if (status & (STATUS_PLAY | STATUS_ENDING)) {
                tracker = fillChan(vChan, tracker, 0);
@@ -226,46 +261,96 @@ void SampleChannel::onBar(int frame)
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::save(const char *path)
+int SampleChannel::save(const charpath)
 {
-       return wave->writeData(path);
+       return waveManager::save(wave, path);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setBegin(int v)
+void SampleChannel::setBegin(int f)
 {
-       if (v < 0)
+       /* TODO - Opaque channel's count - everything in SampleChannel should be
+       frame-based, not sample-based. I.e. Remove * wave->getChannels() stuff. */
+
+       if (f < 0)
                begin = 0;
        else
-       if (v > wave->size)
-               begin = wave->size - 2;
+       if (f > wave->getSize())
+               begin = wave->getSize() * wave->getChannels();
        else
-       if (v >= end)
-               begin = end - 2;
+       if (f >= end)
+               begin = end - wave->getChannels();
        else
-               begin = v;
+               begin = f * wave->getChannels();
+
        tracker = begin;
+       trackerPreview = begin;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setEnd(int v)
+void SampleChannel::setEnd(int f)
 {
-       if (v < 0)
-               end = begin + 2;
+       /* TODO - Opaque channel's count - everything in SampleChannel should be
+       frame-based, not sample-based. I.e. Remove * wave->getChannels() stuff. */
+       
+       if (f < 0)
+               end = begin + wave->getChannels();
        else
-       if (v > wave->size)
-               end = wave->size;
+       if (f >= wave->getSize())
+               end = (wave->getSize() - 1) * wave->getChannels();
        else
-       if (v <= begin)
-               end = begin + 2;
+       if (f <= begin)
+               end = begin + wave->getChannels();
        else
-               end = v;
+               end = f * wave->getChannels();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int SampleChannel::getBegin()
+{
+       /* TODO - Opaque channel's count - everything in SampleChannel should be
+       frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
+       
+       return begin / wave->getChannels();
+}
+
+
+int SampleChannel::getEnd()
+{
+       /* TODO - Opaque channel's count - everything in SampleChannel should be
+       frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
+
+       return end / wave->getChannels();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::setTrackerPreview(int f)
+{
+       /* TODO - Opaque channel's count - everything in SampleChannel should be
+       frame-based, not sample-based. I.e. Remove * wave->getChannels() stuff. */
+       
+       trackerPreview = f * wave->getChannels();
+}
+
+
+int SampleChannel::getTrackerPreview() const
+{
+       /* TODO - Opaque channel's count - everything in SampleChannel should be
+       frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
+
+       return trackerPreview / wave->getChannels();
 }
 
 
@@ -314,7 +399,7 @@ void SampleChannel::rewind()
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::parseAction(recorder::action *a, int localFrame,
+void SampleChannel::parseAction(recorder::actiona, int localFrame,
                int globalFrame, int quantize, bool mixerIsRunning)
 {
        if (readActions == false)
@@ -548,8 +633,11 @@ void SampleChannel::quantize(int index, int localFrame)
 
 int SampleChannel::getPosition()
 {
+       /* TODO - Opaque channel's count - everything in SampleChannel should be
+       frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
+
        if (status & ~(STATUS_EMPTY | STATUS_MISSING | STATUS_OFF)) // if is not (...)
-               return tracker - begin;
+               return (tracker - begin) / wave->getChannels();
        else
                return -1;
 }
@@ -673,6 +761,15 @@ void SampleChannel::setReadActions(bool v, bool recsStopOnChanHalt)
 /* -------------------------------------------------------------------------- */
 
 
+void SampleChannel::setOnEndPreviewCb(std::function<void()> f)
+{
+       onPreviewEnd = f;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void SampleChannel::setFadeIn(bool internal)
 {
        if (internal) mute_i = false;  // remove mute before fading in
@@ -713,14 +810,13 @@ void SampleChannel::setXFade(int frame)
 
 /* -------------------------------------------------------------------------- */
 
+/* On reset, if frame > 0 and in play, fill again pChan to create something like 
+this:
 
-/* on reset, if frame > 0 and in play, fill again pChan to create
- * something like this:
- *
- * |abcdefabcdefab*abcdefabcde|
- * [old data-----]*[new data--]
- *
- * */
+       |abcdefabcdefab*abcdefabcde|
+       [old data-----]*[new data--]
+
+*/
 
 void SampleChannel::reset(int frame)
 {
@@ -738,7 +834,7 @@ void SampleChannel::reset(int frame)
 void SampleChannel::empty()
 {
        status = STATUS_OFF;
-       if (wave) {
+       if (wave != nullptr) {
                delete wave;
                wave = nullptr;
        }
@@ -755,42 +851,22 @@ void SampleChannel::empty()
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::pushWave(Wave *w)
+void SampleChannel::pushWave(Wave* w, bool generateName)
 {
+       sendMidiLplay();     // FIXME - why here?!?!
        wave   = w;
        status = STATUS_OFF;
-       sendMidiLplay();     // FIXME - why here?!?!
        begin  = 0;
-       end    = wave->size;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::allocEmpty(int frames, int samplerate, int takeId)
-{
-       Wave *w = new Wave();
-       if (!w->allocEmpty(frames, samplerate))
-               return false;
-
-       w->name     = "TAKE-" + gu_itoa(takeId);
-       w->pathfile = gu_getCurrentPath() + G_SLASH + w->name;
-       wave        = w;
-       status      = STATUS_OFF;
-       begin       = 0;
-       end         = wave->size;
-
-       sendMidiLplay();  // FIXME - why here?!?!
-
-       return true;
+       end    = (wave->getSize() - 1) * wave->getChannels(); // TODO - Opaque channels' count
+       if (generateName)
+               generateUniqueSampleName();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::process(float *outBuffer, float *inBuffer)
+void SampleChannel::process(float* outBuffer, float* inBuffer)
 {
        /* If armed and inbuffer is not nullptr (i.e. input device available) and
   input monitor is on, copy input buffer to vChan: this enables the input
@@ -815,6 +891,42 @@ void SampleChannel::process(float *outBuffer, float *inBuffer)
 /* -------------------------------------------------------------------------- */
 
 
+void SampleChannel::preview(float* outBuffer)
+{
+       if (previewMode == G_PREVIEW_NONE)
+               return;
+
+       std::memset(vChanPreview, 0, sizeof(float) * bufferSize);
+
+       /* If the tracker exceedes the end point and preview is looped, split the 
+       rendering as in SampleChannel::reset(). */
+
+       if (trackerPreview + bufferSize >= end) {
+               int offset = end - trackerPreview;
+               trackerPreview = fillChan(vChanPreview, trackerPreview, 0, false);
+               trackerPreview = begin;
+               if (previewMode == G_PREVIEW_LOOP)
+                       trackerPreview = fillChan(vChanPreview, begin, offset, false);
+               else
+               if (previewMode == G_PREVIEW_NORMAL) {
+                       previewMode = G_PREVIEW_NONE;
+                       if (onPreviewEnd)
+                               onPreviewEnd();
+               }
+       }
+       else
+               trackerPreview = fillChan(vChanPreview, trackerPreview, 0, false);
+
+       for (int j=0; j<bufferSize; j+=2) {
+               outBuffer[j]   += vChanPreview[j]   * volume * calcPanning(0) * boost;
+               outBuffer[j+1] += vChanPreview[j+1] * volume * calcPanning(1) * boost;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void SampleChannel::kill(int frame)
 {
        if (wave != nullptr && status != STATUS_OFF) {
@@ -877,55 +989,6 @@ void SampleChannel::stop()
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::load(const char *file, int samplerate, int rsmpQuality)
-{
-       if (strcmp(file, "") == 0 || gu_isDir(file)) {
-               gu_log("[SampleChannel] file not specified\n");
-               return SAMPLE_LEFT_EMPTY;
-       }
-
-       if (strlen(file) > FILENAME_MAX)
-               return SAMPLE_PATH_TOO_LONG;
-
-       Wave *w = new Wave();
-
-       if (!w->open(file)) {
-               gu_log("[SampleChannel] %s: read error\n", file);
-               delete w;
-               return SAMPLE_READ_ERROR;
-       }
-
-       if (w->channels() > 2) {
-               gu_log("[SampleChannel] %s: unsupported multichannel wave\n", file);
-               delete w;
-               return SAMPLE_MULTICHANNEL;
-       }
-
-       if (!w->readData()) {
-               delete w;
-               return SAMPLE_READ_ERROR;
-       }
-
-       if (w->channels() == 1) /** FIXME: error checking  */
-               wfx_monoToStereo(w);
-
-       if (w->rate() != samplerate) {
-               gu_log("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n",
-                               w->rate(), samplerate);
-               w->resample(rsmpQuality, samplerate);
-       }
-
-       pushWave(w);
-       generateUniqueSampleName();
-
-       gu_log("[SampleChannel] %s loaded in channel %d\n", file, index);
-       return SAMPLE_LOADED_OK;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 int SampleChannel::readPatch(const string &basePath, int i,
                pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality)
 {
@@ -944,17 +1007,20 @@ int SampleChannel::readPatch(const string &basePath, int i,
        midiInPitch       = pch->midiInPitch;
   inputMonitor      = pch->inputMonitor;
 
-       int res = load((basePath + pch->samplePath).c_str(), samplerate, rsmpQuality);
-       if (res == SAMPLE_LOADED_OK) {
+  Wave *w = nullptr;
+  int res = waveManager::create(basePath + pch->samplePath, &w); 
+
+       if (res == G_RES_OK) {
+               pushWave(w);
                setBegin(pch->begin);
                setEnd  (pch->end);
                setPitch(pch->pitch);
        }
        else {
-               if (res == SAMPLE_LEFT_EMPTY)
+               if (res == G_RES_ERR_NO_DATA)
                        status = STATUS_EMPTY;
                else
-               if (res == SAMPLE_READ_ERROR)
+               if (res == G_RES_ERR_IO)
                        status = STATUS_MISSING;
                sendMidiLplay();  // FIXME - why sending MIDI lightning if sample status is wrong?
        }
@@ -1058,9 +1124,9 @@ int SampleChannel::writePatch(int i, bool isProject)
        patch::channel_t *pch = &patch::channels.at(pchIndex);
 
        if (wave != nullptr) {
-               pch->samplePath = wave->pathfile;
+               pch->samplePath = wave->getPath();
                if (isProject)
-                       pch->samplePath = gu_basename(wave->pathfile);  // make it portable
+                       pch->samplePath = gu_basename(wave->getPath());  // make it portable
        }
        else
                pch->samplePath = "";
@@ -1101,7 +1167,7 @@ int SampleChannel::fillChan(float *dest, int start, int offset, bool rewind)
                 * end) */
 
                if (start+bufferSize-offset <= end) {
-                       memcpy(dest+offset, wave->data+start, (bufferSize-offset)*sizeof(float));
+                       memcpy(dest+offset, wave->getData()+start, (bufferSize-offset)*sizeof(float));
                        position = start+bufferSize-offset;
                        if (rewind)
                                frameRewind = -1;
@@ -1111,7 +1177,7 @@ int SampleChannel::fillChan(float *dest, int start, int offset, bool rewind)
                 * is smaller than 'dest' */
 
                else {
-                       memcpy(dest+offset, wave->data+start, (end-start)*sizeof(float));
+                       memcpy(dest+offset, wave->getData()+start, (end-start)*sizeof(float));
                        position = end;
                        if (rewind)
                                frameRewind = end-start+offset;
@@ -1119,7 +1185,7 @@ int SampleChannel::fillChan(float *dest, int start, int offset, bool rewind)
        }
        else {
 
-               rsmp_data.data_in       = wave->data+start;         // source data
+               rsmp_data.data_in       = wave->getData()+start;    // source data
                rsmp_data.input_frames  = (end-start)/2;            // how many readable bytes
                rsmp_data.data_out      = dest+offset;              // destination (processed data)
                rsmp_data.output_frames = (bufferSize-offset)/2;    // how many bytes to process
index f8203ef90a47c353c466e28ed92eeb8d97d3e9df..8435dfe8ec9ef3c3d0edc0ab08c082829a9de586 100644 (file)
@@ -29,6 +29,7 @@
 #define G_SAMPLE_CHANNEL_H
 
 
+#include <functional>
 #include <samplerate.h>
 #include "channel.h"
 
@@ -44,33 +45,46 @@ private:
        /* rsmp_state, rsmp_data
         * structs from libsamplerate */
 
-       SRC_STATE *rsmp_state;
+       SRC_STATErsmp_state;
        SRC_DATA   rsmp_data;
 
        /* pChan
        Extra virtual channel for processing resampled data. */
 
-       float *pChan;
+       float* pChan;
+
+       /* pChan
+       Extra virtual channel for audio preview. */
+
+       float* vChanPreview;
 
        /* frameRewind
        Exact frame in which a rewind occurs. */
 
        int frameRewind;
 
+       int   begin;
+       int   end;
        float boost;
        float pitch;
+       int   trackerPreview;  // chan position for audio preview
+
+       /* onPreviewEnd
+       A callback fired when audio preview ends. */
+
+       std::function<void()> onPreviewEnd;
 
        /* fillChan
-        * copy from wave to *dest and resample data from wave, if necessary.
-        * Start to fill pChan from byte 'offset'. If rewind=false don't
-        * rewind internal tracker. Returns new sample position, in frames */
+       Fills 'dest' buffer at point 'offset' with wave data taken from 'start'. If 
+       rewind=false don't rewind internal tracker. Returns new sample position, 
+       in frames. It resamples data if pitch != 1.0f. */
 
-       int fillChan(float *dest, int start, int offset, bool rewind=true);
+       int fillChan(floatdest, int start, int offset, bool rewind=true);
 
        /* clearChan
         * set data to zero from start to bufferSize-1. */
 
-       void clearChan(float *dest, int start);
+       void clearChan(floatdest, int start);
 
        /* calcFadeoutStep
         * how many frames are left before the end of the sample? Is there
@@ -94,9 +108,10 @@ public:
        SampleChannel(int bufferSize, bool inputMonitor);
        ~SampleChannel();
 
-       void copy(const Channel *src, pthread_mutex_t *pluginMutex) override;
+       void copy(const Channel* src, pthread_mutex_t* pluginMutex) override;
        void clear() override;
-       void process(float *outBuffer, float *inBuffer) override;
+       void process(float* outBuffer, float* inBuffer) override;
+       void preview(float* outBuffer) override;
        void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
                bool forceStart, bool isUserGenerated) override;
        void kill(int frame) override;
@@ -106,17 +121,16 @@ public:
        void rewind() override;
        void setMute(bool internal) override;
        void unsetMute(bool internal) override;
-  int readPatch(const std::string &basePath, int i, pthread_mutex_t *pluginMutex,
+  int readPatch(const std::string& basePath, int i, pthread_mutex_t* pluginMutex,
     int samplerate, int rsmpQuality) override;
        int writePatch(int i, bool isProject) override;
        void quantize(int index, int localFrame) override;
        void onZero(int frame, bool recsStopOnChanHalt) override;
        void onBar(int frame) override;
-       void parseAction(giada::m::recorder::action *a, int localFrame, int globalFrame,
+       void parseAction(giada::m::recorder::actiona, int localFrame, int globalFrame,
                        int quantize, bool mixerIsRunning) override;
        bool canInputRec() override;
-
-       int load(const char *file, int samplerate, int rsmpQuality);
+       bool allocBuffers() override;
 
        void reset(int frame);
 
@@ -124,14 +138,15 @@ public:
         * prepare channel for fade, mixer will take care of the process
         * during master play. */
 
-       void  setFadeIn  (bool internal);
-       void  setFadeOut (int actionPostFadeout);
-       void  setXFade   (int frame);
+       void setFadeIn(bool internal);
+       void setFadeOut(int actionPostFadeout);
+       void setXFade(int frame);
 
        /* pushWave
-        * add a new wave to an existing channel. */
+       Adds a new wave to an existing channel. It also generates a unique name
+       on request. */
 
-       void pushWave(Wave *w);
+       void pushWave(Wave* w, bool generateName=true);
 
        /* getPosition
         * returns the position of an active sample. If EMPTY o MISSING
@@ -145,33 +160,26 @@ public:
 
        void sum(int frame, bool running);
 
-       /* setPitch
-        * updates the pitch value and chanStart+chanEnd accordingly. */
 
        void setPitch(float v);
        float getPitch();
-
-       /* setStart/end
-        * change begin/end read points in sample. */
-
-       void setBegin(int v);
-       void setEnd  (int v);
+       void setBegin(int f);
+       int getBegin();
+       void setEnd(int f);
+       int getEnd();
+       void setTrackerPreview(int f);
+       int getTrackerPreview() const;
 
        /* save
         * save sample to file. */
 
-       int save(const char *path);
+       int save(const charpath);
 
        /* hardStop
         * stop the channel immediately, no further checks. */
 
        void hardStop(int frame);
 
-       /* allocEmpty
-        * alloc an empty wave used in input recordings. */
-
-       bool allocEmpty(int frames, int samplerate, int takeId);
-
        /* setReadActions
         * if enabled (v == true), recorder will read actions from this channel. If
         * recsStopOnChanHalt == true, stop reading actions right away. */
@@ -181,23 +189,23 @@ public:
        void setBoost(float v);
        float getBoost();       
 
+       void setOnEndPreviewCb(std::function<void()> f);
+
        /* ------------------------------------------------------------------------ */
 
-       Wave  *wave;
-       int    tracker;         // chan position
-       int    begin;
-       int    end;
-       int    mode;            // mode: see const.h
-       bool   qWait;           // quantizer wait
-       bool   fadeinOn;
-       float  fadeinVol;
-       bool   fadeoutOn;
-       float  fadeoutVol;      // fadeout volume
-       int    fadeoutTracker;  // tracker fadeout, xfade only
-       float  fadeoutStep;     // fadeout decrease
-  int    fadeoutType;     // xfade or fadeout
-  int           fadeoutEnd;      // what to do when fadeout ends
-  bool   inputMonitor;
+       Wave* wave;
+       int   tracker;         // chan position
+       int   mode;            // mode: see const.h
+       bool  qWait;           // quantizer wait
+       bool  fadeinOn;
+       float fadeinVol;
+       bool  fadeoutOn;
+       float fadeoutVol;      // fadeout volume
+       int   fadeoutTracker;  // tracker fadeout, xfade only
+       float fadeoutStep;     // fadeout decrease
+  int   fadeoutType;     // xfade or fadeout
+  int          fadeoutEnd;      // what to do when fadeout ends
+  bool  inputMonitor;
 
        /* midi stuff */
 
index 1f821b6859819d98ecf50dd2658d92517bda7e39..28edd18630eb8bda376d79ea0ed6fc0c18f876bc 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include <cstdio>
-#include <cstdlib>
+#include <cassert>
 #include <cstring>  // memcpy
-#include <cmath>
-#include <samplerate.h>
 #include "../utils/fs.h"
 #include "../utils/log.h"
-#include "init.h"
 #include "const.h"
 #include "wave.h"
 
@@ -43,244 +39,156 @@ using std::string;
 
 
 Wave::Wave()
-: data     (nullptr),
-       size     (0),
-       isLogical(0),
-       isEdited (0) {}
+: m_data   (nullptr),
+       m_size   (0),
+       m_logical(0),
+       m_edited (0) {}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-Wave::~Wave()
+Wave::Wave(float* data, int size, int channels, int rate, int bits, 
+       const std::string& path)
+: m_data    (data),
+  m_size    (size),
+  m_channels(channels),
+  m_rate    (rate),
+  m_bits    (bits),
+  m_logical (false),
+  m_edited  (false),
+  m_path    (path),
+  m_name    (gu_stripExt(gu_basename(path)))
 {
-       clear();
-}
+}      
 
 
 /* -------------------------------------------------------------------------- */
 
 
-Wave::Wave(const Wave &other)
-: data     (nullptr),
-       size     (0),
-       isLogical(false),
-       isEdited (false)
+Wave::~Wave()
 {
-       size = other.size;
-       data = new float[size];
-       memcpy(data, other.data, size * sizeof(float));
-       memcpy(&inHeader, &other.inHeader, sizeof(other.inHeader));
-       pathfile = other.pathfile;
-       name = other.name;
-       isLogical = true;
+       clear();
 }
 
+
 /* -------------------------------------------------------------------------- */
 
 
-int Wave::open(const char *f)
+Wave::Wave(const Wave& other)
+: m_data    (nullptr),
+       m_size    (other.m_size),
+       m_channels(other.m_channels),
+  m_rate    (other.m_rate),
+  m_bits    (other.m_bits),    
+       m_logical (true),   // a cloned wave does not exist on disk
+       m_edited  (false),
+       m_path    (other.m_path),
+       m_name    (other.m_name)
 {
-       pathfile = f;
-       name     = gu_stripExt(gu_basename(f));
-       fileIn   = sf_open(f, SFM_READ, &inHeader);
-
-       if (fileIn == nullptr) {
-               gu_log("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn));
-               pathfile = "";
-               name     = "";
-               return 0;
-       }
-
-       isLogical = false;
-       isEdited  = false;
-
-       return 1;
+       m_data = new float[m_size];
+       memcpy(m_data, other.m_data, m_size * sizeof(float));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-/* how to read and write with libsndfile:
- *
- * a frame consists of all items (samples) that belong to the same
- * point in time. So in each frame there are as many items as there
- * are channels.
- *
- * Quindi:
- *     frame  = [item, item, ...]
- * In pratica:
- *  frame1 = [itemLeft, itemRight]
- *     frame2 = [itemLeft, itemRight]
- *     ...
- */
-
-int Wave::readData()
+
+void Wave::clear()
 {
-       size = inHeader.frames * inHeader.channels;
-       data = (float *) malloc(size * sizeof(float));
-       if (data == nullptr) {
-               gu_log("[wave] unable to allocate memory\n");
-               return 0;
-       }
-
-       if (sf_read_float(fileIn, data, size) != size)
-               gu_log("[wave] warning: incomplete read!\n");
-
-       sf_close(fileIn);
-       return 1;
+       free();
+       m_path = "";
+       m_size = 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Wave::writeData(const char *f)
+void Wave::free()
 {
-       /* prepare the header for output file */
-
-       outHeader.samplerate = inHeader.samplerate;
-       outHeader.channels   = inHeader.channels;
-       outHeader.format     = inHeader.format;
-
-       fileOut = sf_open(f, SFM_WRITE, &outHeader);
-       if (fileOut == nullptr) {
-               gu_log("[wave] unable to open %s for exporting\n", f);
-               return 0;
-       }
-
-       int out = sf_write_float(fileOut, data, size);
-       if (out != (int) size) {
-               gu_log("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut));
-               return 0;
-       }
-
-       isLogical = false;
-       isEdited  = false;
-       sf_close(fileOut);
-       return 1;
+       if (m_data == nullptr) 
+               return;
+       delete[] m_data;
+       m_data = nullptr;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Wave::clear()
+string Wave::getBasename(bool ext) const
 {
-       if (data != nullptr) {
-               free(data);
-               data     = nullptr;
-               pathfile = "";
-               size     = 0;
-       }
+       return ext ? gu_basename(m_path) : gu_stripExt(gu_basename(m_path));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Wave::allocEmpty(unsigned __size, unsigned samplerate)
+void Wave::setName(const string& n)
 {
-       /* the caller must pass a __size for stereo values */
-
-       /// FIXME - this way if malloc fails size becomes wrong
-       size = __size;
-       data = (float *) malloc(size * sizeof(float));
-       if (data == nullptr) {
-               gu_log("[wave] unable to allocate memory\n");
-               return 0;
-       }
+       string ext = gu_getExt(m_path);
+       m_name = gu_stripExt(gu_basename(n));
+       m_path = gu_dirname(m_path) + G_SLASH + m_name + "." + ext;
+       m_logical  = true;
 
-       memset(data, 0, sizeof(float) * size); /// FIXME - is it useful?
-
-       inHeader.samplerate = samplerate;
-       inHeader.channels   = 2;
-       inHeader.format     = SF_FORMAT_WAV | SF_FORMAT_FLOAT; // wave only
-
-       isLogical = true;
-       return 1;
+       /* A wave with updated m_name must become logical, since the underlying file
+       does not exist yet. */
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Wave::resample(int quality, int newRate)
-{
-       float ratio = newRate / (float) inHeader.samplerate;
-       int newSize = ceil(size * ratio);
-       if (newSize % 2 != 0)   // libsndfile goes crazy with odd size in case of saving
-               newSize++;
-
-       float *tmp = (float *) malloc(newSize * sizeof(float));
-       if (!tmp) {
-               gu_log("[wave] unable to allocate memory for resampling\n");
-               return -1;
-       }
-
-       SRC_DATA src_data;
-       src_data.data_in       = data;
-       src_data.input_frames  = size/2;     // in frames, i.e. /2 (stereo)
-       src_data.data_out      = tmp;
-       src_data.output_frames = newSize/2;  // in frames, i.e. /2 (stereo)
-       src_data.src_ratio     = ratio;
-
-       gu_log("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2);
-
-       int ret = src_simple(&src_data, quality, 2);
-       if (ret != 0) {
-               gu_log("[wave] resampling error: %s\n", src_strerror(ret));
-               return 0;
-       }
-
-       free(data);
-       data = tmp;
-       size = newSize;
-       inHeader.samplerate = newRate;
-       return 1;
-}
+int Wave::getRate() const { return m_rate; }
+int Wave::getChannels() const { return m_channels; }
+std::string Wave::getPath() const { return m_path; }
+std::string Wave::getName() const { return m_name; }
+float* Wave::getData() const { return m_data; }
+int Wave::getSize() const { return m_size / m_channels; }
+int Wave::getBits() const { return m_bits; }
+bool Wave::isLogical() const { return m_logical; }
+bool Wave::isEdited() const { return m_edited; }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-string Wave::basename(bool ext) const
+int Wave::getDuration() const
 {
-       return ext ? gu_basename(pathfile) : gu_stripExt(gu_basename(pathfile));
-}
-
-string Wave::extension() const
-{
-       return gu_getExt(pathfile);
+       return m_size / m_channels / m_rate;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Wave::updateName(const char *n)
+float* Wave::getFrame(int f) const
 {
-       string ext = gu_getExt(pathfile);
-       name       = gu_stripExt(gu_basename(n));
-       pathfile   = gu_dirname(pathfile) + G_SLASH + name + "." + ext;
-       isLogical  = true;
+       assert(f >= 0);
+       assert(f < getSize());  
 
-       /* a wave with updated name must become logical, since the underlying
-        * file does not exist yet. */
+       f *= m_channels;    // convert frame to sample
+       return m_data + f;  // i.e. a pointer to m_data[f]
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int  Wave::rate    () { return inHeader.samplerate; }
-int  Wave::channels() { return inHeader.channels; }
-int  Wave::frames  () { return inHeader.frames; }
+void Wave::setRate(int v) { m_rate = v; }
+void Wave::setChannels(int v) { m_channels = v; }
+void Wave::setPath(const string& p) { m_path = p; }
+void Wave::setLogical(bool l) { m_logical = l; }
+void Wave::setEdited(bool e) { m_edited = e; }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Wave::rate    (int v) { inHeader.samplerate = v; }
-void Wave::channels(int v) { inHeader.channels = v; }
-void Wave::frames  (int v) { inHeader.frames = v; }
+void Wave::setData(float* d, int size) 
+{ 
+       m_data = d; 
+       m_size = size;
+}
index 11c721d51eb9a09489a360ddec9bf5577bdfbaee..7c9f56b9031451bec9e71a4caf339ca0fef41f87 100644 (file)
 
 #include <sndfile.h>
 #include <string>
+#include "const.h"
 
 
 class Wave
 {
 private:
 
-       SNDFILE *fileIn;
-       SNDFILE *fileOut;
-       SF_INFO  inHeader;
-       SF_INFO  outHeader;
+       float* m_data;
+       int m_size;                 // Wave size in bytes (size in stereo: size / 2)
+       int m_channels;
+       int m_rate;
+       int m_bits;
+       bool m_logical;   // memory only (a take)
+       bool m_edited;    // edited via editor
+       
+       std::string m_path; // E.g. /path/to/my/sample.wav
+       std::string m_name; // Sample name (can be changed)
 
 public:
 
        Wave();
+       Wave(float* data, int size, int channels, int rate, int bits, const std::string& path);
        ~Wave();
-       Wave(const Wave &other);
+       Wave(const Waveother);
 
-       std::string pathfile; // full path + sample name
-       std::string name;                       // sample name (changeable)
+       void setRate(int v);
+       void setChannels(int v);
+       void setPath(const std::string& p);
+       void setName(const std::string& p);
+       void setData(float* data, int size);
+       void setLogical(bool l);
+       void setEdited(bool e);
 
-       float *data;
-       int    size;                      // wave size (size in stereo: size / 2)
-       bool   isLogical;   // memory only (a take)
-       bool   isEdited;    // edited via editor
+       std::string getBasename(bool ext=false) const;
+       int getRate() const;
+       int getChannels() const;
+       std::string getPath() const;    
+       std::string getName() const;
+       int getBits() const;
+       float* getData() const;
+       int getSize() const;        // with channels count
+       int getDuration() const;
+       bool isLogical() const;
+       bool isEdited() const;
 
-       int  rate    ();
-       int  channels();
-       int  frames  ();
-       void rate    (int v);
-       void channels(int v);
-       void frames  (int v);
+       /* clear
+       Resets Wave to init state. */
 
-       std::string basename(bool ext=false) const;
-       std::string extension() const;
+       void clear();
 
-       void updateName(const char *n);
-       int  open      (const char *f);
-       int  readData  ();
-       int      writeData (const char *f);
-       void clear     ();
+       /* free
+       Frees memory, leaving everything else untouched. */
 
-       /* allocEmpty
-        * alloc an empty waveform. */
+       void free();
 
-       int allocEmpty(unsigned size, unsigned samplerate);
+       /* getFrame
+       Given a frame 'f', returns a pointer to it. This is useful for digging inside
+       a frame, i.e. parsing each channel. How to use it:
 
-       /* resample
-        * simple algorithm for one-shot resampling. */
+               float* frame = w->getFrame(40);
+               for (int i=0; i<w->getChannels(); i++)
+                       ... frame[i] ...
+       */
+
+       float* getFrame(int f) const;
 
-       int resample(int quality, int newRate);
 };
 
 #endif
index d31bead32c2fee5af645b9ef62410c25ee6d54e3..2f0e8ea62b4a5557a294f8d22d2869f7b1bf3950 100644 (file)
@@ -2,8 +2,6 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * waveFx
- *
  * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
 #include "waveFx.h"
 
 
-float wfx_normalizeSoft(Wave *w)
+namespace giada {
+namespace m {
+namespace wfx
+{
+namespace
+{
+void fadeFrame(Wave* w, int i, float val)
+{
+       float* frame = w->getFrame(i);
+       for (int j=0; j<w->getChannels(); j++)
+               frame[j] *= val;
+}
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+float normalizeSoft(Wave *w)
 {
        float peak = 0.0f;
        float abs  = 0.0f;
-       for (int i=0; i<w->size; i++) { // i++: both L and R samples
-               abs = fabs(w->data[i]);
+       for (int i=0; i<w->getSize(); i++) {
+               float* frame = w->getFrame(i);
+               for (int j=0; j<w->getChannels(); j++) // Find highest value in any channel
+                       abs = fabs(frame[j]);
                if (abs > peak)
                        peak = abs;
        }
@@ -56,48 +76,47 @@ float wfx_normalizeSoft(Wave *w)
 /* -------------------------------------------------------------------------- */
 
 
-bool wfx_monoToStereo(Wave *w)
+int monoToStereo(Wave* w)
 {
-       unsigned newSize = w->size * 2;
-       float *dataNew = (float *) malloc(newSize * sizeof(float));
-       if (dataNew == nullptr) {
-               gu_log("[wfx] unable to allocate memory for mono>stereo conversion\n");
-               return 0;
+       if (w->getChannels() >= G_DEFAULT_AUDIO_CHANS)
+               return G_RES_OK;
+
+       unsigned newSize = w->getSize() * G_DEFAULT_AUDIO_CHANS;
+       
+       float* newData = new (std::nothrow) float[newSize];
+       if (newData == nullptr) {
+               gu_log("[wfx::monoToStereo] unable to allocate memory!\n");
+               return G_RES_ERR_MEMORY;
        }
 
-       for (int i=0, j=0; i<w->size; i++) {
-               dataNew[j]   = w->data[i];
-               dataNew[j+1] = w->data[i];
-               j+=2;
+       for (int i=0, k=0; i<w->getSize(); i++, k+=G_DEFAULT_AUDIO_CHANS) {
+               float* frame = w->getFrame(i);
+               for (int j=0; j<G_DEFAULT_AUDIO_CHANS; j++)
+                       newData[k+j] = frame[j];
        }
 
-       free(w->data);
-       w->data = dataNew;
-       w->size = newSize;
-       w->frames(w->frames()*2);
-       w->channels(2);
+       w->free();
+       w->setData(newData, newSize);
+       w->setChannels(G_DEFAULT_AUDIO_CHANS);
 
-       return 1;
+       return G_RES_OK;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void wfx_silence(Wave *w, int a, int b)
+void silence(Wave* w, int a, int b)
 {
-       /* stereo values */
-       a = a * 2;
-       b = b * 2;
+       gu_log("[wfx::silence] silencing from %d to %d\n", a, b);
 
-       gu_log("[wfx] silencing from %d to %d\n", a, b);
-
-       for (int i=a; i<b; i+=2) {
-               w->data[i]   = 0.0f;
-               w->data[i+1] = 0.0f;
+       for (int i=a; i<b; i++) {
+               float* frame = w->getFrame(i);
+               for (int j=0; j<w->getChannels(); j++)  
+                       frame[j] = 0.0f;
        }
 
-       w->isEdited = true;
+       w->setEdited(true);
 
        return;
 }
@@ -106,120 +125,109 @@ void wfx_silence(Wave *w, int a, int b)
 /* -------------------------------------------------------------------------- */
 
 
-int wfx_cut(Wave *w, int a, int b)
+int cut(Wave* w, int a, int b)
 {
-       a = a * 2;
-       b = b * 2;
-
        if (a < 0) a = 0;
-       if (b > w->size) b = w->size;
+       if (b > w->getSize()) b = w->getSize();
 
-       /* create a new temp wave and copy there the original one, skipping
-        * the a-b range */
+       /* Create a new temp wave and copy there the original one, skipping the a-b 
+       range. */
 
-       unsigned newSize = w->size-(b-a);
-       float *temp = (float *) malloc(newSize * sizeof(float));
-       if (temp == nullptr) {
-               gu_log("[wfx] unable to allocate memory for cutting\n");
-               return 0;
+       unsigned newSize = (w->getSize() - (b - a)) * w->getChannels();
+       float* newData = new (std::nothrow) float[newSize];
+       if (newData == nullptr) {
+               gu_log("[wfx::cut] unable to allocate memory!\n");
+               return G_RES_ERR_MEMORY;
        }
 
-       gu_log("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", 
-               a, b, newSize, newSize/2);
+       gu_log("[wfx::cut] cutting from %d to %d\n", a, b);
 
-       for (int i=0, k=0; i<w->size; i++) {
-               if (i < a || i >= b) {                         // left margin always included, in order to keep
-                       temp[k] = w->data[i];   // the stereo pair
-                       k++;
+       for (int i=0, k=0; i<w->getSize(); i++) {
+               if (i < a || i >= b) {
+                       float* frame = w->getFrame(i);
+                       for (int j=0; j<w->getChannels(); j++)  
+                               newData[k+j] = frame[j];
+                       k += w->getChannels();
                }
        }
 
-       free(w->data);
-       w->data = temp;
-       w->size = newSize;
-       //w->inHeader.frames -= b-a;
-       w->frames(w->frames() - b - a);
-       w->isEdited = true;
-
-       gu_log("[wfx] cutting done\n");
+       w->free();
+       w->setData(newData, newSize);
+       w->setEdited(true);
 
-       return 1;
+       return G_RES_OK;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int wfx_trim(Wave *w, int a, int b)
+int trim(Wave* w, int a, int b)
 {
-       a = a * 2;
-       b = b * 2;
-
        if (a < 0) a = 0;
-       if (b > w->size) b = w->size;
+       if (b > w->getSize()) b = w->getSize();
 
-       int newSize = b - a;
-       float *temp = (float *) malloc(newSize * sizeof(float));
-       if (temp == nullptr) {
+       int newSize = (b - a) * w->getChannels();
+       float* newData = new (std::nothrow) float[newSize];
+       if (newData == nullptr) {
                gu_log("[wfx] unable to allocate memory for trimming\n");
-               return 0;
+               return G_RES_ERR_MEMORY;
        }
 
-       gu_log("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a);
+       gu_log("[wfx::trim] trimming from %d to %d (area = %d)\n", a, b, b-a);
 
-       for (int i=a, k=0; i<b; i++, k++)
-               temp[k] = w->data[i];
+       for (int i=a, k=0; i<b; i++, k+=w->getChannels()) {
+               float* frame = w->getFrame(i);
+               for (int j=0; j<w->getChannels(); j++)
+                       newData[k+j] = frame[j];
+       }
 
-       free(w->data);
-       w->data = temp;
-       w->size = newSize;
-       //w->inHeader.frames = b-a;
-       w->frames(b - a);
-       w->isEdited = true;
+       w->free();
+       w->setData(newData, newSize);
+       w->setEdited(true);
 
-       return 1;
+       return G_RES_OK;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void wfx_fade(Wave *w, int a, int b, int type)
+void fade(Wave* w, int a, int b, int type)
 {
-       float m = type == 0 ? 0.0f : 1.0f;
-       float d = 1.0f/(float)(b-a);
-       if (type == 1)
-               d = -d;
-
-       a *= 2;
-       b *= 2;
-
-       for (int i=a; i<b; i+=2) {
-               w->data[i]   *= m;
-               w->data[i+1] *= m;
-               m += d;
-       }
+       gu_log("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b-a);
 
-  w->isEdited = true;
+       float m = 0.0f;
+       float d = 1.0f / (float) (b - a);
+
+       if (type == FADE_IN)
+               for (int i=a; i<=b; i++, m+=d)
+                       fadeFrame(w, i, m);
+       else
+               for (int i=b; i>=a; i--, m+=d)
+                       fadeFrame(w, i, m);             
+
+  w->setEdited(true);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void wfx_smooth(Wave *w, int a, int b)
+void smooth(Wave* w, int a, int b)
 {
-       int d = 32;  // 64 if stereo data
-
-       /* do nothing if fade edges (both of 32 samples) are > than selected
-        * portion of wave. d*2 => count both edges, (b-a)*2 => stereo
-        * values. */
+       /* Do nothing if fade edges (both of SMOOTH_SIZE samples) are > than selected 
+       portion of wave. SMOOTH_SIZE*2 to count both edges. */
 
-       if (d*2 > (b-a)*2) {
-               gu_log("[WFX] selection is too small, nothing to do\n");
+       if (SMOOTH_SIZE*2 > (b-a)) {
+               gu_log("[wfx::smooth] selection is too small, nothing to do\n");
                return;
        }
 
-       wfx_fade(w, a, a+d, 0);
-       wfx_fade(w, b-d, b, 1);
+       fade(w, a, a+SMOOTH_SIZE, FADE_IN);
+       fade(w, b-SMOOTH_SIZE, b, FADE_OUT);
+
+       w->setEdited(true);
 }
+
+}}}; // giada::m::wfx::
\ No newline at end of file
index 4b7dee82377c697791457abe2ae6f50df8759e5c..43600528521b60111c1c013334f68f3637cf4a60 100644 (file)
@@ -2,8 +2,6 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * waveFx
- *
  * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
 class Wave;
 
 
-/* normalizeSoft
- * normalize the wave by returning the dB value for the boost volume. It
- * doesn't deal with data in memory. */
-
-float wfx_normalizeSoft(Wave *w);
-
-bool wfx_monoToStereo(Wave *w);
+namespace giada {
+namespace m {
+namespace wfx
+{
+static const int FADE_IN  = 0;
+static const int FADE_OUT = 1;
+static const int SMOOTH_SIZE = 32;
 
-void wfx_silence(Wave *w, int a, int b);
+/* normalizeSoft
+Normalizes the wave by returning the dB value for the boost volume. */
 
-int wfx_cut(Wave *w, int a, int b);
+float normalizeSoft(Wave* w);
 
-int wfx_trim(Wave *w, int a, int b);
+int monoToStereo(Wave* w);
+void silence(Wave* w, int a, int b);
+int cut(Wave* w, int a, int b);
+int trim(Wave* w, int a, int b);
 
 /* fade
  * fade in or fade out selection. Fade In = type 0, Fade Out = type 1 */
 
-void wfx_fade(Wave *w, int a, int b, int type);
+void fade(Wave* w, int a, int b, int type);
 
 /* smooth
  * smooth edges of selection. */
 
-void wfx_smooth(Wave *w, int a, int b);
-
+void smooth(Wave* w, int a, int b);
+}}}; // giada::m::wfx::
 
 #endif
diff --git a/src/core/waveManager.cpp b/src/core/waveManager.cpp
new file mode 100644 (file)
index 0000000..bd102b4
--- /dev/null
@@ -0,0 +1,222 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include <sndfile.h>
+#include <samplerate.h>
+#include "../utils/log.h"
+#include "../utils/fs.h"
+#include "const.h"
+#include "wave.h"
+#include "waveFx.h"
+#include "waveManager.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace m {
+namespace waveManager
+{
+namespace
+{
+int getBits(SF_INFO& header)
+{
+       if      (header.format & SF_FORMAT_PCM_S8)
+               return 8;
+       else if (header.format & SF_FORMAT_PCM_16)
+               return 16;      
+       else if (header.format & SF_FORMAT_PCM_24)
+               return 24;      
+       else if (header.format & SF_FORMAT_PCM_32)
+               return 32;      
+       else if (header.format & SF_FORMAT_PCM_U8)
+               return 8;       
+       else if (header.format & SF_FORMAT_FLOAT)
+               return 32;
+       else if (header.format & SF_FORMAT_DOUBLE)
+               return 64;
+       return 0;
+}
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+int create(const string& path, Wave** out)
+{
+       if (path == "" || gu_isDir(path)) {
+               gu_log("[waveManager::create] malformed path (was '%s')\n", path.c_str());
+               return G_RES_ERR_NO_DATA;
+       }
+
+       if (path.size() > FILENAME_MAX)
+               return G_RES_ERR_PATH_TOO_LONG;
+
+       SF_INFO header;
+       SNDFILE* fileIn = sf_open(path.c_str(), SFM_READ, &header);
+
+       if (fileIn == nullptr) {
+               gu_log("[waveManager::create] unable to read %s. %s\n", path.c_str(), sf_strerror(fileIn));
+               return G_RES_ERR_IO;
+       }
+
+       if (header.channels > 2) {
+               gu_log("[waveManager::create] unsupported multi-channel sample\n");
+               return G_RES_ERR_WRONG_DATA;
+       }
+
+       /* Libsndfile's frame structure:
+
+       frame1 = [leftChannel, rightChannel]
+       frame2 = [leftChannel, rightChannel]
+       ... */
+       
+       int size = header.frames * header.channels;
+       float* data = new (std::nothrow) float[size];
+       if (data == nullptr) {
+               gu_log("[waveManager::create] unable to allocate memory\n");
+               return G_RES_ERR_MEMORY;
+       }
+
+       if (sf_read_float(fileIn, data, size) != size)
+               gu_log("[waveManager::create] warning: incomplete read!\n");
+
+       sf_close(fileIn);
+
+       Wave* wave = new Wave(data, size, header.channels, header.samplerate, 
+               getBits(header), path);
+
+       if (header.channels == 1 && !wfx::monoToStereo(wave)) {
+               delete wave;
+               return G_RES_ERR_PROCESSING;
+       }
+
+       *out = wave;
+
+       gu_log("[waveManager::create] new Wave created, %d frames\n", wave->getSize());
+
+       return G_RES_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int createEmpty(int size, int samplerate, const string& name, Wave** out)
+{
+       float* data = new (std::nothrow) float[size];
+       if (data == nullptr) {
+               gu_log("[waveManager::createEmpty] unable to allocate memory\n");
+               return G_RES_ERR_MEMORY;
+       }
+
+       Wave *wave = new Wave(data, size, 2, samplerate, G_DEFAULT_BIT_DEPTH, "");
+       wave->setLogical(true); 
+       wave->setName(name);
+       wave->setPath(gu_getCurrentPath() + G_SLASH + wave->getName());
+
+       *out = wave;
+
+       gu_log("[waveManager::createEmpty] new empty Wave created, %d frames\n", size);
+
+       return G_RES_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int resample(Wave* w, int quality, int samplerate)
+{
+       float ratio = samplerate / (float) w->getRate();
+       int newSizeFrames  = ceil(w->getSize() * ratio);
+       int newSizeSamples = newSizeFrames * w->getChannels();
+
+       float* newData = new (std::nothrow) float[newSizeSamples];
+       if (newData == nullptr) {
+               gu_log("[waveManager::resample] unable to allocate memory\n");
+               return G_RES_ERR_MEMORY;
+       }
+
+       SRC_DATA src_data;
+       src_data.data_in       = w->getData();
+       src_data.input_frames  = w->getSize();
+       src_data.data_out      = newData;
+       src_data.output_frames = newSizeFrames;
+       src_data.src_ratio     = ratio;
+
+       gu_log("[waveManager::resample] resampling: new size=%d (%d frames)\n", 
+               newSizeSamples, newSizeFrames);
+
+       int ret = src_simple(&src_data, quality, w->getChannels());
+       if (ret != 0) {
+               gu_log("[waveManager::resample] resampling error: %s\n", src_strerror(ret));
+               delete[] newData;
+               return G_RES_ERR_PROCESSING;
+       }
+
+       w->free();
+       w->setData(newData, newSizeSamples);
+       w->setRate(samplerate);
+
+       return G_RES_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int save(Wave* w, const string& path)
+{
+       SF_INFO header;
+       header.samplerate = w->getRate();
+       header.channels   = w->getChannels();
+       header.format     = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
+
+       SNDFILE* file = sf_open(path.c_str(), SFM_WRITE, &header);
+       if (file == nullptr) {
+               gu_log("[waveManager::save] unable to open %s for exporting: %s\n", 
+                       path.c_str(), sf_strerror(file));
+               return G_RES_ERR_IO;
+       }
+
+       if (sf_writef_float(file, w->getData(), w->getSize()) != w->getSize())
+               gu_log("[waveManager::save] warning: incomplete write!\n");
+
+       sf_close(file);
+
+       w->setLogical(false);
+       w->setEdited(false);
+       
+       return G_RES_OK;
+}
+}}}; // giada::m::waveManager
diff --git a/src/core/waveManager.h b/src/core/waveManager.h
new file mode 100644 (file)
index 0000000..b9e8a05
--- /dev/null
@@ -0,0 +1,57 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_WAVE_MANAGER_H
+#define G_WAVE_MANAGER_H
+
+
+#include <string>
+
+
+class Wave;
+
+
+namespace giada {
+namespace m {
+namespace waveManager
+{
+/* create
+Creates a new Wave object with data read from file 'path'. */
+
+int create(const std::string& path, Wave** out);
+
+/* createEmpty
+Creates a new silent Wave object. Note: 'size' must take 2 channels into account
+(stereo). */
+
+int createEmpty(int size, int samplerate, const std::string& name, Wave** out);
+int resample(Wave* w, int quality, int samplerate); 
+int save(Wave* w, const std::string& path);
+
+}}}; // giada::m::waveManager
+
+#endif
\ No newline at end of file
index eec216b920b57e0b0d08258e66c25afb808bfa7e..d9c13d5f1304f570bd510c98b0b57ea7f6912f6d 100644 (file)
@@ -45,6 +45,7 @@
 #include "../gui/elems/mainWindow/keyboard/channelButton.h"
 #include "../utils/gui.h"
 #include "../utils/fs.h"
+#include "../utils/log.h"
 #include "../core/kernelAudio.h"
 #include "../core/mixerHandler.h"
 #include "../core/mixer.h"
 #include "../core/sampleChannel.h"
 #include "../core/midiChannel.h"
 #include "../core/plugin.h"
+#include "../core/waveManager.h"
 #include "main.h"
 #include "channel.h"
 
 
-extern gdMainWindow *G_MainWin;
+extern gdMainWindowG_MainWin;
 
 
 using std::string;
@@ -70,7 +72,7 @@ using namespace giada::m;
 static bool __soloSession__ = false;
 
 
-int glue_loadChannel(SampleChannel *ch, const string &fname)
+int glue_loadChannel(SampleChannel* ch, const string& fname)
 {
   /* Always stop a channel before loading a new sample in it. This will prevent
   issues if tracker is outside the boundaries of the new sample -> segfault. */
@@ -82,10 +84,24 @@ int glue_loadChannel(SampleChannel *ch, const string &fname)
 
        conf::samplePath = gu_dirname(fname);
 
-       int result = ch->load(fname.c_str(), conf::samplerate, conf::rsmpQuality);
+       Wave* wave = nullptr;
+       int result = waveManager::create(fname, &wave); 
+       if (result != G_RES_OK)
+               return result;
+
+       if (wave->getRate() != conf::samplerate) {
+               gu_log("[glue_loadChannel] input rate (%d) != system rate (%d), conversion needed\n",
+                       wave->getRate(), conf::samplerate);
+               result = waveManager::resample(wave, conf::rsmpQuality, conf::samplerate); 
+               if (result != G_RES_OK) {
+                       delete wave;
+                       return result;
+               }
+       }
 
-       if (result == SAMPLE_LOADED_OK)
-               G_MainWin->keyboard->updateChannel(ch->guiChannel);
+       ch->pushWave(wave);
+
+       G_MainWin->keyboard->updateChannel(ch->guiChannel);
 
        return result;
 }
@@ -94,10 +110,10 @@ int glue_loadChannel(SampleChannel *ch, const string &fname)
 /* -------------------------------------------------------------------------- */
 
 
-Channel *glue_addChannel(int column, int type)
+Channelglue_addChannel(int column, int type)
 {
-       Channel *ch     = mh::addChannel(type);
-       geChannel *gch  = G_MainWin->keyboard->addChannel(column, ch);
+       Channelch     = mh::addChannel(type);
+       geChannelgch  = G_MainWin->keyboard->addChannel(column, ch);
        ch->guiChannel  = gch;
        return ch;
 }
@@ -106,7 +122,7 @@ Channel *glue_addChannel(int column, int type)
 /* -------------------------------------------------------------------------- */
 
 
-void glue_deleteChannel(Channel *ch)
+void glue_deleteChannel(Channelch)
 {
   if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
     return;
index 3abaa5861efdc06dd27138d5551da77fc53cf5c5..efc8c67731a85215db932c9416187f0d1addd437 100644 (file)
@@ -100,7 +100,7 @@ void glue_setBpm(float v)
   double fPpart = modf(v, &fIpart);
   int iIpart = fIpart;
   int iPpart = ceilf(fPpart);
-  glue_setBpm(gu_itoa(iIpart).c_str(), gu_itoa(iPpart).c_str());
+  glue_setBpm(gu_toString(iIpart).c_str(), gu_toString(iPpart).c_str());
 }
 
 
@@ -223,7 +223,9 @@ void glue_clearAllRecs()
 
 void glue_resetToInitState(bool resetGui, bool createColumns)
 {
+       gu_closeAllSubwindows();
        mixer::close();
+       clock::init(conf::samplerate, conf::midiTCfps);
        mixer::init(clock::getTotalFrames(), kernelAudio::getRealBufSize());
        recorder::init();
 #ifdef WITH_VST
index fbbb7efc18ac1b075d136bf729f08879896d2fec..ecaade6fd1ae710d496d91fbe30d9f20a1fb26ba 100644 (file)
@@ -30,6 +30,7 @@
 #include "../gui/dialogs/gd_mainWindow.h"
 #include "../gui/dialogs/sampleEditor.h"
 #include "../gui/dialogs/gd_warnings.h"
+#include "../gui/elems/basics/button.h"
 #include "../gui/elems/sampleEditor/waveTools.h"
 #include "../gui/elems/sampleEditor/volumeTool.h"
 #include "../gui/elems/sampleEditor/boostTool.h"
@@ -62,6 +63,7 @@ gdSampleEditor* getSampleEditorWindow()
        return se;
 }
 
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -81,14 +83,15 @@ void setBeginEndChannel(SampleChannel* ch, int b, int e)
 
 void cut(SampleChannel* ch, int a, int b)
 {
-       if (!wfx_cut(ch->wave, a, b)) {
+       if (!wfx::cut(ch->wave, a, b)) {
                gdAlert("Unable to cut the sample!");
                return;
        }
-       setBeginEndChannel(ch, ch->begin, ch->end);
+       setBeginEndChannel(ch, ch->getBegin(), ch->getEnd());
        gdSampleEditor* gdEditor = getSampleEditorWindow();
-  gdEditor->waveTools->waveform->clearSel();
-  gdEditor->waveTools->waveform->refresh();
+       gdEditor->waveTools->waveform->clearSel();
+       gdEditor->waveTools->waveform->refresh();
+       gdEditor->updateInfo();
 }
 
 
@@ -97,7 +100,7 @@ void cut(SampleChannel* ch, int a, int b)
 
 void silence(SampleChannel* ch, int a, int b)
 {
-       wfx_silence(ch->wave, a, b);
+       wfx::silence(ch->wave, a, b);
        gdSampleEditor* gdEditor = getSampleEditorWindow();
        gdEditor->waveTools->waveform->refresh();
 }
@@ -108,7 +111,7 @@ void silence(SampleChannel* ch, int a, int b)
 
 void fade(SampleChannel* ch, int a, int b, int type)
 {
-       wfx_fade(ch->wave, a, b, type);
+       wfx::fade(ch->wave, a, b, type);
        gdSampleEditor* gdEditor = getSampleEditorWindow();
        gdEditor->waveTools->waveform->refresh();
 }
@@ -119,7 +122,7 @@ void fade(SampleChannel* ch, int a, int b, int type)
 
 void smoothEdges(SampleChannel* ch, int a, int b)
 {
-       wfx_smooth(ch->wave, a, b);
+       wfx::smooth(ch->wave, a, b);
        gdSampleEditor* gdEditor = getSampleEditorWindow();
        gdEditor->waveTools->waveform->refresh();
 }
@@ -130,7 +133,7 @@ void smoothEdges(SampleChannel* ch, int a, int b)
 
 void setStartEnd(SampleChannel* ch, int a, int b)
 {
-       setBeginEndChannel(ch, a * 2, b * 2);  // stereo values
+       setBeginEndChannel(ch, a, b);
        gdSampleEditor* gdEditor = getSampleEditorWindow();
        gdEditor->waveTools->waveform->recalcPoints();
        gdEditor->waveTools->waveform->clearSel();
@@ -143,14 +146,49 @@ void setStartEnd(SampleChannel* ch, int a, int b)
 
 void trim(SampleChannel* ch, int a, int b)
 {
-       if (!wfx_trim(ch->wave, a, b)) {
+       if (!wfx::trim(ch->wave, a, b)) {
                gdAlert("Unable to trim the sample!");
                return;
        }
-       setBeginEndChannel(ch, ch->begin, ch->end);
+       setBeginEndChannel(ch, ch->getBegin(), ch->getEnd());
+       gdSampleEditor* gdEditor = getSampleEditorWindow();
+       gdEditor->waveTools->waveform->clearSel();
+       gdEditor->waveTools->waveform->refresh();
+       gdEditor->updateInfo();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setPlayHead(SampleChannel* ch, int f)
+{
+       ch->setTrackerPreview(f);
+       gdSampleEditor* gdEditor = getSampleEditorWindow();
+       gdEditor->waveTools->waveform->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setPreview(SampleChannel* ch, int mode)
+{
+       ch->setPreviewMode(mode);
        gdSampleEditor* gdEditor = getSampleEditorWindow();
-  gdEditor->waveTools->waveform->clearSel();
-  gdEditor->waveTools->waveform->refresh();
+       gdEditor->play->value(!gdEditor->play->value());
 }
 
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewindPreview(SampleChannel* ch)
+{
+       geWaveform* waveform = getSampleEditorWindow()->waveTools->waveform;
+       if (waveform->isSelected() && ch->getTrackerPreview() != waveform->getSelectionA())
+               setPlayHead(ch, waveform->getSelectionA());
+       else
+               setPlayHead(ch, 0);
+}
 }}}; // giada::c::sampleEditor::
index 63babff4b3fee5a27027bc3ce5884369a0251843..e4feeec2736d4aa3d6c90e383f4f871e9325ad71 100644 (file)
@@ -51,6 +51,13 @@ void fade(SampleChannel* ch, int a, int b, int type);
 void smoothEdges(SampleChannel* ch, int a, int b);
 void setStartEnd(SampleChannel* ch, int a, int b);
 
+/* setPlayHead
+Changes playhead's position. Used in preview. */
+
+void setPlayHead(SampleChannel* ch, int f);
+
+void setPreview(SampleChannel* ch, int mode);
+void rewindPreview(SampleChannel* ch);
 }}}; // giada::c::sampleEditor::
 
 #endif
index 61a3c393f4f28d67fc747165e8ab6916d24dafa1..c853a9b59866487c7fe8fe08d30bd89943a0cc3e 100644 (file)
@@ -329,12 +329,12 @@ void glue_saveProject(void *data)
                /* update the new samplePath: everything now comes from the project
                 * folder (folderPath). Also remove any existing file. */
 
-               string samplePath = fullPath + G_SLASH + ch->wave->basename(true);
+               string samplePath = fullPath + G_SLASH + ch->wave->getBasename(true);
 
                if (gu_fileExists(samplePath))
                        remove(samplePath.c_str());
                if (ch->save(samplePath.c_str()))
-                       ch->wave->pathfile = samplePath;
+                       ch->wave->setPath(samplePath);
        }
 
        string gptcPath = fullPath + G_SLASH + gu_stripExt(name) + ".gptc";
@@ -358,7 +358,7 @@ void glue_loadSample(void *data)
 
        int res = glue_loadChannel((SampleChannel*) browser->getChannel(), fullPath.c_str());
 
-       if (res == SAMPLE_LOADED_OK) {
+       if (res == G_RES_OK) {
                conf::samplePath = gu_dirname(fullPath);
                browser->do_callback();
                G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
index dd416b6a0920c2a6f968e89baeb6a3f0d24e243f..527e32c6e8f6c242e9b72c0d9846afcaaecd4e2a 100644 (file)
@@ -48,12 +48,10 @@ using namespace giada::u;
 gdAbout::gdAbout()
 #ifdef WITH_VST
 : gdWindow(340, 435, "About Giada")
-{
 #else
 : gdWindow(340, 350, "About Giada")
-{
 #endif
-
+{
        if (conf::aboutX)
                resize(conf::aboutX, conf::aboutY, w(), h());
 
@@ -75,7 +73,7 @@ gdAbout::gdAbout()
        sprintf(
          message,
          "Version " G_VERSION_STR " (" BUILD_DATE ")\n\n"
-               "Developed by Monocasual\n"
+               "Developed by Monocasual Laboratories\n"
                "Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
                "RtMidi (%s), Libsamplerate, Jansson (%s),\n"
                "Libsndfile (%s)"
@@ -93,10 +91,9 @@ gdAbout::gdAbout()
                deps::getRtMidiVersion().c_str(),
                JANSSON_VERSION, deps::getLibsndfileVersion().c_str()
 #ifdef WITH_VST
-               , JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER);
-#else
-               );
+               , JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER
 #endif
+       );
 
        int tw = 0;
        int th = 0;
index 59b1f4e88cfb4defad003069f0e00ea560ed62e9..c7c074dcfdd376ce2fc6b618c4809a17f6ec843f 100644 (file)
@@ -52,21 +52,21 @@ gdDevInfo::gdDevInfo(unsigned dev)
        int    lines = 7;
 
        body  = "Device name: " + kernelAudio::getDeviceName(dev) + "\n";
-       body += "Total output(s): " + gu_itoa(kernelAudio::getMaxOutChans(dev)) + "\n";
-       body += "Total intput(s): " + gu_itoa(kernelAudio::getMaxInChans(dev)) + "\n";
-       body += "Duplex channel(s): " + gu_itoa(kernelAudio::getDuplexChans(dev)) + "\n";
+       body += "Total output(s): " + gu_toString(kernelAudio::getMaxOutChans(dev)) + "\n";
+       body += "Total intput(s): " + gu_toString(kernelAudio::getMaxInChans(dev)) + "\n";
+       body += "Duplex channel(s): " + gu_toString(kernelAudio::getDuplexChans(dev)) + "\n";
        body += "Default output: " + string(kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
        body += "Default input: " + string(kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
 
        int totalFreq = kernelAudio::getTotalFreqs(dev);
-       body += "Supported frequencies: " + gu_itoa(totalFreq);
+       body += "Supported frequencies: " + gu_toString(totalFreq);
 
        for (int i=0; i<totalFreq; i++) {
                if (i % 6 == 0) {
                        body += "\n    ";  // add new line each 6 printed freqs AND on the first line (i % 0 != 0)
                        lines++;
                }
-               body += gu_itoa( kernelAudio::getFreq(dev, i)) + "  ";
+               body += gu_toString( kernelAudio::getFreq(dev, i)) + "  ";
        }
 
        text->copy_label(body.c_str());
index 063285225e376d5c29e99532379e3764cbf8847b..edb8de73b6ddaadfdb914e0892b874a94633da2d 100644 (file)
@@ -89,7 +89,7 @@ gdPluginList::gdPluginList(int stackType, Channel *ch)
        if (stackType == pluginHost::MASTER_IN)
                label("Master In Plugins");
        else {
-    string l = "Channel " + gu_itoa(ch->index+1) + " Plugins";
+    string l = "Channel " + gu_toString(ch->index+1) + " Plugins";
     copy_label(l.c_str());
        }
 
index 224d40308adad4a77686758d5c5a906d9c4c9f67..2f02c2ee57a196bae8738379011dc97114c27b0f 100644 (file)
@@ -53,7 +53,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel *ch)
       conf::midiInputH, "MIDI Input Setup"),
                ch(ch)
 {
-  string title = "MIDI Input Setup (channel " + gu_itoa(ch->index+1) + ")";
+  string title = "MIDI Input Setup (channel " + gu_toString(ch->index+1) + ")";
        label(title.c_str());
   size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
 
index 1da61abf87d25cc1dd986f1f51f5c6a8eb400489..0d8b47441b3b04c173b2aa678b8912ecbfd678cf 100644 (file)
@@ -38,7 +38,6 @@
 #include "../../core/sampleChannel.h"
 #include "../../core/mixer.h"
 #include "../../core/wave.h"
-#include "../../core/clock.h"
 #include "../../utils/gui.h"
 #include "../../utils/string.h"
 #include "../elems/basics/button.h"
 #include "sampleEditor.h"
 
 
+using std::string;
 using namespace giada::m;
 using namespace giada::c;
 
 
-gdSampleEditor::gdSampleEditor(SampleChannel *ch)
+gdSampleEditor::gdSampleEditor(SampleChannelch)
   : gdWindow(640, 480),
     ch(ch)
 {
-  begin();
+  Fl_Group* upperBar = createUpperBar();
+  
+  waveTools = new geWaveTools(8, upperBar->y()+upperBar->h()+8, w()-16, h()-128, 
+       ch);
+  
+  Fl_Group* bottomBar = createBottomBar(8, waveTools->y()+waveTools->h()+8, 
+       h()-waveTools->h()-upperBar->h()-32);
+
+  add(upperBar);
+  add(waveTools);
+  add(bottomBar);
 
-  /* top bar: grid and zoom tools */
+  resizable(waveTools);
 
-  Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20);
-  bar->begin();
-    grid    = new geChoice(bar->x(), bar->y(), 50, 20);
-    snap    = new geCheck(grid->x()+grid->w()+4, bar->y(), 12, 12);
-    sep1    = new geBox(snap->x()+snap->w()+4, bar->y(), 506, 20);
-    zoomOut = new geButton(sep1->x()+sep1->w()+4, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
-    zoomIn  = new geButton(zoomOut->x()+zoomOut->w()+4, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
-  bar->end();
-  bar->resizable(sep1);
+  gu_setFavicon(this);
+  set_non_modal();
+  label(ch->wave->getName().c_str());
 
-  /* waveform */
+  size_range(720, 480);
+  if (conf::sampleEditorX)
+    resize(conf::sampleEditorX, conf::sampleEditorY, conf::sampleEditorW, 
+       conf::sampleEditorH);
+  
+  show();
+}
 
-  waveTools = new geWaveTools(8, bar->y()+bar->h()+8, w()-16, h()-128, ch);
-  waveTools->end();
 
-  /* other tools */
 
-  Fl_Group *row1 = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 20);
-  row1->begin();
-    volumeTool = new geVolumeTool(row1->x(), row1->y(), ch);
-    boostTool  = new geBoostTool(volumeTool->x()+volumeTool->w()+4, row1->y(), ch);
-    panTool    = new gePanTool(boostTool->x()+boostTool->w()+4, row1->y(), ch);
-  row1->end();
-  row1->resizable(0);
+/* -------------------------------------------------------------------------- */
 
-  Fl_Group *row2 = new Fl_Group(8, row1->y()+row1->h()+8, 800, 20);
-  row2->begin();
-    pitchTool = new gePitchTool(row2->x(), row2->y(), ch);
-  row2->end();
-  row2->resizable(0);
 
-  Fl_Group *row3 = new Fl_Group(8, row2->y()+row2->h()+8, w()-16, 20);
-  row3->begin();
-    rangeTool = new geRangeTool(row3->x(), row3->y(), ch);
-    sep2      = new geBox(rangeTool->x()+rangeTool->w()+4, row3->y(), 246, 20);
-    reload    = new geButton(sep2->x()+sep2->w()+4, row3->y(), 70, 20, "Reload");
-  row3->end();
-  row3->resizable(sep2);
+gdSampleEditor::~gdSampleEditor()
+{
+  conf::sampleEditorX = x();
+  conf::sampleEditorY = y();
+  conf::sampleEditorW = w();
+  conf::sampleEditorH = h();
+  conf::sampleEditorGridVal = atoi(grid->text());
+  conf::sampleEditorGridOn  = snap->value();
+  sampleEditor::setPreview(ch, G_PREVIEW_NONE);
+}
 
-  end();
 
-  /* grid tool setup */
+/* -------------------------------------------------------------------------- */
+
+
+Fl_Group* gdSampleEditor::createUpperBar()
+{
+  Fl_Group* g = new Fl_Group(8, 8, w()-16, 20);
+  g->begin();
+    grid    = new geChoice(g->x(), g->y(), 50, 20);
+    snap    = new geCheck(grid->x()+grid->w()+4, g->y()+3, 12, 12, "Snap");
+    sep1    = new geBox(snap->x()+snap->w()+4, g->y(), 506, 20);
+    zoomOut = new geButton(sep1->x()+sep1->w()+4, g->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+    zoomIn  = new geButton(zoomOut->x()+zoomOut->w()+4, g->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+  g->end();
+  g->resizable(sep1);
 
   grid->add("(off)");
   grid->add("2");
@@ -126,7 +137,7 @@ gdSampleEditor::gdSampleEditor(SampleChannel *ch)
   if (conf::sampleEditorGridVal == 0)
     grid->value(0);
   else 
-    grid->value(grid->find_item(gu_itoa(conf::sampleEditorGridVal).c_str()));
+    grid->value(grid->find_item(gu_toString(conf::sampleEditorGridVal).c_str()));
   grid->callback(cb_changeGrid, (void*)this);
 
   snap->value(conf::sampleEditorGridOn);
@@ -134,53 +145,117 @@ gdSampleEditor::gdSampleEditor(SampleChannel *ch)
 
   /* TODO - redraw grid if != (off) */
 
-  reload->callback(cb_reload, (void*)this);
-
   zoomOut->callback(cb_zoomOut, (void*)this);
   zoomIn->callback(cb_zoomIn, (void*)this);
 
-  /* logical samples (aka takes) cannot be reloaded. So far. */
+  return g;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
 
-  if (ch->wave->isLogical)
+Fl_Group* gdSampleEditor::createOpTools(int x, int y, int h)
+{
+  Fl_Group* g = new Fl_Group(x, y, 572, h);
+  g->begin();
+  g->resizable(0);
+    volumeTool = new geVolumeTool(g->x(), g->y(), ch);
+    boostTool  = new geBoostTool(volumeTool->x()+volumeTool->w()+4, g->y(), ch);
+    panTool    = new gePanTool(boostTool->x()+boostTool->w()+4, g->y(), ch);
+   
+    pitchTool = new gePitchTool(g->x(), panTool->y()+panTool->h()+8, ch);
+
+    rangeTool = new geRangeTool(g->x(), pitchTool->y()+pitchTool->h()+8, ch);
+    reload    = new geButton(g->x()+g->w()-70, rangeTool->y(), 70, 20, "Reload");
+  g->end();
+
+  if (ch->wave->isLogical()) // Logical samples (aka takes) cannot be reloaded.
     reload->deactivate();
 
-  gu_setFavicon(this);
-  size_range(640, 480);
-  resizable(waveTools);
+  reload->callback(cb_reload, (void*)this);
 
-  label(ch->wave->name.c_str());
+  return g;
+}
 
-  set_non_modal();
 
-  if (conf::sampleEditorX)
-    resize(conf::sampleEditorX, conf::sampleEditorY, conf::sampleEditorW, conf::sampleEditorH);
+/* -------------------------------------------------------------------------- */
 
-  show();
+
+Fl_Group* gdSampleEditor::createPreviewBox(int x, int y, int h)
+{
+  Fl_Group* g = new Fl_Group(x, y, 110, h);
+  g->begin();
+    rewind = new geButton(g->x(), g->y()+(g->h()/2)-12, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
+    play   = new geButton(rewind->x()+rewind->w()+4, g->y()+(g->h()/2)-12, 25, 25, "", play_xpm, pause_xpm);
+    loop   = new geCheck(play->x()+play->w()+6, g->y()+(g->h()/2)-6, 12, 12, "Loop");
+  g->end();
+
+  play->callback(cb_togglePreview, (void*)this);
+  rewind->callback(cb_rewindPreview, (void*)this);
+
+  ch->setOnEndPreviewCb([this] { 
+       play->value(0);
+  });
+
+  return g;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-gdSampleEditor::~gdSampleEditor()
+Fl_Group* gdSampleEditor::createInfoBox(int x, int y, int h)
 {
-  conf::sampleEditorX = x();
-  conf::sampleEditorY = y();
-  conf::sampleEditorW = w();
-  conf::sampleEditorH = h();
-  conf::sampleEditorGridVal = atoi(grid->text());
-  conf::sampleEditorGridOn  = snap->value();
+  Fl_Group* g = new Fl_Group(x, y, 400, h);
+  g->begin();
+    info = new geBox(g->x(), g->y(), g->w(), g->h());
+  g->end();    
+
+  info->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
+  
+  updateInfo();
+
+  return g;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Fl_Group* gdSampleEditor::createBottomBar(int x, int y, int h)
+{
+  Fl_Group* g = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, h);
+  g->begin();
+    Fl_Group* previewBox = createPreviewBox(g->x(), g->y(), g->h());
+
+    geBox* divisor1 = new geBox(previewBox->x()+previewBox->w()+8, g->y(), 1, g->h());
+    divisor1->box(FL_BORDER_BOX);
+
+    Fl_Group* opTools = createOpTools(divisor1->x()+divisor1->w()+12, g->y(), g->h());
+
+    geBox* divisor2 = new geBox(opTools->x()+opTools->w()+8, g->y(), 1, g->h());
+    divisor2->box(FL_BORDER_BOX);
+
+    createInfoBox(divisor2->x()+divisor2->w()+8, g->y(), g->h());
+
+  g->end();
+  g->resizable(0);
+
+  return g;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdSampleEditor::cb_reload    (Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_reload(); }
-void gdSampleEditor::cb_zoomIn    (Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_zoomIn(); }
-void gdSampleEditor::cb_zoomOut   (Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_zoomOut(); }
-void gdSampleEditor::cb_changeGrid(Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_changeGrid(); }
-void gdSampleEditor::cb_enableSnap(Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_enableSnap(); }
+void gdSampleEditor::cb_reload       (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_reload(); }
+void gdSampleEditor::cb_zoomIn       (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_zoomIn(); }
+void gdSampleEditor::cb_zoomOut      (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_zoomOut(); }
+void gdSampleEditor::cb_changeGrid   (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_changeGrid(); }
+void gdSampleEditor::cb_enableSnap   (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_enableSnap(); }
+void gdSampleEditor::cb_togglePreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_togglePreview(); }
+void gdSampleEditor::cb_rewindPreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_rewindPreview(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -195,18 +270,35 @@ void gdSampleEditor::__cb_enableSnap()
 /* -------------------------------------------------------------------------- */
 
 
+void gdSampleEditor::__cb_togglePreview()
+{
+       if (play->value())
+       sampleEditor::setPreview(ch, G_PREVIEW_NONE);
+       else
+       sampleEditor::setPreview(ch, loop->value() ? G_PREVIEW_LOOP : G_PREVIEW_NORMAL);
+}
+
+
+void gdSampleEditor::__cb_rewindPreview()
+{
+       sampleEditor::rewindPreview(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void gdSampleEditor::__cb_reload()
 {
   if (!gdConfirmWin("Warning", "Reload sample: are you sure?"))
     return;
 
-  /* no need for glue_loadChan, there's no gui to refresh */
-
-  ch->load(ch->wave->pathfile.c_str(), conf::samplerate, conf::rsmpQuality);
+  if (glue_loadChannel(ch, ch->wave->getPath()) != G_RES_OK)
+    return;
 
   glue_setBoost(ch, G_DEFAULT_BOOST);
   glue_setPitch(ch, G_DEFAULT_PITCH);
-  glue_setPanning(ch, 1.0f);
+  glue_setPanning(ch, 0.5f);
 
   panTool->refresh();
   boostTool->refresh();
@@ -214,7 +306,7 @@ void gdSampleEditor::__cb_reload()
   waveTools->waveform->stretchToWindow();
   waveTools->updateWaveform();
 
-  sampleEditor::setBeginEndChannel(ch, 0, ch->wave->size);
+  sampleEditor::setBeginEndChannel(ch, 0, ch->wave->getSize());
 
   redraw();
 }
@@ -247,3 +339,19 @@ void gdSampleEditor::__cb_changeGrid()
 {
   waveTools->waveform->setGridLevel(atoi(grid->text()));
 }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::updateInfo()
+{
+       string bitDepth = ch->wave->getBits() != 0 ? gu_toString(ch->wave->getBits()) : "(unknown)";
+       string infoText = 
+               "File: "  + ch->wave->getPath() + "\n"
+               "Size: " + gu_toString(ch->wave->getSize()) + " frames\n"
+               "Duration: " + gu_toString(ch->wave->getDuration()) + " seconds\n"
+               "Bit depth: " + bitDepth + "\n"
+               "Frequency: " + gu_toString(ch->wave->getRate()) + " Hz\n";
+       info->copy_label(infoText.c_str());
+}
index d0fca5405059da2c13c00f459b8bb5a6306b9563..dc09694f9e5cbecf656e7d2898bfc0bd02342565 100644 (file)
@@ -48,43 +48,62 @@ class geButton;
 
 class gdSampleEditor : public gdWindow
 {
+friend class geWaveform;
+
 private:
 
-       static void cb_reload        (Fl_Widget *w, void *p);
-       static void cb_zoomIn        (Fl_Widget *w, void *p);
-       static void cb_zoomOut       (Fl_Widget *w, void *p);
-       static void cb_changeGrid    (Fl_Widget *w, void *p);
-       static void cb_enableSnap    (Fl_Widget *w, void *p);
-       inline void __cb_reload();
-       inline void __cb_zoomIn();
-       inline void __cb_zoomOut();
-       inline void __cb_changeGrid();
-       inline void __cb_enableSnap();
+       Fl_Group* createUpperBar();
+       Fl_Group* createBottomBar(int x, int y, int h);
+
+       Fl_Group* createPreviewBox(int x, int y, int h);
+       Fl_Group* createOpTools(int x, int y, int h);
+       Fl_Group* createInfoBox(int x, int y, int h);
+
+       static void cb_reload    (Fl_Widget* w, void* p);
+       static void cb_zoomIn    (Fl_Widget* w, void* p);
+       static void cb_zoomOut   (Fl_Widget* w, void* p);
+       static void cb_changeGrid(Fl_Widget* w, void* p);
+       static void cb_enableSnap(Fl_Widget* w, void* p);
+       static void cb_togglePreview(Fl_Widget* w, void* p);
+       static void cb_rewindPreview(Fl_Widget* w, void* p);
+       void __cb_reload();
+       void __cb_zoomIn();
+       void __cb_zoomOut();
+       void __cb_changeGrid();
+       void __cb_enableSnap();
+       void __cb_togglePreview();
+       void __cb_rewindPreview();
 
 public:
 
-       gdSampleEditor(SampleChannel *ch);
+       gdSampleEditor(SampleChannelch);
        ~gdSampleEditor();
 
-       geChoice     *grid;
-       geCheck      *snap;
-       geBox        *sep1;
-       geButton     *zoomIn;
-       geButton     *zoomOut;
+       void updateInfo();
+
+       geChoice* grid;
+       geCheck*  snap;
+       geBox*    sep1;
+       geButton* zoomIn;
+       geButton* zoomOut;
        
-       geWaveTools  *waveTools;
+       geWaveTools* waveTools;
+
+       geVolumeTool* volumeTool;
+       geBoostTool*  boostTool;
+       gePanTool*    panTool;
 
-       geVolumeTool *volumeTool;
-       geBoostTool  *boostTool;
-       gePanTool    *panTool;
+       gePitchTool* pitchTool;
 
-       gePitchTool  *pitchTool;
+       geRangeTool* rangeTool;
+       geButton*    reload;
 
-       geRangeTool  *rangeTool;
-       geBox        *sep2;
-       geButton     *reload;
+       geButton* play;
+       geButton* rewind;
+       geCheck* loop;
+       geBox* info;
 
-       SampleChannel *ch;
+       SampleChannelch;
 };
 
 
index 6049acdc2395d172ab5c465de2bc8921b29df259..ae37e9f2921379747a0473a59de58fb5c4bf9153 100644 (file)
@@ -154,7 +154,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
                int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value());
                for (int i=0; i<nfreq; i++) {
                        int freq = kernelAudio::getFreq(sounddevOut->value(), i);
-                       samplerate->add(gu_itoa(freq).c_str());
+                       samplerate->add(gu_toString(freq).c_str());
                        if (freq == conf::samplerate)
                                samplerate->value(i);
                }
@@ -179,7 +179,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
        buffersize->add("1024");
        buffersize->add("2048");
        buffersize->add("4096");
-       buffersize->showItem(gu_itoa(conf::buffersize).c_str());
+       buffersize->showItem(gu_toString(conf::buffersize).c_str());
 
        rsmpQuality->add("Sinc best quality (very slow)");
        rsmpQuality->add("Sinc medium quality (slow)");
@@ -188,7 +188,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
        rsmpQuality->add("Linear (very fast)");
        rsmpQuality->value(conf::rsmpQuality);
 
-       delayComp->value(gu_itoa(conf::delayComp).c_str());
+       delayComp->value(gu_toString(conf::delayComp).c_str());
        delayComp->type(FL_INT_INPUT);
        delayComp->maximum_size(5);
 
index 33b222546ca44a34e29a26cf0d1a1d4157a626d6..ee9b77ef1fcddfcba6129e540ab1793dc2053d80 100644 (file)
@@ -74,7 +74,7 @@ geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
 
 void geTabPlugins::updateCount()
 {
-       string scanLabel = "Scan (" + gu_itoa(pluginHost::countAvailablePlugins()) + " found)";
+       string scanLabel = "Scan (" + gu_toString(pluginHost::countAvailablePlugins()) + " found)";
        scanButton->label(scanLabel.c_str());
 }
 
@@ -90,7 +90,7 @@ void geTabPlugins::cb_scan(Fl_Widget *w, void *p) { ((geTabPlugins*)p)->__cb_sca
 
 void geTabPlugins::cb_onScan(float progress, void *p)
 {
-       string l = "Scan in progress (" + gu_itoa((int)(progress*100)) + "%). Please wait...";
+       string l = "Scan in progress (" + gu_toString((int)(progress*100)) + "%). Please wait...";
        ((geTabPlugins *)p)->info->label(l.c_str());
        Fl::wait();
 }
index a9b3fc78fba9864c3e83a08bfb18a5215d1dcd5a..ff94d684fd4ec88a0ccb438a418f8a02f5b8f994 100644 (file)
@@ -76,7 +76,7 @@ void geChannelStatus::draw()
     if (pos == -1)
       pos = 0;
     else
-      pos = (pos * (w()-1)) / (ch->end - ch->begin);
+      pos = (pos * (w()-1)) / ((ch->getEnd() - ch->getBegin()));
     fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1);
   }
 }
index 6293ae38e58f5b0d420969f5d411009b5eceebc6..111ad4477b2ac85a419d5641b0e2eb98fdd71c6d 100644 (file)
@@ -107,7 +107,7 @@ int geColumn::handle(int e)
                                gu_log("[geColumn::handle] loading %s...\n", paths.at(i).c_str());
                                SampleChannel *c = (SampleChannel*) glue_addChannel(index, CHANNEL_SAMPLE);
                                result = glue_loadChannel(c, gu_stripFileUrl(paths.at(i)).c_str());
-                               if (result != SAMPLE_LOADED_OK) {
+                               if (result != G_RES_OK) {
                                        deleteChannel(c->guiChannel);
                                        fails = true;
                                }
index b423196e6978dbedbe18020700e9b6f2a2a86b7c..f76b216b48e8302f9ecc08a96cf039a82aba301c 100644 (file)
@@ -88,7 +88,7 @@ void geKeyboard::init()
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::freeChannel(geChannel *gch)
+void geKeyboard::freeChannel(geChannelgch)
 {
        gch->reset();
 }
@@ -97,7 +97,7 @@ void geKeyboard::freeChannel(geChannel *gch)
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::deleteChannel(geChannel *gch)
+void geKeyboard::deleteChannel(geChannelgch)
 {
        for (unsigned i=0; i<columns.size(); i++) {
                int k = columns.at(i)->find(gch);
@@ -112,7 +112,7 @@ void geKeyboard::deleteChannel(geChannel *gch)
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::updateChannel(geChannel *gch)
+void geKeyboard::updateChannel(geChannelgch)
 {
        gch->update();
 }
@@ -123,34 +123,32 @@ void geKeyboard::updateChannel(geChannel *gch)
 
 void geKeyboard::organizeColumns()
 {
-       /* if only one column exists don't cleanup: the initial column must
-        * stay here. */
-
-       if (columns.size() == 1)
+       if (columns.size() == 0)
                return;
 
-       /* otherwise delete all empty columns */
-       /** FIXME - this for loop might not work correctly! */
+       /* Otherwise delete all empty columns. */
 
-       for (unsigned i=columns.size()-1; i>=1; i--) {
+       for (size_t i=columns.size(); i-- > 0;) {
                if (columns.at(i)->isEmpty()) {
-                       //Fl::delete_widget(columns.at(i));
-                       delete columns.at(i);
+                       Fl::delete_widget(columns.at(i));
                        columns.erase(columns.begin() + i);
                }
        }
 
-       /* compact column, avoid empty spaces */
-
-       for (unsigned i=1; i<columns.size(); i++)
-               columns.at(i)->position(columns.at(i-1)->x() + columns.at(i-1)->w() + 16, y());
+       /* Zero columns? Just add the "add column" button. Compact column and avoid 
+       empty spaces otherwise. */
 
-       addColumnBtn->position(columns.back()->x() + columns.back()->w() + 16, y());
-
-       /* recompute col indexes */
+       if (columns.size() == 0)
+               addColumnBtn->position(x() - xposition(), y());
+       else {
+               for (size_t i=0; i<columns.size(); i++) {
+                       int pos = i == 0 ? x() - xposition() : columns.at(i-1)->x() + columns.at(i-1)->w() + COLUMN_GAP;
+                       columns.at(i)->position(pos, y());
+               }
+               addColumnBtn->position(columns.back()->x() + columns.back()->w() + COLUMN_GAP, y());
+       }
 
        refreshColIndexes();
-
        redraw();
 }
 
@@ -158,7 +156,7 @@ void geKeyboard::organizeColumns()
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::cb_addColumn(Fl_Widget *v, void *p)
+void geKeyboard::cb_addColumn(Fl_Widget* v, void* p)
 {
        ((geKeyboard*)p)->__cb_addColumn(G_DEFAULT_COLUMN_WIDTH);
 }
@@ -167,9 +165,9 @@ void geKeyboard::cb_addColumn(Fl_Widget *v, void *p)
 /* -------------------------------------------------------------------------- */
 
 
-geChannel *geKeyboard::addChannel(int colIndex, Channel *ch, bool build)
+geChannel* geKeyboard::addChannel(int colIndex, Channel* ch, bool build)
 {
-       geColumn *col = getColumnByIndex(colIndex);
+       geColumncol = getColumnByIndex(colIndex);
 
        /* no column with index 'colIndex' found? Just create it and set its index
        to 'colIndex'. */
@@ -199,7 +197,7 @@ void geKeyboard::refreshColumns()
 /* -------------------------------------------------------------------------- */
 
 
-geColumn *geKeyboard::getColumnByIndex(int index)
+geColumngeKeyboard::getColumnByIndex(int index)
 {
        for (unsigned i=0; i<columns.size(); i++)
                if (columns.at(i)->getIndex() == index)
@@ -271,7 +269,7 @@ int geKeyboard::handle(int e)
 
                        for (unsigned i=0; i<columns.size(); i++)
                                for (int k=1; k<columns.at(i)->children(); k++)
-                                       ret &= ((geChannel*)columns.at(i)->child(k))->keyPress(e);
+                                       ret &= static_cast<geChannel*>(columns.at(i)->child(k))->keyPress(e);
                        break;
                }
        }
@@ -285,7 +283,7 @@ int geKeyboard::handle(int e)
 void geKeyboard::clear()
 {
        for (unsigned i=0; i<columns.size(); i++)
-               delete columns.at(i);
+               Fl::delete_widget(columns.at(i));
        columns.clear();
        indexColumn = 0;     // new columns will start from index=0
        addColumnBtn->position(8, y());
@@ -295,7 +293,7 @@ void geKeyboard::clear()
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::setChannelWithActions(geSampleChannel *gch)
+void geKeyboard::setChannelWithActions(geSampleChannelgch)
 {
        if (gch->ch->hasActions)
                gch->showActionButton();
@@ -309,19 +307,11 @@ void geKeyboard::setChannelWithActions(geSampleChannel *gch)
 
 void geKeyboard::printChannelMessage(int res)
 {
-       if      (res == SAMPLE_NOT_VALID)
-               gdAlert("This is not a valid WAVE file.");
-       else if (res == SAMPLE_MULTICHANNEL)
+       if      (res == G_RES_ERR_WRONG_DATA)
                gdAlert("Multichannel samples not supported.");
-       else if (res == SAMPLE_WRONG_BIT)
-               gdAlert("This sample has an\nunsupported bit-depth (> 32 bit).");
-       else if (res == SAMPLE_WRONG_ENDIAN)
-               gdAlert("This sample has a wrong\nbyte order (not little-endian).");
-       else if (res == SAMPLE_WRONG_FORMAT)
-               gdAlert("This sample is encoded in\nan unsupported audio format.");
-       else if (res == SAMPLE_READ_ERROR)
+       else if (res == G_RES_ERR_IO)
                gdAlert("Unable to read this sample.");
-       else if (res == SAMPLE_PATH_TOO_LONG)
+       else if (res == G_RES_ERR_PATH_TOO_LONG)
                gdAlert("File path too long.");
        else
                gdAlert("Unknown error.");
@@ -335,27 +325,26 @@ void geKeyboard::__cb_addColumn(int width)
 {
        int colx;
        int colxw;
-       int gap = 16;
        if (columns.size() == 0) {
                colx  = x() - xposition();  // mind the offset with xposition()
                colxw = colx + width;
        }
        else {
-               geColumn *prev = columns.back();
-               colx  = prev->x()+prev->w() + gap;
+               geColumnprev = columns.back();
+               colx  = prev->x()+prev->w() + COLUMN_GAP;
                colxw = colx + width;
        }
 
        /* add geColumn to geKeyboard and to columns vector */
 
-       geColumn *gc = new geColumn(colx, y(), width, 2000, indexColumn, this);
+       geColumngc = new geColumn(colx, y(), width, 2000, indexColumn, this);
   add(gc);
        columns.push_back(gc);
        indexColumn++;
 
        /* move addColumn button */
 
-       addColumnBtn->position(colxw + gap, y());
+       addColumnBtn->position(colxw + COLUMN_GAP, y());
        redraw();
 
        gu_log("[geKeyboard::__cb_addColumn] new column added (index=%d, w=%d), total count=%d, addColumn(x)=%d\n",
@@ -398,7 +387,7 @@ int geKeyboard::getColumnWidth(int i)
 /* -------------------------------------------------------------------------- */
 
 
-geColumn *geKeyboard::getColumn(int i)
+geColumngeKeyboard::getColumn(int i)
 {
   return columns.at(i);
 }
index a73cdab3ad93949c55920a0dcb3709d4eae71a10..89c3b4e359f6017af398e7de20454d3b198a15d2 100644 (file)
@@ -45,13 +45,15 @@ class geKeyboard : public Fl_Scroll
 {
 private:
 
+       static const int COLUMN_GAP = 16;
+
        /* refreshColIndexes
         * Recompute all column indexes in order to avoid any gaps between them.
         * Indexes must always be contiguous! */
 
        void refreshColIndexes();
 
-       static void cb_addColumn  (Fl_Widget *v, void *p);
+       static void cb_addColumn  (Fl_Widget* v, void* p);
        inline void __cb_addColumn(int width=G_DEFAULT_COLUMN_WIDTH);
 
        bool bckspcPressed;
@@ -64,7 +66,7 @@ private:
 
        static int indexColumn;
 
-       geButton *addColumnBtn;
+       geButtonaddColumnBtn;
 
        /* columns
         * a vector of columns which in turn contain channels. */
@@ -88,7 +90,7 @@ public:
         * set to true, also generate the corresponding column if column (index) does
         * not exist yet. */
 
-       geChannel *addChannel(int column, Channel *ch, bool build=false);
+       geChannel* addChannel(int column, Channel* ch, bool build=false);
 
        /* addColumn
         * add a new column to the top of the stack. */
@@ -99,18 +101,18 @@ public:
         * delete a channel from geChannels<> where geChannel->ch == ch and remove
         * it from the stack. */
 
-       void deleteChannel(geChannel *gch);
+       void deleteChannel(geChannelgch);
 
        /* freeChannel
         * free a channel from geChannels<> where geChannel->ch == ch. No channels
         * are deleted */
 
-       void freeChannel(geChannel *gch);
+       void freeChannel(geChannelgch);
 
        /* updateChannel
         * wrapper function to call gch->update(). */
 
-       void updateChannel(geChannel *gch);
+       void updateChannel(geChannelgch);
 
        /* organizeColumns
         * reorganize columns layout by removing empty gaps. */
@@ -125,12 +127,12 @@ public:
        /* getColumnByIndex
         * return the column with index 'index', or nullptr if not found. */
 
-       geColumn *getColumnByIndex(int index);
+       geColumngetColumnByIndex(int index);
 
        /* getColumn
         * return the column with from columns->at(i). */
 
-       geColumn *getColumn(int i);
+       geColumngetColumn(int i);
 
        /* clear
         * delete all channels and groups. */
@@ -140,7 +142,7 @@ public:
        /* setChannelWithActions
         * add 'R' button if channel has actions, and set recorder to active. */
 
-       void setChannelWithActions(geSampleChannel *gch);
+       void setChannelWithActions(geSampleChannelgch);
 
        /* printChannelMessage
         * given any output by glue_loadChannel, print the message on screen
index 26f13107d22eb6f6db32b0d49ffb3123f9fb429a..b22dcc1c536c7cf8c81871819403d73d686c6d8b 100644 (file)
@@ -368,7 +368,7 @@ void geSampleChannel::update()
                        mainButton->label("* file not found! *");
                        break;
                default:
-                       mainButton->label(((SampleChannel*) ch)->wave->name.c_str());
+                       mainButton->label(((SampleChannel*) ch)->wave->getName().c_str());
                        break;
        }
 
index 781437a61af65302dccd680d50bc478087c7332f..66f886786ca84263ef8559dbc2dcf489de635c7a 100644 (file)
@@ -64,7 +64,7 @@ int geSampleChannelButton::handle(int e)
       geSampleChannel *gch = static_cast<geSampleChannel*>(parent());
       SampleChannel   *ch  = static_cast<SampleChannel*>(gch->ch);
       int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str());
-                       if (result != SAMPLE_LOADED_OK)
+                       if (result != G_RES_OK)
                                G_MainWin->keyboard->printChannelMessage(result);
                        ret = 1;
                        break;
index 85974d3dd813e72f16f95358e642e85a93600d78..a61f8ed996d67624a155a43c01b82f527b0439a4 100644 (file)
@@ -49,7 +49,7 @@
 #include "mainMenu.h"
 
 
-extern gdMainWindow *G_MainWin;
+extern gdMainWindowG_MainWin;
 
 
 using namespace giada::m;
@@ -79,10 +79,10 @@ geMainMenu::geMainMenu(int x, int y)
 /* -------------------------------------------------------------------------- */
 
 
-void geMainMenu::cb_about (Fl_Widget *v, void *p) { ((geMainMenu*)p)->__cb_about(); }
-void geMainMenu::cb_config(Fl_Widget *v, void *p) { ((geMainMenu*)p)->__cb_config(); }
-void geMainMenu::cb_file  (Fl_Widget *v, void *p) { ((geMainMenu*)p)->__cb_file(); }
-void geMainMenu::cb_edit  (Fl_Widget *v, void *p) { ((geMainMenu*)p)->__cb_edit(); }
+void geMainMenu::cb_about (Fl_Widget* v, void* p) { ((geMainMenu*)p)->__cb_about(); }
+void geMainMenu::cb_config(Fl_Widget* v, void* p) { ((geMainMenu*)p)->__cb_config(); }
+void geMainMenu::cb_file  (Fl_Widget* v, void* p) { ((geMainMenu*)p)->__cb_file(); }
+void geMainMenu::cb_edit  (Fl_Widget* v, void* p) { ((geMainMenu*)p)->__cb_edit(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -118,13 +118,13 @@ void geMainMenu::__cb_file()
                {0}
        };
 
-       Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+       Fl_Menu_Buttonb = new Fl_Menu_Button(0, 0, 100, 50);
        b->box(G_CUSTOM_BORDER_BOX);
        b->textsize(G_GUI_FONT_SIZE_BASE);
        b->textcolor(G_COLOR_LIGHT_2);
        b->color(G_COLOR_GREY_2);
 
-       const Fl_Menu_Item *m = menu->popup(Fl::event_x(),      Fl::event_y(), 0, 0, b);
+       const Fl_Menu_Itemm = menu->popup(Fl::event_x(),      Fl::event_y(), 0, 0, b);
        if (!m) return;
 
        if (strcmp(m->label(), "Open patch or project...") == 0) {
@@ -189,13 +189,13 @@ void geMainMenu::__cb_edit()
                                break;
                        }
 
-       Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+       Fl_Menu_Buttonb = new Fl_Menu_Button(0, 0, 100, 50);
        b->box(G_CUSTOM_BORDER_BOX);
        b->textsize(G_GUI_FONT_SIZE_BASE);
        b->textcolor(G_COLOR_LIGHT_2);
        b->color(G_COLOR_GREY_2);
 
-       const Fl_Menu_Item *m = menu->popup(Fl::event_x(),      Fl::event_y(), 0, 0, b);
+       const Fl_Menu_Itemm = menu->popup(Fl::event_x(),      Fl::event_y(), 0, 0, b);
        if (!m) return;
 
        if (strcmp(m->label(), "Clear all samples") == 0) {
@@ -215,7 +215,6 @@ void geMainMenu::__cb_edit()
        if (strcmp(m->label(), "Reset to init state") == 0) {
                if (!gdConfirmWin("Warning", "Reset to init state: are you sure?"))
                        return;
-               gu_closeAllSubwindows();
                glue_resetToInitState();
                return;
        }
index 9a72a4f3c7dbb8ace576384e6211bada8dc16da9..e189525f8689392b75e9d80cf1078d69b03bb23f 100644 (file)
@@ -41,6 +41,9 @@
 #include "boostTool.h"
 
 
+using namespace giada::m;
+
+
 geBoostTool::geBoostTool(int X, int Y, SampleChannel *ch)
   : Fl_Group(X, Y, 220, 20),
     ch      (ch)
@@ -96,7 +99,7 @@ void geBoostTool::__cb_setBoost()
   else 
   if (Fl::event() == FL_RELEASE) {
     glue_setBoost(ch, dial->value());
-    static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+    static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
   }
 }
 
@@ -107,7 +110,7 @@ void geBoostTool::__cb_setBoost()
 void geBoostTool::__cb_setBoostNum()
 {
   glue_setBoost(ch, gu_dBtoLinear(atof(input->value())));
-  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+  static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
 }
 
 
@@ -116,8 +119,8 @@ void geBoostTool::__cb_setBoostNum()
 
 void geBoostTool::__cb_normalize()
 {
-  float val = wfx_normalizeSoft(ch->wave);
+  float val = wfx::normalizeSoft(ch->wave);
   glue_setBoost(ch, val); // it's like a fake user moving the dial 
-  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+  static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
 }
 
index 8e6f439637552638545e6fa94c33e8d2418b3611..84da01e7f2fcf4c75a525b1a67122b8ac2aee289 100644 (file)
@@ -141,7 +141,7 @@ void gePitchTool::__cb_setPitchDouble()
 
 void gePitchTool::__cb_setPitchToBar()
 {
-  glue_setPitch(ch, ch->end / (float) clock::getFramesPerBar());
+  glue_setPitch(ch, ch->getEnd() / (float) clock::getFramesPerBar());
 }
 
 
@@ -150,7 +150,7 @@ void gePitchTool::__cb_setPitchToBar()
 
 void gePitchTool::__cb_setPitchToSong()
 {
-  glue_setPitch(ch, ch->end / (float) clock::getTotalFrames());
+  glue_setPitch(ch, ch->getEnd() / (float) clock::getTotalFrames());
 }
 
 
index 8f411ff9627fb94641cc32035a21a748de7435b7..8abd173fc86e350fa90a1725f114759e9dc2ff00 100644 (file)
 using namespace giada::c;
 
 
-geRangeTool::geRangeTool(int x, int y, SampleChannel *ch)
-  : Fl_Group(x, y, 300, 20),
-    ch      (ch)
+geRangeTool::geRangeTool(int x, int y, SampleChannelch)
+       : Fl_Group(x, y, 300, 20),
+         m_ch    (ch)
 {
   begin();
-    label  = new geBox  (x, y, gu_getStringWidth("Range"), 20, "Range", FL_ALIGN_RIGHT);
-    begin_ = new geInput(label->x()+label->w()+4, y, 70, 20);
-    end_   = new geInput(begin_->x()+begin_->w()+4, y, 70, 20);
-    reset  = new geButton(end_->x()+end_->w()+4, y, 70, 20, "Reset");
+    m_label = new geBox  (x, y, gu_getStringWidth("Range"), 20, "Range", FL_ALIGN_RIGHT);
+    m_begin = new geInput(m_label->x()+m_label->w()+4, y, 70, 20);
+    m_end   = new geInput(m_begin->x()+m_begin->w()+4, y, 70, 20);
+    m_reset = new geButton(m_end->x()+m_end->w()+4, y, 70, 20, "Reset");
   end();
 
-  begin_->type(FL_INT_INPUT);
-  begin_->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
-  begin_->callback(cb_setChanPos, this);
+  m_begin->type(FL_INT_INPUT);
+  m_begin->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
+  m_begin->callback(cb_setChanPos, this);
   
-  end_->type(FL_INT_INPUT);
-  end_->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
-  end_->callback(cb_setChanPos, this);
+  m_end->type(FL_INT_INPUT);
+  m_end->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
+  m_end->callback(cb_setChanPos, this);
 
-  reset->callback(cb_resetStartEnd, this);
+  m_reset->callback(cb_resetStartEnd, this);
 
   refresh();
 }
@@ -73,16 +73,16 @@ geRangeTool::geRangeTool(int x, int y, SampleChannel *ch)
 
 void geRangeTool::refresh()
 {
-  begin_->value(gu_itoa(ch->begin / 2).c_str());
-  end_->value(gu_itoa(ch->end / 2).c_str());
+  m_begin->value(gu_toString(m_ch->getBegin()).c_str());
+  m_end->value(gu_toString(m_ch->getEnd()).c_str());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geRangeTool::cb_setChanPos   (Fl_Widget *w, void *p) { ((geRangeTool*)p)->__cb_setChanPos(); }
-void geRangeTool::cb_resetStartEnd(Fl_Widget *w, void *p) { ((geRangeTool*)p)->__cb_resetStartEnd(); }
+void geRangeTool::cb_setChanPos   (Fl_Widget* w, void* p) { ((geRangeTool*)p)->__cb_setChanPos(); }
+void geRangeTool::cb_resetStartEnd(Fl_Widget* w, void* p) { ((geRangeTool*)p)->__cb_resetStartEnd(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -90,8 +90,8 @@ void geRangeTool::cb_resetStartEnd(Fl_Widget *w, void *p) { ((geRangeTool*)p)->_
 
 void geRangeTool::__cb_setChanPos()
 {
-  sampleEditor::setBeginEndChannel(ch, atoi(begin_->value())*2, atoi(end_->value())*2);
-  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+  sampleEditor::setBeginEndChannel(m_ch, atoi(m_begin->value()), atoi(m_end->value()));
+  static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
 }
 
 
@@ -100,6 +100,6 @@ void geRangeTool::__cb_setChanPos()
 
 void geRangeTool::__cb_resetStartEnd()
 {
-  sampleEditor::setBeginEndChannel(ch, 0, ch->wave->size);
-  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+  sampleEditor::setBeginEndChannel(m_ch, 0, m_ch->wave->getSize() - 1);
+  static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
 }
index 909f0f3d710976f9871cff78917ea03256463c4c..048038835eb84ee472b14219b2cee5b1f7fe6bcc 100644 (file)
@@ -42,21 +42,21 @@ class geRangeTool : public Fl_Group
 {
 private:
 
-  SampleChannel *ch;
+  SampleChannel* m_ch;
 
-  geBox    *label;
-  geInput  *begin_;
-  geInput  *end_;
-  geButton *reset;
+  geBox*    m_label;
+  geInput*  m_begin;
+  geInput*  m_end;
+  geButton* m_reset;
 
-  static void cb_setChanPos   (Fl_Widget *w, void *p);
-  static void cb_resetStartEnd(Fl_Widget *w, void *p);
+  static void cb_setChanPos   (Fl_Widget* w, void* p);
+  static void cb_resetStartEnd(Fl_Widget* w, void* p);
   inline void __cb_setChanPos();
   inline void __cb_resetStartEnd();
 
 public:
 
-  geRangeTool(int x, int y, SampleChannel *ch);
+  geRangeTool(int x, int y, SampleChannelch);
 
   void refresh();
 };
index e3c0c13207ce1cf5e3613e58f627d301851e8dc4..0b7e059c81d3363a31b7a5802623cd1a2ddd3311 100644 (file)
@@ -80,10 +80,10 @@ void menuCallback(Fl_Widget* w, void* v)
                c::sampleEditor::silence(wavetools->ch, a, b);
                break;  
        case Menu::FADE_IN:
-               c::sampleEditor::fade(wavetools->ch, a, b, 0);
+               c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_IN);
                break;
        case Menu::FADE_OUT:
-               c::sampleEditor::fade(wavetools->ch, a, b, 1);
+               c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_OUT);
                break;
        case Menu::SMOOTH_EDGES:
                c::sampleEditor::smoothEdges(wavetools->ch, a, b);
@@ -128,7 +128,7 @@ void geWaveTools::updateWaveform()
 
 void geWaveTools::redrawWaveformAsync()
 {
-       if (ch->status & (STATUS_PLAY | STATUS_ENDING))
+       if (ch->isPreview())
                waveform->redraw();
 }
 
@@ -161,23 +161,22 @@ void geWaveTools::resize(int x, int y, int w, int h)
 
 int geWaveTools::handle(int e)
 {
-       int ret = Fl_Group::handle(e);
        switch (e) {
                case FL_MOUSEWHEEL: {
                        waveform->setZoom(Fl::event_dy());
                        redraw();
-                       ret = 1;
-                       break;
+                       return 1;
                }
                case FL_PUSH: {
                        if (Fl::event_button3()) {  // right button
                                openMenu();
-                               ret = 1;
+                               return 1;
                        }
-                       break;
+                       Fl::focus(waveform);
                }
+               default:
+                       return Fl_Group::handle(e);
        }
-       return ret;
 }
 
 
index e315ca18f0232a7704b0827aae342d895e490385..d153ad18bd0f83cf345e20df0f9cbe9ebc998f83 100644 (file)
@@ -37,6 +37,8 @@
 #include "../../../core/sampleChannel.h"
 #include "../../../glue/channel.h"
 #include "../../../glue/sampleEditor.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/sampleEditor.h"
 #include "../basics/boxtypes.h"
 #include "waveTools.h"
 #include "waveform.h"
@@ -47,25 +49,25 @@ using namespace giada::c;
 
 
 geWaveform::geWaveform(int x, int y, int w, int h, SampleChannel* ch, const char* l)
-: Fl_Widget   (x, y, w, h, l),
-  selection   {},
-  chan        (ch),
-  chanStart   (0),
-  chanStartLit(false),
-  chanEnd     (0),
-  chanEndLit  (false),
-  pushed      (false),
-  dragged     (false),
-  resizedA    (false),
-  resizedB    (false),
-  ratio       (0.0f)
+: Fl_Widget     (x, y, w, h, l),
+  m_selection   {},
+  m_ch          (ch),
+  m_chanStart   (0),
+  m_chanStartLit(false),
+  m_chanEnd     (0),
+  m_chanEndLit  (false),
+  m_pushed      (false),
+  m_dragged     (false),
+  m_resizedA    (false),
+  m_resizedB    (false),
+  m_ratio       (0.0f)
 {
-  data.sup  = nullptr;
-  data.inf  = nullptr;
-  data.size = 0;
+  m_data.sup  = nullptr;
+  m_data.inf  = nullptr;
+  m_data.size = 0;
 
-  grid.snap  = conf::sampleEditorGridOn;
-  grid.level = conf::sampleEditorGridVal;
+  m_grid.snap  = conf::sampleEditorGridOn;
+  m_grid.level = conf::sampleEditorGridVal;
 
   alloc(w);
 }
@@ -85,98 +87,101 @@ geWaveform::~geWaveform()
 
 void geWaveform::freeData()
 {
-  if (data.sup) {
-    delete[] data.sup;
-    delete[] data.inf;
-    data.sup  = nullptr;
-    data.inf  = nullptr;
-    data.size = 0;
+  if (m_data.sup) {
+    delete[] m_data.sup;
+    delete[] m_data.inf;
+    m_data.sup  = nullptr;
+    m_data.inf  = nullptr;
+    m_data.size = 0;
   }
-  grid.points.clear();
+  m_grid.points.clear();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int geWaveform::alloc(int datasize)
+int geWaveform::alloc(int datasize, bool force)
 {
-  ratio = chan->wave->size / (float) datasize;
+  Wave* wave = m_ch->wave;
 
-  if (ratio < 2)
-    return 0;
+  m_ratio = wave->getSize() / (float) datasize;
+
+  /* Limit 1:1 drawing (to avoid sub-frame drawing) by keeping m_ratio >= 1. */
+
+  if (m_ratio < 1) {  
+    datasize = wave->getSize();
+    m_ratio = 1;
+  }
+
+  if (datasize == m_data.size && !force)
+       return 0;
 
   freeData();
 
-  data.size = datasize;
-  data.sup  = new (std::nothrow) int[data.size];
-  data.inf  = new (std::nothrow) int[data.size];
+  m_data.size = datasize;
+  m_data.sup  = new (std::nothrow) int[m_data.size];
+  m_data.inf  = new (std::nothrow) int[m_data.size];
 
-  if (!data.sup || !data.inf)
+  if (!m_data.sup || !m_data.inf) {
+       gu_log("[geWaveform::alloc] unable to allocate memory for the waveform!\n");
     return 0;
+  }
+
+  gu_log("[geWaveform::alloc] %d pixels, %f m_ratio\n", m_data.size, m_ratio);
 
   int offset = h() / 2;
   int zero   = y() + offset; // center, zero amplitude (-inf dB)
 
-  /* grid frequency: store a grid point every 'gridFreq' pixel. Must be
-   * even, as always */
-
-  int gridFreq = 0;
-  if (grid.level != 0) {
-    gridFreq = chan->wave->size / grid.level;
-    if (gridFreq % 2 != 0)
-      gridFreq--;
-  }
+  /* Frid frequency: store a grid point every 'gridFreq' frame (if grid is
+  enabled). TODO - this will cause round off errors, since gridFreq is integer. */
 
-  for (int i=0; i<data.size; i++) {
+  int gridFreq = m_grid.level != 0 ? wave->getSize() / m_grid.level : 0;
 
-    int pp;  // point prev
-    int pn;  // point next
+       /* Resampling the waveform, hardcore way. Many thanks to 
+       http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html */
 
-    /* resampling the waveform, hardcore way. Many thanks to
-     * http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
-     * Note: we use
-     *   p = j * (m-1 / n)
-     * instead of
-     *   p = j * (m-1 / n-1)
-     * in order to obtain 'datasize' cells to parse (and not datasize-1) */
+  for (int i=0; i<m_data.size; i++) {
+    
+    /* Scan the original waveform in chunks [pc, pn]. */
 
-    pp = i * ((chan->wave->size - 1) / (float) datasize);
-    pn = (i+1) * ((chan->wave->size - 1) / (float) datasize);
-
-    if (pp % 2 != 0) pp -= 1;
-    if (pn % 2 != 0) pn -= 1;
+    float pc = i     * m_ratio;  // current point
+    float pn = (i+1) * m_ratio;  // next point
 
     float peaksup = 0.0f;
     float peakinf = 0.0f;
 
-    /* scan the original data in chunks */
+    for (float k=pc; k<pn; k++) {
 
-    int k = pp;
-    while (k < pn) {
+       if (k >= wave->getSize())
+               continue;
 
-      if (chan->wave->data[k] > peaksup)
-        peaksup = chan->wave->data[k];    // FIXME - Left data only
-      else
-      if (chan->wave->data[k] <= peakinf)
-        peakinf = chan->wave->data[k];    // FIXME - Left data only
+      /* Compute average of stereo signal. */
 
-      /* print grid */
+                       float avg = 0.0f;
+                       float* frame = wave->getFrame(k);
+                       for (int j=0; j<wave->getChannels(); j++)
+                               avg += frame[j];
+                       avg /= wave->getChannels();
+                       
+                       /* Find peaks (greater and lower). */
 
-      if (gridFreq != 0)
-        if (k % gridFreq == 0 && k != 0)
-          grid.points.push_back(i);
+                       if      (avg > peaksup)  peaksup = avg;
+                       else if (avg <= peakinf) peakinf = avg;
 
-      k += 2;
+                       /* Fill up grid vector. */
+
+                       if (gridFreq != 0 && (int) k % gridFreq == 0 && k != 0)
+                               m_grid.points.push_back(k);
     }
 
-    data.sup[i] = zero - (peaksup * chan->getBoost() * offset);
-    data.inf[i] = zero - (peakinf * chan->getBoost() * offset);
+    m_data.sup[i] = zero - (peaksup * m_ch->getBoost() * offset);
+    m_data.inf[i] = zero - (peakinf * m_ch->getBoost() * offset);
 
     // avoid window overflow
 
-    if (data.sup[i] < y())       data.sup[i] = y();
-    if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1;
+    if (m_data.sup[i] < y())       m_data.sup[i] = y();
+    if (m_data.inf[i] > y()+h()-1) m_data.inf[i] = y()+h()-1;
   }
 
   recalcPoints();
@@ -189,10 +194,8 @@ int geWaveform::alloc(int datasize)
 
 void geWaveform::recalcPoints()
 {
-  selection.aPixel = relativePoint(selection.aFrame);
-  selection.bPixel = relativePoint(selection.bFrame);
-  chanStart = relativePoint(chan->begin / 2);
-  chanEnd   = relativePoint(chan->end / 2);
+  m_chanStart = m_ch->getBegin();
+  m_chanEnd   = m_ch->getEnd();
 }
 
 
@@ -204,18 +207,18 @@ void geWaveform::drawSelection()
   if (!isSelected()) 
     return;
 
-  int a_x = selection.aPixel + x(); // - start;
-  int b_x = selection.bPixel + x(); //  - start;
+  int a = frameToPixel(m_selection.a) + x();
+  int b = frameToPixel(m_selection.b) + x();
 
-  if (a_x < 0)
-    a_x = 0;
-  if (b_x >= w() + BORDER)
-    b_x = w() + BORDER;
+  if (a < 0)
+    a = 0;
+  if (b >= w() + BORDER)
+    b = w() + BORDER;
 
-  if (selection.aPixel < selection.bPixel)
-    fl_rectf(a_x, y(), b_x-a_x, h(), G_COLOR_GREY_4);
+  if (a < b)
+    fl_rectf(a, y(), b-a, h(), G_COLOR_GREY_4);
   else
-    fl_rectf(b_x, y(), a_x-b_x, h(), G_COLOR_GREY_4);
+    fl_rectf(b, y(), a-b, h(), G_COLOR_GREY_4);
 }
 
 
@@ -228,8 +231,10 @@ void geWaveform::drawWaveform(int from, int to)
 
   fl_color(G_COLOR_BLACK);
   for (int i=from; i<to; i++) {
-    fl_line(i+x(), zero, i+x(), data.sup[i]);
-    fl_line(i+x(), zero, i+x(), data.inf[i]);
+       if (i >= m_data.size)
+               break;
+    fl_line(i+x(), zero, i+x(), m_data.sup[i]);
+    fl_line(i+x(), zero, i+x(), m_data.inf[i]);
   }
 }
 
@@ -239,17 +244,16 @@ void geWaveform::drawWaveform(int from, int to)
 
 void geWaveform::drawGrid(int from, int to)
 {
-  fl_color(G_COLOR_GREY_3);
-  fl_line_style(FL_DASH, 1, nullptr);
-  for (int i=from; i<to; i++) {
-    for (unsigned k=0; k<grid.points.size(); k++) {
-      if (grid.points.at(k) != i) 
-        continue;
-      fl_line(i+x(), y(), i+x(), y()+h());
-      break;
-    }
-  }
-  fl_line_style(FL_SOLID, 0, nullptr);
+       fl_color(G_COLOR_GREY_3);
+       fl_line_style(FL_DASH, 1, nullptr);
+
+       for (int pf : m_grid.points) {
+               int pp = frameToPixel(pf);
+               if (pp > from && pp < to)
+                       fl_line(pp+x(), y(), pp+x(), y()+h());
+       }
+
+       fl_line_style(FL_SOLID, 0, nullptr);
 }
 
 
@@ -258,11 +262,11 @@ void geWaveform::drawGrid(int from, int to)
 
 void geWaveform::drawStartEndPoints()
 {
-  /* print chanStart */
+  /* print m_chanStart */
 
-  int lineX = chanStart + x();
+  int lineX = frameToPixel(m_chanStart) + x();
 
-  if (chanStartLit) fl_color(G_COLOR_LIGHT_2);
+  if (m_chanStartLit) fl_color(G_COLOR_LIGHT_2);
   else              fl_color(G_COLOR_LIGHT_1);
 
   /* vertical line */
@@ -276,10 +280,10 @@ void geWaveform::drawStartEndPoints()
   else
     fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
 
-  /* print chanEnd */
+  /* print m_chanEnd */
 
-  lineX = chanEnd + x() - 1;
-  if (chanEndLit) fl_color(G_COLOR_LIGHT_2);
+  lineX = frameToPixel(m_chanEnd) + x() - 1;
+  if (m_chanEndLit) fl_color(G_COLOR_LIGHT_2);
   else            fl_color(G_COLOR_LIGHT_1);
 
   /* vertical line */
@@ -298,9 +302,7 @@ void geWaveform::drawStartEndPoints()
 
 void geWaveform::drawPlayHead()
 {
-  if (chan->status == STATUS_OFF)
-    return;
-  int p = ceilf(chan->tracker / ratio) + x();
+  int p = frameToPixel(m_ch->getTrackerPreview()) + x();
   fl_color(G_COLOR_LIGHT_2);
   fl_line(p, y() + 1, p, y() + h() - 2);
 }
@@ -311,8 +313,8 @@ void geWaveform::drawPlayHead()
 
 void geWaveform::draw()
 {
-  assert(data.sup != nullptr);
-  assert(data.inf != nullptr);
+  assert(m_data.sup != nullptr);
+  assert(m_data.inf != nullptr);
 
   fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2);  // blank canvas
 
@@ -340,102 +342,97 @@ void geWaveform::draw()
 
 int geWaveform::handle(int e)
 {
-  int ret = 0;
+       m_mouseX = pixelToFrame(Fl::event_x() - x());
+       m_mouseY = pixelToFrame(Fl::event_y() - y());
 
   switch (e) {
 
+    case FL_KEYDOWN: {
+      if (Fl::event_key() == ' ')
+        static_cast<gdSampleEditor*>(window())->__cb_togglePreview();
+      else
+      if (Fl::event_key() == FL_BackSpace)
+                               sampleEditor::rewindPreview(m_ch);
+      return 1;
+    }
+
     case FL_PUSH: {
 
-      mouseX = Fl::event_x();
-      pushed = true;
+      m_pushed = true;
 
       if (!mouseOnEnd() && !mouseOnStart()) {
-        if (Fl::event_button3()) {  // let the parent (waveTools) handle this
-          ret = 0;
-          break;
-        }
+        if (Fl::event_button3())  // let the parent (waveTools) handle this
+          return 0;
         if (mouseOnSelectionA())
-          resizedA = true;
+          m_resizedA = true;
         else
         if(mouseOnSelectionB())
-          resizedB = true;
+          m_resizedB = true;
         else {
-          dragged = true;
-          selection.aPixel = Fl::event_x() - x();
-          selection.bPixel = selection.aPixel;
+          m_dragged = true;
+          m_selection.a = m_mouseX;
+          m_selection.b = m_mouseX;
         }
       }
-      ret = 1;
-      break;
+      return 1;
     }
 
     case FL_RELEASE: {
 
-      /* If selection has been done (dragged or resized), make sure that point A 
+       sampleEditor::setPlayHead(m_ch, m_mouseX);
+
+      /* If selection has been done (m_dragged or resized), make sure that point A 
       is always lower than B. */
 
-      if (dragged || resizedA || resizedB)
+      if (m_dragged || m_resizedA || m_resizedB)
         fixSelection();
 
       /* Handle begin/end markers interaction. */
 
-      if (chanStartLit || chanEndLit) {
-        int realChanStart = chan->begin;
-        int realChanEnd   = chan->end;
-        if (chanStartLit)
-          realChanStart = absolutePoint(chanStart) * 2;
-        else
-        if (chanEndLit)
-          realChanEnd = absolutePoint(chanEnd) * 2;
-        sampleEditor::setBeginEndChannel(chan, realChanStart, realChanEnd);
-      }
+      if (m_chanStartLit || m_chanEndLit)
+        sampleEditor::setBeginEndChannel(m_ch, m_chanStart, m_chanEnd);
 
-      pushed   = false;
-      dragged  = false;
-      resizedA = false;
-      resizedB = false;
+      m_pushed   = false;
+      m_dragged  = false;
+      m_resizedA = false;
+      m_resizedB = false;
 
       redraw();
-      ret = 1;
-      break;
+      return 1;
     }
 
     case FL_ENTER: {  // enables FL_DRAG
-      ret = 1;
-      break;
+      return 1;
     }
 
     case FL_LEAVE: {
-      if (chanStartLit || chanEndLit) {
-        chanStartLit = false;
-        chanEndLit   = false;
+      if (m_chanStartLit || m_chanEndLit) {
+        m_chanStartLit = false;
+        m_chanEndLit   = false;
         redraw();
       }
-      ret = 1;
-      break;
+      return 1;
     }
 
     case FL_MOVE: {
-      mouseX = Fl::event_x();
-      mouseY = Fl::event_y();
 
       if (mouseOnStart()) {
-        chanStartLit = true;
+        m_chanStartLit = true;
         redraw();
       }
       else
-      if (chanStartLit) {
-        chanStartLit = false;
+      if (m_chanStartLit) {
+        m_chanStartLit = false;
         redraw();
       }
 
       if (mouseOnEnd()) {
-        chanEndLit = true;
+        m_chanEndLit = true;
         redraw();
       }
       else
-      if (chanEndLit) {
-        chanEndLit = false;
+      if (m_chanEndLit) {
+        m_chanEndLit = false;
         redraw();
       }
 
@@ -447,42 +444,35 @@ int geWaveform::handle(int e)
       else
         fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
 
-      ret = 1;
-      break;
+      return 1;
     }
 
     case FL_DRAG: {
 
-      /* here the mouse is on the chanStart tool */
+      /* here the mouse is on the m_chanStart tool */
 
-      if (chanStartLit && pushed) {
+      if (m_chanStartLit && m_pushed) {
 
-        chanStart = Fl::event_x() - x();
+        m_chanStart = snap(m_mouseX);
 
-        if (grid.snap)
-          chanStart = applySnap(chanStart);
-
-        if (chanStart < 0)
-          chanStart = 0;
+        if (m_chanStart < 0)
+          m_chanStart = 0;
         else
-        if (chanStart >= chanEnd)
-          chanStart = chanEnd - 2;
+        if (m_chanStart >= m_chanEnd)
+          m_chanStart = m_chanEnd - 2;
 
         redraw();
       }
       else
-      if (chanEndLit && pushed) {
-
-        chanEnd = Fl::event_x() - x();
+      if (m_chanEndLit && m_pushed) {
 
-        if (grid.snap)
-          chanEnd = applySnap(chanEnd);
+        m_chanEnd = snap(m_mouseX);
 
-        if (chanEnd > data.size)
-          chanEnd = data.size;
+        if (m_chanEnd > m_ch->wave->getSize())
+          m_chanEnd = m_ch->wave->getSize();
         else
-        if (chanEnd <= chanStart)
-          chanEnd = chanStart + 2;
+        if (m_chanEnd <= m_chanStart)
+          m_chanEnd = m_chanStart + 2;
 
         redraw();
       }
@@ -490,49 +480,44 @@ int geWaveform::handle(int e)
       /* Here the mouse is on the waveform, i.e. a new selection has started. */
 
       else
-      if (dragged) {
-        selection.bPixel = Fl::event_x() - x();
-        if (grid.snap)
-          selection.bPixel = applySnap(selection.bPixel);
+      if (m_dragged) {
+        m_selection.b = snap(m_mouseX);
         redraw();
       }
 
       /* here the mouse is on a selection boundary i.e. resize */
 
       else
-      if (resizedA || resizedB) {
-        int pos = Fl::event_x() - x();
-        if (grid.snap)
-          pos = applySnap(pos);
-        resizedA ? selection.aPixel = pos : selection.bPixel = pos;
+      if (m_resizedA || m_resizedB) {
+        int pos = snap(m_mouseX);
+        m_resizedA ? m_selection.a = pos : m_selection.b = pos;
         redraw();
       }
 
-      mouseX = Fl::event_x();
-      ret = 1;
-      break;
+      return 1;
     }
+
+    default:
+      return Fl_Widget::handle(e);
   }
-  return ret;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int geWaveform::applySnap(int pos)
+int geWaveform::snap(int pos)
 {
-  /* Pixel snap disances (SNAPPING) must be equal to those defined in 
-  mouseOnSelectionA() and mouseOnSelectionB(). */
-
-  for (unsigned i=0; i<grid.points.size(); i++) {
-    if (pos >= grid.points.at(i) - SNAPPING &&
-        pos <= grid.points.at(i) + SNAPPING)
-    {
-      return grid.points.at(i);
-    }
-  }
-  return pos;
+       if (!m_grid.snap)
+               return pos;
+       for (int pf : m_grid.points) {
+               if (pos >= pf - pixelToFrame(SNAPPING) &&
+                   pos <= pf + pixelToFrame(SNAPPING))
+               {
+                       return pf;
+               }
+       }
+       return pos;
 }
 
 
@@ -541,9 +526,12 @@ int geWaveform::applySnap(int pos)
 
 bool geWaveform::mouseOnStart()
 {
-  return mouseX - (FLAG_WIDTH / 2) >  chanStart + x() - BORDER              &&
-         mouseX - (FLAG_WIDTH / 2) <= chanStart + x() - BORDER + FLAG_WIDTH &&
-         mouseY    >  h() + y() - FLAG_HEIGHT;
+       int mouseXp    = frameToPixel(m_mouseX);
+       int mouseYp    = frameToPixel(m_mouseY);
+       int chanStartP = frameToPixel(m_chanStart);
+  return mouseXp - (FLAG_WIDTH / 2) >  chanStartP - BORDER              &&
+         mouseXp - (FLAG_WIDTH / 2) <= chanStartP - BORDER + FLAG_WIDTH &&
+         mouseYp > h() - FLAG_HEIGHT;
 }
 
 
@@ -552,52 +540,53 @@ bool geWaveform::mouseOnStart()
 
 bool geWaveform::mouseOnEnd()
 {
-  return mouseX - (FLAG_WIDTH / 2) >= chanEnd + x() - BORDER - FLAG_WIDTH &&
-         mouseX - (FLAG_WIDTH / 2) <= chanEnd + x() - BORDER              &&
-         mouseY    <= y() + FLAG_HEIGHT + 1;
+       int mouseXp  = frameToPixel(m_mouseX);
+       int mouseYp  = frameToPixel(m_mouseY);
+       int chanEndP = frameToPixel(m_chanEnd);
+  return mouseXp - (FLAG_WIDTH / 2) >= chanEndP - BORDER - FLAG_WIDTH &&
+         mouseXp - (FLAG_WIDTH / 2) <= chanEndP - BORDER              &&
+         mouseYp <= FLAG_HEIGHT + 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-/* pixel boundaries (10px) must be equal to the snap factor distance
- * defined in geWaveform::applySnap() */
 
 bool geWaveform::mouseOnSelectionA()
 {
-  return mouseX >= selection.aPixel - (FLAG_WIDTH / 2) + x() && 
-         mouseX <= selection.aPixel + (FLAG_WIDTH / 2) + x();
+       int mouseXp = frameToPixel(m_mouseX);
+       int selAp   = frameToPixel(m_selection.a);
+  return mouseXp >= selAp - (FLAG_WIDTH / 2) && mouseXp <= selAp + (FLAG_WIDTH / 2);
 }
 
 
 bool geWaveform::mouseOnSelectionB()
 {
-  return mouseX >= selection.bPixel - (FLAG_WIDTH / 2) + x() && 
-         mouseX <= selection.bPixel + (FLAG_WIDTH / 2) + x();
+       int mouseXp = frameToPixel(m_mouseX);
+       int selBp   = frameToPixel(m_selection.b);
+  return mouseXp >= selBp - (FLAG_WIDTH / 2) && mouseXp <= selBp + (FLAG_WIDTH / 2);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int geWaveform::absolutePoint(int p)
+int geWaveform::pixelToFrame(int p)
 {
   if (p <= 0)
     return 0;
-
-  if (p > data.size)
-    return chan->wave->size / 2;
-
-  return (p * ratio) / 2;
+  if (p > m_data.size)
+    return m_ch->wave->getSize() - 1;
+  return p * m_ratio;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int geWaveform::relativePoint(int p)
+int geWaveform::frameToPixel(int p)
 {
-  return (ceil(p / ratio)) * 2;
+  return ceil(p / m_ratio);
 }
 
 
@@ -606,10 +595,9 @@ int geWaveform::relativePoint(int p)
 
 void geWaveform::fixSelection()
 {
-  if (selection.aPixel > selection.bPixel) // inverted selection
-    std::swap(selection.aPixel, selection.bPixel);
-  selection.aFrame = absolutePoint(selection.aPixel);
-  selection.bFrame = absolutePoint(selection.bPixel);
+  if (m_selection.a > m_selection.b) // inverted m_selection
+    std::swap(m_selection.a, m_selection.b);
+  sampleEditor::setPlayHead(m_ch, m_selection.a);
 }
 
 
@@ -618,10 +606,8 @@ void geWaveform::fixSelection()
 
 void geWaveform::clearSel()
 {
-  selection.aPixel = 0;
-  selection.bPixel = 0;
-  selection.aFrame = 0;
-  selection.bFrame = 0;  
+  m_selection.a = 0;
+  m_selection.b = 0;  
 }
 
 
@@ -630,40 +616,30 @@ void geWaveform::clearSel()
 
 void geWaveform::setZoom(int type)
 {
-  int newSize = type == ZOOM_IN ? data.size * 2 : data.size / 2;
-
-  if (!alloc(newSize)) 
+  if (!alloc(type == ZOOM_IN ? m_data.size * 2 : m_data.size / 2)) 
     return;
 
-  size(newSize, h());
+  size(m_data.size, h());
 
-  /* zoom to pointer */
+  /* Zoom to cursor. */
+       
+       int newX = -frameToPixel(m_mouseX) + Fl::event_x();
+       if (newX > BORDER)
+               newX = BORDER;
+       position(newX, y());
 
-  int shift;
-  if (x() > 0)
-    shift = Fl::event_x() - x();
-  else
-  if (type == ZOOM_IN)
-    shift = Fl::event_x() + abs(x());
-  else
-    shift = (Fl::event_x() + abs(x())) / -2;
-
-  if (x() - shift > BORDER)
-    shift = 0;
+  /* Avoid overflow when zooming out with scrollbar like that:
 
-  position(x() - shift, y());
+       |----------[scrollbar]|
 
+  Offset vs smaller:
+  
+       |[wave------------| offset > 0  smaller = false
+       |[wave----]       | offset < 0, smaller = true
+       |-------------]   | offset < 0, smaller = false  */
 
-  /* avoid overflow when zooming out with scrollbar like that:
-   * |----------[scrollbar]|
-   *
-   * offset vs smaller:
-   * |[wave------------| offset > 0  smaller = false
-   * |[wave----]       | offset < 0, smaller = true
-   * |-------------]   | offset < 0, smaller = false  */
-
-  int  parentW = parent()->w();
-  int  thisW   = x() + w() - BORDER;           // visible width, not full width
+  int parentW = parent()->w();
+  int thisW   = x() + w() - BORDER;           // visible width, not full width
 
   if (thisW < parentW)
     position(x() + parentW - thisW, y());
@@ -691,7 +667,7 @@ void geWaveform::stretchToWindow()
 
 void geWaveform::refresh()
 {
-  alloc(data.size);
+  alloc(m_data.size, true); // force
   redraw();
 }
 
@@ -710,9 +686,9 @@ bool geWaveform::smaller()
 
 void geWaveform::setGridLevel(int l)
 {
-  grid.points.clear();
-  grid.level = l;
-  alloc(data.size);
+  m_grid.points.clear();
+  m_grid.level = l;
+  alloc(m_data.size, true); // force alloc
   redraw();
 }
 
@@ -722,21 +698,29 @@ void geWaveform::setGridLevel(int l)
 
 bool geWaveform::isSelected()
 {
-  return selection.aPixel != selection.bPixel;
+  return m_selection.a != m_selection.b;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
+void geWaveform::setSnap(bool v) { m_grid.snap = v; }
+bool geWaveform::getSnap() { return m_grid.snap; }
+int geWaveform::getSize() { return m_data.size; }
+
+
+/* -------------------------------------------------------------------------- */
+
+
 int geWaveform::getSelectionA()
 {
-  return selection.aFrame;
+  return m_selection.a;
 }
 
 
 int geWaveform::getSelectionB()
 {
-  return selection.bFrame;
+  return m_selection.b;
 }
 
index 8f7cc8f8cb85a4d7c659365bad0f0b258d6bd75d..a4278601a0a7b427c65a20079ae12030fac0f50a 100644 (file)
@@ -43,18 +43,16 @@ private:
        static const int FLAG_WIDTH  = 20;
        static const int FLAG_HEIGHT = 20;
        static const int BORDER      = 8;  // window border <-> widget border
-       static const int SNAPPING    = 10;
+       static const int SNAPPING    = 16;
 
        /* selection
-       Portion of the selected wave, in pixel and in frames. */
+       Portion of the selected wave, in frames. */
 
        struct
        {
-               int aPixel;
-               int bPixel;
-               int aFrame;
-               int bFrame;
-       } selection;
+               int a;
+               int b;
+       } m_selection;
 
        /* data
        Real graphic stuff from the underlying waveform. */
@@ -62,18 +60,29 @@ private:
        struct
   {
                int* sup;   // upper part of the waveform
-               int* inf;   // lower ""   "" ""  ""
+               int* inf;   // lower part of the waveform
                int  size;  // width of the waveform to draw (in pixel)
-       } data;
+       } m_data;
 
        struct
   {
                bool snap;
                int level;
                std::vector<int> points;
-       } grid;
-
-       SampleChannel* chan;
+       } m_grid;
+
+       SampleChannel* m_ch;
+       int m_chanStart;
+       bool m_chanStartLit;
+       int m_chanEnd;
+       bool m_chanEndLit;
+       bool m_pushed;
+       bool m_dragged;
+       bool m_resizedA;
+       bool m_resizedB;
+       float m_ratio;
+       int m_mouseX;
+       int m_mouseY;
 
        /* mouseOnStart/end
        Is mouse on start or end flag? */
@@ -87,16 +96,8 @@ private:
        bool mouseOnSelectionA();
        bool mouseOnSelectionB();
 
-       /* absolutePoint
-       From a relative 'p' point (zoom affected) returns the same point zoom 1:1 
-       based. */
-
-       int absolutePoint(int p);
-
-       /* relativePoint
-       From an absolute 'p' point (1:1 zoom) returns the same point zoom affected. */
-
-       int relativePoint(int p);
+       int pixelToFrame(int p);
+       int frameToPixel(int f);
 
        /* fixSelection
        Helper function which flattens the selection if it was made from right to left 
@@ -115,10 +116,10 @@ private:
 
        bool smaller();
 
-  /* applySnap
-  Snap a point at 'pos' pixel. */
+  /* snap
+  Snaps a point at 'pos' pixel. */
 
-  int applySnap(int pos);
+  int snap(int pos);
 
   /* draw*
   Drawing functions. */
@@ -141,12 +142,13 @@ public:
        int  handle(int e) override;
 
        /* alloc
-        * allocate memory for the picture */
+       Allocates memory for the picture. It's smart enough not to reallocate if 
+       datasize hasn't changed, but it can be forced otherwise. */
 
-       int alloc(int datasize=0);
+       int alloc(int datasize, bool force=false);
 
        /* recalcPoints
-        * re-calc chanStart, chanEnd, ... */
+        * re-calc m_chanStart, m_chanEnd, ... */
 
        void recalcPoints();
 
@@ -170,10 +172,9 @@ public:
 
        void setGridLevel(int l);
 
-  void setSnap(bool v) { grid.snap = v; }
-  bool getSnap()       { return grid.snap; }
-
-       int getSize() { return data.size; }
+  void setSnap(bool v);
+  bool getSnap();
+       int getSize();
 
        /* isSelected
        Tells whether a portion of the waveform has been selected. */
@@ -187,18 +188,6 @@ public:
        Removes any active selection. */
 
        void clearSel();
-
-       int  chanStart;
-       bool chanStartLit;
-       int  chanEnd;
-       bool chanEndLit;
-       bool pushed;
-       bool dragged;
-       bool resizedA;
-       bool resizedB;
-       float ratio;
-       int mouseX;
-       int mouseY;
 };
 
 
index b7ea894360e42365d13d10680e24c9ce713f52a6..9752b12299525d887345bfb1d756b8e3323df765 100644 (file)
@@ -87,7 +87,7 @@ void gu_refreshUI()
        /* If Sample Editor is open, repaint it (for dynamic play head). */
 
        gdSampleEditor* se = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
-       if (se)
+       if (se != nullptr)
                se->waveTools->redrawWaveformAsync();
 
        /* redraw GUI */
index 0e5cc9c385c43a6885aaf963220a6514e80882ca..92df941176dd4080b691d019612b6aac9ef2678a 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <climits>
 #include <sstream>
+#include "../core/const.h"
 #include "string.h"
 
 
@@ -40,7 +41,7 @@ string gu_getRealPath(const string &path)
 {
        string out = "";
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(G_OS_LINUX) || defined(G_OS_MAC)
 
        char *buf = realpath(path.c_str(), nullptr);
 
@@ -61,12 +62,23 @@ string gu_getRealPath(const string &path)
 /* -------------------------------------------------------------------------- */
 
 
-string gu_itoa(int i)
+string gu_toString(int i)
 {
-    // TODO - use std::to_string -> http://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int?rq=1
+       /* std::to_string is the way to go. Unfortunately  it seems that it isn't 
+       available in gcc's standard library (libstdc++), it is however, available in
+       libc++ which comes with LLVM/clang. */
+
+#ifdef G_OS_MAC
+
        std::stringstream out;
        out << i;
        return out.str();
+
+#else
+
+       return std::to_string(i);
+
+#endif
 }
 
 
@@ -105,8 +117,8 @@ void gu_split(string in, string sep, vector<string> *v)
        size_t curr = 0;
        size_t next = -1;
        do {
-         curr  = next + 1;
-         next  = full.find_first_of(sep, curr);
+               curr  = next + 1;
+               next  = full.find_first_of(sep, curr);
                token = full.substr(curr, next - curr);
                if (token != "")
                        v->push_back(token);
index a460a352bbc8bf700d6335358de80e91951c22e7..401b773476e9ff4d66dcee340800b5708e2e2f83 100644 (file)
@@ -42,8 +42,7 @@ std::string gu_replace(std::string in, const std::string &search,
 
 std::string gu_trim(const std::string &s);
 
-// TODO - use std::to_string -> http://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int?rq=1
-std::string gu_itoa(int i);
+std::string gu_toString(int i);
 
 void gu_split(std::string in, std::string sep, std::vector<std::string> *v);
 
index 378745c350a07c13220e2d2df1d1527aed849030..852b77a86802fce6f0bb1d63121780c9a3d8dcbd 100644 (file)
@@ -25,7 +25,7 @@ TEST_CASE("Test string utils")
 {
   REQUIRE(gu_replace("Giada is cool", "cool", "hot") == "Giada is hot");
   REQUIRE(gu_trim("   Giada is cool       ") == "Giada is cool");
-  REQUIRE(gu_itoa(666) == "666");
+  REQUIRE(gu_toString(666) == "666");
 
   vector<std::string> v;
   gu_split("Giada is cool", " ", &v);
index 14666791d7cf2bee9903854c45ec85c2d6d07297..4f2e18aa0ea759bf40fea6b53016cdb28038a676 100644 (file)
@@ -1,3 +1,4 @@
+#include <memory>
 #include "../src/core/wave.h"
 #include "catch/single_include/catch.hpp"
 
@@ -5,41 +6,49 @@
 using std::string;
 
 
-#define G_SAMPLE_RATE 44100
-#define G_BUFFER_SIZE 4096
-
-
 TEST_CASE("Test Wave class")
 {
-  Wave w1;
+       static const int SAMPLE_RATE = 44100;
+       static const int BUFFER_SIZE = 4096;
+       static const int CHANNELS = 2;
+       static const int BIT_DEPTH = 32;
 
-  SECTION("test read & write")
+  /* Each SECTION the TEST_CASE is executed from the start. Any code between 
+  this comment and the first SECTION macro is exectuted before each SECTION. */
+
+  std::unique_ptr<Wave> wave;
+  
+  SECTION("test basename")
   {
-    REQUIRE(w1.open("tests/resources/test.wav") == 1);
-    REQUIRE(w1.readData() == 1);
-    REQUIRE(w1.rate() == 44100);
-    REQUIRE(w1.channels() == 1);
-    REQUIRE(w1.basename() == "test");
-    REQUIRE(w1.extension() == "wav");
-    REQUIRE(w1.writeData("test-write.wav") == true);
-  }
+    wave = std::unique_ptr<Wave>(new Wave(nullptr, BUFFER_SIZE, CHANNELS, 
+      SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
 
-  SECTION("test copy constructor")
+    REQUIRE(wave->getPath() == "path/to/sample.wav");
+    REQUIRE(wave->getBasename() == "sample");
+    REQUIRE(wave->getBasename(true) == "sample.wav");
+  }  
+
+  SECTION("test change name")
   {
-    Wave w2(w1);
-    REQUIRE(w2.size == w1.size);
-    REQUIRE(w2.isLogical == true);
-    //REQUIRE(w2.rate() == 44100);  // WHAT THE FUCK???
-    REQUIRE(w2.channels() == 1);
-    REQUIRE(w2.writeData("test-write.wav") == true);
+    wave = std::unique_ptr<Wave>(new Wave(nullptr, BUFFER_SIZE, CHANNELS, 
+      SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
+    wave->setName("waveform");
+
+    REQUIRE(wave->getPath() == "path/to/waveform.wav");
+    REQUIRE(wave->getBasename() == "waveform");
+    REQUIRE(wave->getBasename(true) == "waveform.wav");
   }
 
-  SECTION("test rec")
+  SECTION("test memory cleanup")
   {
-    Wave w3;
-    REQUIRE(w3.allocEmpty(G_BUFFER_SIZE, G_SAMPLE_RATE) == 1);
-    REQUIRE(w3.rate() == G_SAMPLE_RATE);
-    REQUIRE(w3.channels() == 2);
-    REQUIRE(w3.writeData("test-write.wav") == true);
+    float* data = new float[BUFFER_SIZE];
+
+    wave = std::unique_ptr<Wave>(new Wave(data, BUFFER_SIZE, CHANNELS, 
+      SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
+    wave->clear();
+
+    REQUIRE(wave->getData() == nullptr);
+    REQUIRE(wave->getPath() == "");
+    REQUIRE(wave->getSize() == 0);
   }
 }
diff --git a/tests/waveFx.cpp b/tests/waveFx.cpp
new file mode 100644 (file)
index 0000000..3032078
--- /dev/null
@@ -0,0 +1,143 @@
+#include <memory>
+#include "../src/core/const.h"
+#include "../src/core/wave.h"
+#include "../src/core/waveFx.h"
+#include "catch/single_include/catch.hpp"
+
+
+using std::string;
+using namespace giada::m;
+
+
+TEST_CASE("Test waveFx")
+{
+       static const int SAMPLE_RATE = 44100;
+       static const int BUFFER_SIZE = 4000;
+       static const int BIT_DEPTH = 32;
+
+       std::unique_ptr<Wave> waveMono = std::unique_ptr<Wave>(new Wave(new float[BUFFER_SIZE], 
+               BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
+
+       std::unique_ptr<Wave> waveStereo = std::unique_ptr<Wave>(new Wave(new float[BUFFER_SIZE * 2], 
+               BUFFER_SIZE, 2, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
+
+       SECTION("test mono->stereo conversion")
+       {
+               int prevSize = waveMono->getSize();
+
+               REQUIRE(wfx::monoToStereo(waveMono.get()) == G_RES_OK);
+               REQUIRE(waveMono->getSize() == prevSize);  // size does not change, channels do
+               REQUIRE(waveMono->getChannels() == 2);
+
+               SECTION("test mono->stereo conversion for already stereo wave")
+               {
+                       /* Should do nothing. */
+                       int prevSize = waveStereo->getSize();
+
+                       REQUIRE(wfx::monoToStereo(waveStereo.get()) == G_RES_OK);
+                       REQUIRE(waveStereo->getSize() == prevSize);
+                       REQUIRE(waveStereo->getChannels() == 2);
+               }
+       }
+
+       SECTION("test silence")
+       {
+               int a = 20;
+               int b = 57;
+               wfx::silence(waveStereo.get(), a, b);
+
+               for (int i=a; i<b; i++) {
+                       float* frame = waveStereo->getFrame(i);
+                       for (int k=0; k<waveStereo->getChannels(); k++)
+                               REQUIRE(frame[k] == 0.0f);
+               }
+
+               SECTION("test silence (mono)")
+               {
+                       wfx::silence(waveMono.get(), a, b);
+
+                       for (int i=a; i<b; i++) {
+                               float* frame = waveMono->getFrame(i);
+                               for (int k=0; k<waveMono->getChannels(); k++)
+                                       REQUIRE(frame[k] == 0.0f);
+                       }
+               }
+       }
+
+       SECTION("test cut")
+       {
+               int a = 47;
+               int b = 210;
+               int range = b - a;
+               int prevSize = waveStereo->getSize();
+
+               REQUIRE(wfx::cut(waveStereo.get(), a, b) == G_RES_OK);
+               REQUIRE(waveStereo->getSize() == prevSize - range);
+
+               SECTION("test cut (mono)")
+               {
+                       prevSize = waveMono->getSize();
+                       REQUIRE(wfx::cut(waveMono.get(), a, b) == G_RES_OK);
+                       REQUIRE(waveMono->getSize() == prevSize - range);
+               }
+       }
+
+       SECTION("test trim")
+       {
+               int a = 47;
+               int b = 210;
+               int area = b - a;
+
+               REQUIRE(wfx::trim(waveStereo.get(), a, b) == G_RES_OK);
+               REQUIRE(waveStereo->getSize() == area);
+
+               SECTION("test trim (mono)")
+               {
+                       REQUIRE(wfx::trim(waveMono.get(), a, b) == G_RES_OK);
+                       REQUIRE(waveMono->getSize() == area);
+               }
+       }
+
+       SECTION("test fade")
+       {
+               int a = 47;
+               int b = 500;
+
+               wfx::fade(waveStereo.get(), a, b, wfx::FADE_IN);
+               wfx::fade(waveStereo.get(), a, b, wfx::FADE_OUT);
+
+               REQUIRE(waveStereo->getFrame(a)[0] == 0.0f);
+               REQUIRE(waveStereo->getFrame(a)[1] == 0.0f);
+               REQUIRE(waveStereo->getFrame(b)[0] == 0.0f);
+               REQUIRE(waveStereo->getFrame(b)[1] == 0.0f);
+
+               SECTION("test fade (mono)")
+               {
+                       wfx::fade(waveMono.get(), a, b, wfx::FADE_IN);
+                       wfx::fade(waveMono.get(), a, b, wfx::FADE_OUT);
+
+                       REQUIRE(waveMono->getFrame(a)[0] == 0.0f);
+                       REQUIRE(waveMono->getFrame(b)[0] == 0.0f);              
+               }
+       }
+
+       SECTION("test smooth")
+       {
+               int a = 11;
+               int b = 79;
+
+               wfx::smooth(waveStereo.get(), a, b);
+
+               REQUIRE(waveStereo->getFrame(a)[0] == 0.0f);
+               REQUIRE(waveStereo->getFrame(a)[1] == 0.0f);
+               REQUIRE(waveStereo->getFrame(b)[0] == 0.0f);
+               REQUIRE(waveStereo->getFrame(b)[1] == 0.0f);
+               
+               SECTION("test smooth (mono)")
+               {
+                       wfx::smooth(waveMono.get(), a, b);
+                       REQUIRE(waveMono->getFrame(a)[0] == 0.0f);
+                       REQUIRE(waveMono->getFrame(b)[0] == 0.0f);              
+               }
+       }
+}
diff --git a/tests/waveManager.cpp b/tests/waveManager.cpp
new file mode 100644 (file)
index 0000000..8008959
--- /dev/null
@@ -0,0 +1,67 @@
+#include <memory>
+#include "../src/core/waveManager.h"
+#include "../src/core/wave.h"
+#include "../src/core/const.h"
+#include "catch/single_include/catch.hpp"
+
+
+using std::string;
+using namespace giada::m;
+
+
+#define G_SAMPLE_RATE 44100
+#define G_BUFFER_SIZE 4096
+#define G_CHANNELS 2
+
+
+TEST_CASE("Test waveManager")
+{
+  /* Each SECTION the TEST_CASE is executed from the start. Any code between 
+  this comment and the first SECTION macro is exectuted before each SECTION. */
+
+  Wave* w;
+
+  SECTION("test creation")
+  {
+    int res = waveManager::create("tests/resources/test.wav", &w);
+    std::unique_ptr<Wave> wave(w);
+    
+    REQUIRE(res == G_RES_OK);
+    REQUIRE(wave->getRate() == G_SAMPLE_RATE);
+    REQUIRE(wave->getChannels() == G_CHANNELS);
+    REQUIRE(wave->isLogical() == false);
+    REQUIRE(wave->isEdited() == false);
+  }
+
+  SECTION("test recording")
+  {
+    int res = waveManager::createEmpty(G_BUFFER_SIZE, G_SAMPLE_RATE, 
+      "test.wav", &w);
+    std::unique_ptr<Wave> wave(w);
+
+    REQUIRE(res == G_RES_OK);
+    REQUIRE(wave->getRate() == G_SAMPLE_RATE);
+    REQUIRE(wave->getSize() == G_BUFFER_SIZE / wave->getChannels());
+    REQUIRE(wave->getChannels() == G_CHANNELS);
+    REQUIRE(wave->isLogical() == true);
+    REQUIRE(wave->isEdited() == false);
+  }
+
+  SECTION("test resampling")
+  {
+    int res = waveManager::create("tests/resources/test.wav", &w);
+    std::unique_ptr<Wave> wave(w);
+    
+    REQUIRE(res == G_RES_OK);
+
+    int oldSize = wave->getSize();
+    res = waveManager::resample(wave.get(), 1, G_SAMPLE_RATE * 2);
+    
+    REQUIRE(res == G_RES_OK);
+    REQUIRE(wave->getRate() == G_SAMPLE_RATE * 2);
+    REQUIRE(wave->getSize() == oldSize * 2);
+    REQUIRE(wave->getChannels() == G_CHANNELS);
+    REQUIRE(wave->isLogical() == false);
+    REQUIRE(wave->isEdited() == false);
+  }
+}