sharing SD-MMC_FATEngine with top-level spin and V2-WAV_DACEngine to Eliminate Redundant Objects

pidzeropidzero Posts: 23
edited 2015-12-11 - 15:44:10 in Propeller 1
[edit]added Tags, clarified Discussion Title[/edit]
Greetings,

I've been using V2-WAV_DACEngine by Kwabena W. Agyeman, available on the Propeller Object Exchange. When I selected it, the object seemed to be the most appropriate one for me. Aside from small glitches every-so-often during playback of a large WAV file, it's perfect.

The V2-WAV_DACEngine depends on SD-MMC_FATEngine... Another great object, which I would love to use in top-level code, without storing this resource twice on-chip.

As a result of this need, I tried integrating V2-WAV_DACEngine into top-level code.
As a result of repeated failure, I decided to isolate the problem by creating two, new, top-level files.
intended-use.spin
|
+-- Simple_Serial.spin
|
+-- V2-WAV_DACEngine.spin
    |
    +-- SD-MMC_FATEngine.spin
desired-use.spin
|
+-- Simple_Serial.spin
|
+-- SD-MMC_FATEngine.spin
desired-use.spin is the same as intended-use.spin with these exceptions:
* intended-use.spin sucessfully plays the audio file. desired-use.spin does not. Both files execute and indicate success to the LCD.
* desired-use.spin comments OBJ wav:"V2-WAV_DACEngine", changes wav. to {wav.}, and un-comments a copy of V2-WAV_DACEngine.spin, simply appended to the end of the file.

Normally, copying parts of methods, or entire methods, from various objects into top-level code would work fine, assuming the copied methods have access to the methods that they might call, and so on.

I'm not sure what I'm missing.

Thank you, in advance, for pointing me in the right direction.

Also, thank you, forum, for providing great coding examples. I have picked up numerous sweet techniques since my first visit to Parallax Discussion Forum.

pidzero

Comments

  • I know, it hasn't even been 24 hours since the OP, but I was lurking around the forum, hoping with every new page load for an indication of a reply.

    I noticed the OP has a fair amount of views, but no replies yet. (I know, "it hasn't even been a day yet, relax!")

    I thought some information about what I'm trying to develop, bigger picture, would be helpful, and perhaps solicit valuable responses. (bump?)

    So, I've been using the Prop1 for various not-highly-complex programs. What kind? LED light control, usually. One day, I decided an ambitious task of creating my own operating system.

    Expressed another way:
    1. Blink LED... check.
    2. Develop mult-user, networking, operating system, in an interpreted language (Spin), that will interpret BASIC... (this really does sound ambitious, right? not to mention it's been done on various development platforms countless times before.)

    Obviously, I had some level of confidence to even commit to it. Reasonably, it is possible to accomplish. Honorably, I cannot simply rebrand someone else's OS. Admittedly, I rely on other people's work to accomplish some tasks (trying to keep credits intact).

  • I have a PropOS based on Kyes FAT SD driver. Not sure if it's any help to you.
  • I'd be happy to look at it!
  • Wow, Cluso99! This looks like fun. And, if I read the PropOS discussion thread correctly, your design does several things of which I have zero development on, so far.

    A little more info about my build: As previously stated, its an ambitious project (for me, anyway). It is my goal to have 4 working units with stable (even if immature) OS--by Christmas. 1 unit is for me, and 3 will be for close, less-tech-savy-than-me friends.

    While your OS interface (aka command line) is vastly appealing to me, most of my intended users would be intimidated by it.
    Obvious answer: make some menus, boot straight into a menu.
    pidzero wrote: »
    Honorably, I cannot simply rebrand someone else's OS.
    Oh, the dilemma. When does code you got somewhere else become your own? Is it when you memorized it, and can produce it on-the-fly? Or is it when you press Ctrl-V and change a few things?

    I digress. Regarding my testing of your OS, I don't have it successfully running yet, but I do have it boot-looping with debug(serial) output. I'm sure I'll get it running when I come back from a break from the computer. I did not copy any files to microSD yet; I'm sure that's it.

    Henceforth, I will post my comments/questions/praises/etc about your OS on the appropriate discussion thread.

    Also, I'll update this thread with any useful information I encounter about V2-WAV_DACEngine sharing SD-MMC_FATEngine with top-level spin.
  • Yes, you need to copy all the files to the microSD card. There is a help command and if you enter a command with incorrect syntax you get a message of its correct usage. Recently I added a hub dump command but haven't posted the code yet. Been too busy testing a new pcb that I will be releasing just as soon as I update my website with details.
  • I am currently taking notes about its installation onto the Activity Board. I'm taking care to make them as absolute-beginner-friendly as possible. I'll post it to the PropOS thread as soon as it's objective is satisfied!
  • JonnyMacJonnyMac Posts: 7,112
    edited 2015-12-13 - 17:58:16
    I have my own WAV player that uses FSRW for file handling (it's faster, and I don't need anything beyond the root). As the parent object may want to deal with files when audio is not playing, I put hooks to low-level methods in FSRW -- like this (these are in my WAV object).
    pub xmount(do, clk, di, cs)
    
      return \sd.mount_explicit(do, clk, di, cs)
    
    
    pub open_file(p_str, cmd)
    
    '' If the file is opened successfully, 0 will be returned. 
    
      return \sd.popen(p_str, cmd)
    
    
    pub close_file
    
      return \sd.pclose
    
    
    pub get_filesize
    
      return sd.get_filesize
    
    
    pub open_dir
    
    '' Sets up buffer for nextfile()
    
      return \sd.opendir
    
    
    pub next_file(p_str)
    
    '' Read next filename in root
    '' -- returns 0 if file, -1 if done
    
      return \sd.nextfile(p_str)
    
    
    pub rd_buf(ubuf, count)
    
    '' Returns the number of bytes successfully read, or a negative number
    
      return \sd.pread(ubuf, count)
    
    
    pub rd_line(p_str, n) | c, len
    
    '' Reads line from open text file
    '' -- terminated by LF or EOF
    '' -- p_str is address of string buffer
    '' -- n is maximum number of characters to read from line
    
      len := 0                                                       ' index into string
    
      repeat n
        c := \sd.pgetc                                               ' get a character
        if (c < 0)                                                   ' end of file
          if (len == 0)                                              ' if nothing
            return -1                                                ' return empty
          else
            quit
    
        if (c <> $0A)                                                ' if not LF char
          byte[p_str++] := c                                         '  add to buffer
          len++
        else                                                         ' if LF
          quit                                                       '  we're done
    
      byte[p_str] := 0                                               ' terminate end of line
    
      return len
    
    
    pub rd_byte
    
    '' Reads char from file. Returns negative number if error or end of file
    
      return \sd.pgetc
    
    
    pub wr_buf(ubuf, count)
    
    '' Returns the number of bytes successfully written
    '' -- or a negative number if there is an error.
    
      return \sd.pwrite(ubuf, count)
    
    
    pub wr_str(p_str)
    
      return \sd.pwrite(p_str, strsize(p_str))
    
    
    pub wr_byte(b)
    
      return \sd.pputc(b)   
    
    
  • Ah. I understand your code example.

    This may not be ideal for me. I'll illustrate. The pseudocode example below employs the technique you suggested:
    {{top.spin}}
    OBJ
     lcd: "simple_serial"
    wav_play: "AWavePlayingObject.spin"
    
    DAT
    textFile byte "textfile.txt", 0
    wavFile byte "wavfile.wav",0
    errorString byte "An error has occurred!",0
    doneString byte "All done with SD!", 0
    
    PUB main
    ifnot \wav_play.initialize({initialization parameters})
      lcd.str(@errorString)
      halt
    
    ifnot wav_play.fileExists(@textFile)
      wav_play.newFile(@textFile)
    
    wav_play.openFile(@textFile, "A"{append-mode})
    wav_play.writeByte($41)
    wav_play.writeByte($42)
    wav_play.closeFile
    
    if wav_play.fileExists(@wavFile)
      if \wav_play.play(@wavFile)
        lcd.str(@errorString)
        halt
    
    repeat while wav_play.wavIsPlaying
    
    wav_play.openFile(@textFile, "A")
    wav_play.writeByte($43)
    wav_play.writeByte($44)
    wav_play.closeFile
    
    wav_play.end ' stop wav driver and SD access
    lcd.str(@doneString)
    halt
    
    PRI halt
    waitcnt(cnt+clkfreq) ' wait 1 second
    cogstop(0) ' stop the main cog
    

    Here, all SD operations--even ones that have nothing to do with playing WAVs--are called with wav_play.methodName.

    While we have avoided object redundancy in this example by hooking, what happens when I add more objects depending on the same SD object:

    OBJ
    wav_rec : "AWaveRecordingObject.spin"
    logger : "ADataLoggingObject.spin"

    For simplicity, we will assume that simultaneous file-system access is not a requirement, but conserving EEPROM space is.
  • If the file system object you're using is written as a singleton, then there is no problem. Even for objects that aren't singletons, the compiler only loads one copy into memory. In the non-singleton case each instance will get its own set of variables, but the code and data will be used across call copies, reducing RAM space requirements.
Sign In or Register to comment.