Forum Rule: Always post complete source code & details to reproduce any issue!
Page 5 of 5 FirstFirst ... 3 4 5
Results 101 to 116 of 116

Thread: WaveplayerEx

  1. #101
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,212
    Quote Originally Posted by Frank B View Post
    - If you say at some point that you really want to include it in the official version, I will change the license. But not before.
    Normally I would not even look at source code using an incompatible open source license. Especially for features I plan to eventually implement, just viewing how it was done can influence future work with really should be done independently when the license is incompatible.

    So far I have only looked at the readme as shown on the repository on github, and the license header. I stopped when I saw you used GPL3.

    Most of the features explained in the readme are things I have long wanted to implement in the audio library wav player. Whether I would use the code as implemented, I do not know yet, because I have not even looked at the code.

  2. #102
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Well, I think that's called "deadlock".

  3. #103
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Quote Originally Posted by Frank B View Post
    Well, I think that's called "deadlock".
    Ok, I think the player is useful for others, so I'll change to licence for some time to MIT.

    That's OK.
    I'll change it back to GPL if you decide not to use it. But please drop a note.
    Thank you.

  4. #104
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,212
    Yes, I will work with it later this month.

    3 other major audio contributions are also pending. Will probably spend several days to do all 4 together.

    Please be patient. I need you to understand many other important tasks need to be done, including minor PCB design changes for 3 Teensy models due to chip shortages on the diodes and voltage regulators (we just recently managed to buy the alternate parts).

  5. #105
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Quote Originally Posted by Frank B View Post
    - Yesterday I noticed that pause() does not work correctly with multiple files.
    I think it works now. Wasn't that easy

    Please report any remaining bugs as issue @ github. Thank you!

  6. #106
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    @Paul, I'm adding a "loop" feature. Please give me a few days.


    Other question: Can we use the existing "Resampler.cpp" to adapt to different samplerates?

  7. #107
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Quote Originally Posted by Frank B View Post
    @Paul, I'm adding a "loop" feature. Please give me a few days.


    Other question: Can we use the existing "Resampler.cpp" to adapt to different samplerates?
    Ok... had too much time today...
    Some new functions:

    size_t position(); - returns the current sample number. Will increase in steps of 128 (or AUDIO_BLOCK_SAMPLES)
    bool setPosition(size_t sample); works only in paused mode - sets the start position for pause(false).
    void loop(bool enable); - loops the whole file. Call it for an already playing or paused file. Will loop endless, until you call stop() or pause()
    void loop(size_t firstSample, size_t lastSample, uint16_t count); - number of first sample, last sample, count of repeats. If lastSample = 0 it uses the last sample in the file.
    int loopCount(void);- returns the count of remaining loops.

    The details are sometimes not as easy as it may sound... I will describe loop() in detail, later.

  8. #108
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Ok, let's assume we use the Nums_7dot1_8_44100.wav file from the examples. There is a voice, counting from 1 to 8 (each number on a different channel - but this is not interesting here)

    There is a gap of exactly one second between the numbers.

    1.
    Code:
    playWav.play("Nums_7dot1_8_44100.wav");
    Plays the whole file, one time.

    2.
    Code:
    playWav.play("Nums_7dot1_8_44100.wav");
    playWav.loop(true);
    Repeats the whole file until your ears bleed.

    3.
    Code:
    playWav.play("Nums_7dot1_8_44100.wav", true);
    playWav.setPosition(44100 * 5);
    playWav.pause(false);
    Starts in paused mode, sets the position to second 5, then un-pauses.


    4.
    Code:
    playWav.play("Nums_7dot1_8_44100.wav");
    playWav.loop(0, 0, -1);
    Same as 2.

    5.
    Code:
    playWav.play("Nums_7dot1_8_44100.wav");
    playWav.loop(0, 0, 0);
    Does noot loop (loop-count is zero)

    6.
    Code:
    playWav.play("Nums_7dot1_8_44100.wav");
    playWav.loop(0, 0, -1);
    delay(20000);
    playWav.loop(0, 0, 0);
    Like 5 - stopps looping after 20 seconds. The files plays to the end! Only the loop gets disabled.

    7.
    Now, the tricky parts:
    Code:
    playWav.play("Nums_7dot1_8_44100.wav");
    for (int i=7; i>0; i--) {
        playWav.loop(44100 * i, 44100 * (i + 1), 1);
        while ( playWav.loopCount() > 0 ) {;}
    }
    Well.. you'll hear:
    "1 2 3 4 5 6 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8"
    - the while waits until the looping is finished.
    - it does _not_ stop or pause then, instead it plays the file to the end. IF there is no new loop, pause or stop command.

    8.
    Code:
    playWav.play("Nums_7dot1_8_44100.wav");
      delay(5000);
      playWav.loop(0, 44100 * 3, 2);
    You'll hear:
    1 2 3 4 1 2 1 2 1 2 3 4 5 6 7 8
    -> it counts up to 4 because of the delay.
    -> after that the loop gets startet. Teensy detects that it already playing behind the stop marker of the loop and rewinds to the first sample.

    Important: It does not detect that immedeately. This happens when it trys to read the next block of data from them media.
    If you need exact timing, you need to define the loop _before_ the stop marker is reached.
    What means "before". Hm, that#s tricky. Rule of thumb: As early as possible. It has to happen before it reads the datablock where the end-sample is located.
    The size of the datablocks is variable, and depends on the number playWav (and record) objects and the AUDIO_BLOCK_SIZE.
    Just give it enough millisconds and try it out.
    Last edited by Frank B; 10-22-2021 at 01:38 PM.

  9. #109
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Ok, last addition for a while.
    As Teensy's LittleFS is not threadsafe and, for NAND Flash, takes way too long to open a file - (I've seen > 12 millisconds!), this can be seen as workaround, too.

    You don't need to rewind the files anymore
    Play() does it now for you:

    Play(file | filename [, bool paused] [, bool rewind]);
    So, if you set rewind to true, it will rewind the file to the start when the end of the file is reached, and set pause = true.
    It can be restarted very fast now (by calling pause(false); ) , without reading the header or calling Play() again.

    Example for Littlefs-NAND (but works with any other filesystem, too:
    Code:
    // WAV file player example
    // This example code is in the public domain.
    
    #include <LittleFS.h>
    
    LittleFS_QPINAND myfs;
    uint64_t fTot, totSize1;
    
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    
    #include <play_wav.h>
    
    // GUItool: begin automatically generated code
    AudioPlayWav             playWav1;
    AudioPlayWav             playWav2;
    AudioPlayWav             playWav3;
    AudioPlayWav             playWav4;
    AudioPlayWav             playWav5;
    AudioPlayWav             playWav6;
    AudioPlayWav             playWav7;
    AudioPlayWav             playWav8;
    AudioMixer4              mixer1;
    AudioMixer4              mixer2;
    AudioOutputPT8211        audioOutput;
    //AudioOutputI2S           audioOutput;
    //AudioOutputMQS           audioOutput;
    AudioConnection          patchCord1(playWav1, 0, mixer1, 0);
    AudioConnection          patchCord2(playWav2, 0, mixer1, 1);
    AudioConnection          patchCord3(playWav3, 0, mixer1, 2);
    AudioConnection          patchCord4(playWav4, 0, mixer1, 3);
    AudioConnection          patchCord5(playWav5, 0, mixer2, 0);
    AudioConnection          patchCord6(playWav6, 0, mixer2, 1);
    AudioConnection          patchCord7(playWav7, 0, mixer2, 2);
    AudioConnection          patchCord8(playWav8, 0, mixer2, 3);
    AudioConnection          patchCord9(mixer1, 0, audioOutput, 0);
    AudioConnection          patchCord10(mixer2, 0, audioOutput, 1);
    
    const int fileCnt = 6;
    const char *filename[fileCnt] = {
      "102790__mhc__acoustic-open-hihat2.wav",
      "171104__dwsd__kick-gettinglaid.wav",
      "201159__kiddpark__cash-register.wav",
      "82583__kevoy__snare-drum-4.wav",
      "86334__zgump__tom-0104.wav",
      "86773__juskiddink__gong.wav"
    };
    
    AudioPlayWav *player[fileCnt] = {
      &playWav1, &playWav2, &playWav3, &playWav4,
      &playWav5, &playWav6
    };
    
    File file[fileCnt];
    
    void setup() {
      Serial.begin(9600);
      if (CrashReport) {
        Serial.println(CrashReport);
        CrashReport.clear();
      }
    
      AudioMemory(50);
      Serial.println("LittleFS Test");
      if (!myfs.begin()) {
        Serial.println("Error starting qspidisk");
        while (1) ;
      }
      Serial.printf("TotalSize (Bytes): %d\n", myfs.totalSize());
    
      printDirectory();
    
      float gain = 1.0f / 4;
      for (int i = 0; i < 4; i++) {
        mixer1.gain(i, gain);
        mixer2.gain(i, gain);
      }
    
      //Open all files in paused mode, autorewind
      for (int i = 0; i < fileCnt; i++)
      {
        file[i] = myfs.open(filename[i]);
        if (!file[i])
        {
          Serial.printf("Could not open \"%s\"\n", filename[i]);
          while (1);
        }
        if (!player[i]->play(file[i], true, true))
        {
          Serial.printf("Could not start \"%s\"\n", filename[i]);
          while (1);
        }
      }
    
      AudioProcessorUsageMaxReset();
      AudioMemoryUsageMaxReset();
    }
    
    
    void loop() {
      static int c = 0;
      static unsigned long t = millis();
      unsigned long m;
      if ( (m = millis()) - t > 200 )
      {    
        t = m;
        if (++c >= fileCnt) c = 0;
        if (! player[c]->isPlaying())
        {
          player[c]->pause(false);
        }
    
        Serial.printf("Proc = %0.2f (%0.2f),  Mem = %d (%d)\n",
                      AudioProcessorUsage(), AudioProcessorUsageMax(),
                      AudioMemoryUsage(), AudioMemoryUsageMax());
        
        AudioProcessorUsageMaxReset();
        AudioMemoryUsageMaxReset();
    
      }
    
    }
    
    void printDirectory() {
      Serial.println("printDirectory\n--------------");
      printDirectory(myfs.open("/"), 0);
      Serial.println();
    }
    
    
    void printDirectory(File dir, int numTabs) {
      //dir.whoami();
      uint64_t fSize = 0;
      uint32_t dCnt = 0, fCnt = 0;
      if ( 0 == dir ) {
        Serial.printf( "\t>>>\t>>>>> No Dir\n" );
        return;
      }
      while (true) {
        File entry =  dir.openNextFile();
        if (! entry) {
          // no more files
          //Serial.printf("\n %u dirs with %u files of Size %u Bytes\n", dCnt, fCnt, fSize);
          fTot += fCnt;
          totSize1 += fSize;
          break;
        }
        for (uint8_t i = 0; i < numTabs; i++) {
          Serial.print('\t');
        }
    
        if (entry.isDirectory()) {
          Serial.print("DIR\t");
          dCnt++;
        } else {
          Serial.print("FILE\t");
          fCnt++;
          fSize += entry.size();
        }
        Serial.print(entry.name());
        if (entry.isDirectory()) {
          Serial.println(" / ");
          printDirectory(entry, numTabs + 1);
        } else {
          // files have sizes, directories do not
          Serial.print("\t\t");
          Serial.println(entry.size(), DEC);
        }
        entry.close();
        //Serial.flush();
      }
    }
    For LittleFS, this means: Open() your files before any file gets played, and everything is ok.

    Cpu usage is pretty ok for these 6 files from LittleFS/NAND (But I had expected way less - seek() seems to be pretty slow)
    Code:
    Proc = 0.36 (3.88),  Mem = 2 (5)
    Proc = 3.82 (3.88),  Mem = 2 (5)
    Proc = 3.88 (3.88),  Mem = 2 (5)
    Proc = 0.42 (3.95),  Mem = 2 (6)
    Proc = 0.36 (10.79),  Mem = 2 (6)
    Proc = 3.82 (3.90),  Mem = 2 (5)
    Proc = 0.36 (3.89),  Mem = 2 (5)
    Proc = 3.76 (3.88),  Mem = 2 (5)
    Proc = 0.23 (3.82),  Mem = 2 (4)
    Proc = 0.29 (3.82),  Mem = 2 (4)
    Proc = 0.30 (3.88),  Mem = 2 (5)
    Proc = 0.30 (3.90),  Mem = 2 (5)
    Last edited by Frank B; 10-22-2021 at 08:58 PM.

  10. #110
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    142
    Quote Originally Posted by PaulStoffregen View Post
    Yes, I will work with it later this month.

    3 other major audio contributions are also pending. Will probably spend several days to do all 4 together.

    Please be patient. I need you to understand many other important tasks need to be done, including minor PCB design changes for 3 Teensy models due to chip shortages on the diodes and voltage regulators (we just recently managed to buy the alternate parts).
    I'm sorry to report that WaveplayerEx and my Dynamic Audio Objects are (currently) not playing well together, at least in my "torture test". Run this with the Serial Plotter to visualise memory use and audio engine activity:
    Code:
    // Simple dynamic WAV file player example
    //
    // Create and destroy tracks played from SD card
    
    #define USE_BOESING_PLAYER
    
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    #if defined(USE_BOESING_PLAYER)
      #define AudioPlaySdWav AudioPlayWav // divert normal player to Franks' one
    #endif // defined(USE_BOESING_PLAYER)
    
    // GUItool: begin automatically generated code
    AudioMixer4              mixerL;         //xy=733,522
    AudioMixer4              mixerR;         //xy=733,606
    AudioOutputI2S           i2s;       //xy=907,572
    AudioConnection          patchCord1(mixerL, 0, i2s, 0);
    AudioConnection          patchCord2(mixerR, 0, i2s, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=908,666
    // GUItool: end automatically generated code
    
    
    #define SDCARD_CS_PIN    BUILTIN_SDCARD
    
    /*********************************************************************************/
    void setStereo(AudioMixer4& left,AudioMixer4& right,int channel,float level,float pan)
    {
       left.gain(channel,level*(pan-1)/-2.0f);
      right.gain(channel,level*(pan+1)/ 2.0f);
    }
    /*********************************************************************************/
    extern unsigned long _heap_end;
    uint32_t FreeMem(){ 
      char* p = (char*) malloc(10000); // size should be quite big, to avoid allocating fragment!
      free(p);
      return (char *)&_heap_end - p; 
    }
    /*********************************************************************************/
    
    AudioPlaySdWav* tracks[4];
    AudioConnection* cables[8];
    
    void setup() {
      Serial.begin(115200);
      while (!Serial)
        ;
      
      if (CrashReport) {
        Serial.println(CrashReport);
        CrashReport.clear();
      }
      Serial.println("Started!");
    
      AudioMemory(50);
    
      while (!(SD.begin(SDCARD_CS_PIN))) {
          Serial.println("Unable to access the SD card");
          delay(500);
      }
    
      setStereo(mixerL,mixerR,0,0.5,-0.3);
      setStereo(mixerL,mixerR,1,0.5,+0.3);
      setStereo(mixerL,mixerR,2,0.5,-0.7);
      setStereo(mixerL,mixerR,3,0.5,+0.7);
      
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.5);
    
      // Although addMemoryForRead() affects all instances via a
      // static value, it isn't itself static: create a throwaway
      // object to allow us to make the setting...
      AudioPlayWav* tmpWav = new AudioPlayWav();
      tmpWav->addMemoryForRead(4);
      delete tmpWav;
      
      Serial.println("mem unused playing stopped paused");
    }
    
    uint32_t next;
    
    char* waves[]={
      "sine110.wav",
      "sine220.wav",
      "sine330.wav",
      "sine440.wav",
      "sine550.wav",
      "sine660.wav"
    };
    
    
    // Start a wave file playing on a specific track
    void playTrack(int trackNum,int waveNum)
    { 
      tracks[trackNum]->play(waves[waveNum],false);
    }
    
    
    void loop() {  
    
      if (millis() > next) // time to pick a new random action
      {
        uint32_t intvl = random(40,265); // next action is after interval ... 
        if (intvl > 250) // ... long interval: lengthen further
        {
          intvl = (intvl - 250)*250; 
        }
        next = millis() + intvl;
    
        // pick random action, track and wave file
        int trackNum = random(4);
        int waveNum=random(6);
        int action=random(9);
    
        // If we're going to interact with this track, it'd better exist...
        if (NULL == tracks[trackNum])
        {
          tracks[trackNum] = new AudioPlaySdWav();
          cables[trackNum*2+0] = new AudioConnection();
          cables[trackNum*2+0]->connect(*tracks[trackNum],0,mixerL,trackNum);
          cables[trackNum*2+1] = new AudioConnection();
          cables[trackNum*2+1]->connect(*tracks[trackNum],0,mixerR,trackNum);
        }
        
        switch (action)
        {
          case 0:
            tracks[trackNum]->stop();
            break;
            
          case 1:
          case 3:
          case 4:
            playTrack(trackNum,waveNum);
            break;
            
          case 2:
          case 5:
    #define ALLOW_DELETE 1      
            if (ALLOW_DELETE)
            {
              delete tracks[trackNum];
              delete cables[trackNum*2+0];
              delete cables[trackNum*2+1];
              tracks[trackNum] = NULL;
            }
            else
              tracks[trackNum]->stop();      
            break;
            
          case 6:
          case 7:
          case 8:
            tracks[trackNum]->pause(!tracks[trackNum]->isPaused());
            break;
        }
      }
    
      // create numbers to plot
      int nx=0,nr=0,ns=0,np=0;
      for (int i=0;i<4;i++)
      {
        char c = 'x';
        if (NULL != tracks[i]) 
        {
          c = '?';
          c = tracks[i]->isPlaying()?'r':c;
          c = tracks[i]->isStopped()?'s':c;
          c = tracks[i]->isPaused()?'p':c;
        }
        
        switch (c)
        {
          case 'x': nx++; break;
          case 'r': nr++; break;
          case 's': ns++; break;
          case 'p': np++; break;
        }
      }
    
      // plot memory use, number of wave player instances, and counts of
      // the instance states (playing, stopped, paused)
      Serial.printf("%d %d %d %d %d\n",(FreeMem() - 00000)/100,nx*1000,nr*1000,ns*1000,np*1000);
      delay(50);
    }
    This test simply starts, pauses, stops and deletes up to four random instances of the wave player, at random intervals. As it stands it works fairly poorly for a while, then stops emitting the plotter output. Changing to #define ALLOW_DELETE 0 emulates the static audio engine, and everything behaves OK, even given the fact my SD card is not optimal.

    "works fairly poorly": obviously random stops and pauses are bound to result in some clicks, but there are also clicks while no actions are occurring. I'd guess this is because the algorithm used to try to keep the filesystem reads out of alignment with one another doesn't work well with dynamically-varying numbers of instances.

    "stops emitting plotter output": this isn't a simple crash, as I don't believe we get a CrashReport. A brief look at the code suggests the apparent number of active AudioPlayWav instances (uint8_t AudioBaseWav::_instances) grows without limit, as it's incremented on object construction, but not decremented when the object is deleted.

    Cheers

    Jonathan

  11. #111
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Quote Originally Posted by h4yn0nnym0u5e View Post
    I'm sorry to report that WaveplayerEx and my Dynamic Audio Objects are (currently) not playing well together
    Yes, decreasing will not help as it also breaks the (time-)order if you add new ones after.
    Simple solution: Don't use it dynamic or don't use the waveplayer. At the moment, these have contrary requirements - interleaving needs a absolute "position" so that the player can calculate the amount of data that has to be read at a given time.
    It will not work anymore if the position changes - at least not if it is playing.
    Edit:
    I don't see a real world use-case for dynamic players, too (where it would not work without "dynamic")

    I'll add a coment that they are not compatible to the sourcecode.

    IF you have a solution, I'd be happy to see a PR. It should work with already playing files, of course.
    Last edited by Frank B; 10-23-2021 at 05:10 PM.

  12. #112
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    142
    Quote Originally Posted by Frank B View Post
    Yes, decreasing will not help as it also radomizes the order if you add new ones after.
    Simple solution: Don't use it dynamic or don't use the waveplayer. These have contrary requirements - interleaving needs a absolute "position" so the the player can calucalte the amount of data that has to be read.

    I'll add a coment that they are not compatible to the sourcecode.

    IF you have a solution, I'd be happy to see a PR. It should work with already playing files, of course.
    Just posting here as they're two of the four "major contributions" that @PaulStoffregen says he's got pending, and an early warning might save later grief! If the incompatibility results in one or the other being deferred pending a fix, so be it.

    Another solution may be to define the required AudioPlayWav instances statically, and other objects dynamically - I've not tested this.

    Cheers

    Jonathan

  13. #113
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403

  14. #114
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    142
    Quote Originally Posted by Frank B View Post
    Edit:
    I don't see a real world use-case for dynamic players, too (where it would not work without "dynamic")
    On the roadmap page?

    Dynamic Updates & Web Designer Control
    Today, the audio objects really need to be statically allocated. Some distant future version will feature working object destructors, and constructors capable of adding objects and connections into a live system.
    Eventually, I want to create an example that listens for commands to create & delete objects, and control many of their functions, and of course corresponding code in the GUI design tool. As you drag objects onto the canvas, and connect them, messages would be sent to your Teensy running that example, to actually implement as you draw.

  15. #115
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Quote Originally Posted by h4yn0nnym0u5e View Post
    On the roadmap page?
    Dynamic Updates & Web Designer Control
    Today, the audio objects really need to be statically allocated. Some distant future version will feature working object destructors, and constructors capable of adding objects and connections into a live system.
    Eventually, I want to create an example that listens for commands to create & delete objects, and control many of their functions, and of course corresponding code in the GUI design tool. As you drag objects onto the canvas, and connect them, messages would be sent to your Teensy running that example, to actually implement as you draw.
    I guess, in this case the roadmap has a problem. Pauls wanted interleaved reads are not compatible to that.
    Think twice:

    Worst case you have to restart (->read vom SD) *all* players if you add one. Do that in 3ms. He wants 14 ...
    Yes, with an RTOS possible, perhaps. But an RTOS is not wanted, too.

    However, I don't care. You can stay with the old player or can write a new one.
    Not sure about the intention of your post here.

  16. #116
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,403
    Quote Originally Posted by PaulStoffregen View Post
    Yes, I will work with it later this month.
    Ok, that was last month, and the new month is again 3 weeks old.
    Any news?
    If not, (or again no answer - a simple "yes" or "no I will no use it " would have been enough...) I'd prefer to change the licence back.

    Thanks.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •